github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/executor/acceptor.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 "go.uber.org/zap" 11 12 "github.com/MetalBlockchain/metalgo/utils" 13 "github.com/MetalBlockchain/metalgo/vms/platformvm/block" 14 "github.com/MetalBlockchain/metalgo/vms/platformvm/metrics" 15 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 16 "github.com/MetalBlockchain/metalgo/vms/platformvm/validators" 17 ) 18 19 var ( 20 _ block.Visitor = (*acceptor)(nil) 21 22 errMissingBlockState = errors.New("missing state of block") 23 ) 24 25 // acceptor handles the logic for accepting a block. 26 // All errors returned by this struct are fatal and should result in the chain 27 // being shutdown. 28 type acceptor struct { 29 *backend 30 metrics metrics.Metrics 31 validators validators.Manager 32 bootstrapped *utils.Atomic[bool] 33 } 34 35 func (a *acceptor) BanffAbortBlock(b *block.BanffAbortBlock) error { 36 return a.optionBlock(b, "banff abort") 37 } 38 39 func (a *acceptor) BanffCommitBlock(b *block.BanffCommitBlock) error { 40 return a.optionBlock(b, "banff commit") 41 } 42 43 func (a *acceptor) BanffProposalBlock(b *block.BanffProposalBlock) error { 44 a.proposalBlock(b, "banff proposal") 45 return nil 46 } 47 48 func (a *acceptor) BanffStandardBlock(b *block.BanffStandardBlock) error { 49 return a.standardBlock(b, "banff standard") 50 } 51 52 func (a *acceptor) ApricotAbortBlock(b *block.ApricotAbortBlock) error { 53 return a.optionBlock(b, "apricot abort") 54 } 55 56 func (a *acceptor) ApricotCommitBlock(b *block.ApricotCommitBlock) error { 57 return a.optionBlock(b, "apricot commit") 58 } 59 60 func (a *acceptor) ApricotProposalBlock(b *block.ApricotProposalBlock) error { 61 a.proposalBlock(b, "apricot proposal") 62 return nil 63 } 64 65 func (a *acceptor) ApricotStandardBlock(b *block.ApricotStandardBlock) error { 66 return a.standardBlock(b, "apricot standard") 67 } 68 69 func (a *acceptor) ApricotAtomicBlock(b *block.ApricotAtomicBlock) error { 70 blkID := b.ID() 71 defer a.free(blkID) 72 73 if err := a.commonAccept(b); err != nil { 74 return err 75 } 76 77 blkState, ok := a.blkIDToState[blkID] 78 if !ok { 79 return fmt.Errorf("%w %s", errMissingBlockState, blkID) 80 } 81 82 // Update the state to reflect the changes made in [onAcceptState]. 83 if err := blkState.onAcceptState.Apply(a.state); err != nil { 84 return err 85 } 86 87 defer a.state.Abort() 88 batch, err := a.state.CommitBatch() 89 if err != nil { 90 return fmt.Errorf( 91 "failed to commit VM's database for block %s: %w", 92 blkID, 93 err, 94 ) 95 } 96 97 // Note that this method writes [batch] to the database. 98 if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { 99 return fmt.Errorf( 100 "failed to atomically accept tx %s in block %s: %w", 101 b.Tx.ID(), 102 blkID, 103 err, 104 ) 105 } 106 107 a.ctx.Log.Trace( 108 "accepted block", 109 zap.String("blockType", "apricot atomic"), 110 zap.Stringer("blkID", blkID), 111 zap.Uint64("height", b.Height()), 112 zap.Stringer("parentID", b.Parent()), 113 zap.Stringer("utxoChecksum", a.state.Checksum()), 114 ) 115 116 return nil 117 } 118 119 func (a *acceptor) optionBlock(b block.Block, blockType string) error { 120 parentID := b.Parent() 121 parentState, ok := a.blkIDToState[parentID] 122 if !ok { 123 return fmt.Errorf("%w: %s", state.ErrMissingParentState, parentID) 124 } 125 126 blkID := b.ID() 127 defer func() { 128 // Note: we assume this block's sibling doesn't 129 // need the parent's state when it's rejected. 130 a.free(parentID) 131 a.free(blkID) 132 }() 133 134 // Note that the parent must be accepted first. 135 if err := a.commonAccept(parentState.statelessBlock); err != nil { 136 return err 137 } 138 139 if err := a.commonAccept(b); err != nil { 140 return err 141 } 142 143 if parentState.onDecisionState != nil { 144 if err := parentState.onDecisionState.Apply(a.state); err != nil { 145 return err 146 } 147 } 148 149 blkState, ok := a.blkIDToState[blkID] 150 if !ok { 151 return fmt.Errorf("%w %s", errMissingBlockState, blkID) 152 } 153 if err := blkState.onAcceptState.Apply(a.state); err != nil { 154 return err 155 } 156 157 defer a.state.Abort() 158 batch, err := a.state.CommitBatch() 159 if err != nil { 160 return fmt.Errorf( 161 "failed to commit VM's database for block %s: %w", 162 blkID, 163 err, 164 ) 165 } 166 167 // Note that this method writes [batch] to the database. 168 if err := a.ctx.SharedMemory.Apply(parentState.atomicRequests, batch); err != nil { 169 return fmt.Errorf("failed to apply vm's state to shared memory: %w", err) 170 } 171 172 if onAcceptFunc := parentState.onAcceptFunc; onAcceptFunc != nil { 173 onAcceptFunc() 174 } 175 176 a.ctx.Log.Trace( 177 "accepted block", 178 zap.String("blockType", blockType), 179 zap.Stringer("blkID", blkID), 180 zap.Uint64("height", b.Height()), 181 zap.Stringer("parentID", parentID), 182 zap.Stringer("utxoChecksum", a.state.Checksum()), 183 ) 184 185 return nil 186 } 187 188 func (a *acceptor) proposalBlock(b block.Block, blockType string) { 189 // Note that: 190 // 191 // * We don't free the proposal block in this method. 192 // It is freed when its child is accepted. 193 // We need to keep this block's state in memory for its child to use. 194 // 195 // * We only update the metrics to reflect this block's 196 // acceptance when its child is accepted. 197 // 198 // * We don't write this block to state here. 199 // That is done when this block's child (a CommitBlock or AbortBlock) is accepted. 200 // We do this so that in the event that the node shuts down, the proposal block 201 // is not written to disk unless its child is. 202 // (The VM's Shutdown method commits the database.) 203 // The snowman.Engine requires that the last committed block is a decision block 204 205 blkID := b.ID() 206 a.backend.lastAccepted = blkID 207 208 a.ctx.Log.Trace( 209 "accepted block", 210 zap.String("blockType", blockType), 211 zap.Stringer("blkID", blkID), 212 zap.Uint64("height", b.Height()), 213 zap.Stringer("parentID", b.Parent()), 214 zap.Stringer("utxoChecksum", a.state.Checksum()), 215 ) 216 } 217 218 func (a *acceptor) standardBlock(b block.Block, blockType string) error { 219 blkID := b.ID() 220 defer a.free(blkID) 221 222 if err := a.commonAccept(b); err != nil { 223 return err 224 } 225 226 blkState, ok := a.blkIDToState[blkID] 227 if !ok { 228 return fmt.Errorf("%w %s", errMissingBlockState, blkID) 229 } 230 231 // Update the state to reflect the changes made in [onAcceptState]. 232 if err := blkState.onAcceptState.Apply(a.state); err != nil { 233 return err 234 } 235 236 defer a.state.Abort() 237 batch, err := a.state.CommitBatch() 238 if err != nil { 239 return fmt.Errorf( 240 "failed to commit VM's database for block %s: %w", 241 blkID, 242 err, 243 ) 244 } 245 246 // Note that this method writes [batch] to the database. 247 if err := a.ctx.SharedMemory.Apply(blkState.atomicRequests, batch); err != nil { 248 return fmt.Errorf("failed to apply vm's state to shared memory: %w", err) 249 } 250 251 if onAcceptFunc := blkState.onAcceptFunc; onAcceptFunc != nil { 252 onAcceptFunc() 253 } 254 255 a.ctx.Log.Trace( 256 "accepted block", 257 zap.String("blockType", blockType), 258 zap.Stringer("blkID", blkID), 259 zap.Uint64("height", b.Height()), 260 zap.Stringer("parentID", b.Parent()), 261 zap.Stringer("utxoChecksum", a.state.Checksum()), 262 ) 263 264 return nil 265 } 266 267 func (a *acceptor) commonAccept(b block.Block) error { 268 blkID := b.ID() 269 270 if err := a.metrics.MarkAccepted(b); err != nil { 271 return fmt.Errorf("failed to accept block %s: %w", blkID, err) 272 } 273 274 a.backend.lastAccepted = blkID 275 a.state.SetLastAccepted(blkID) 276 a.state.SetHeight(b.Height()) 277 a.state.AddStatelessBlock(b) 278 a.validators.OnAcceptedBlockID(blkID) 279 return nil 280 }