github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/height_indexed_vm.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 "fmt" 9 10 "go.uber.org/zap" 11 12 "github.com/ava-labs/avalanchego/database" 13 "github.com/ava-labs/avalanchego/ids" 14 ) 15 16 const pruneCommitPeriod = 1024 17 18 // vm.ctx.Lock should be held 19 func (vm *VM) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error) { 20 switch forkHeight, err := vm.State.GetForkHeight(); err { 21 case nil: 22 if height < forkHeight { 23 return vm.ChainVM.GetBlockIDAtHeight(ctx, height) 24 } 25 return vm.State.GetBlockIDAtHeight(height) 26 27 case database.ErrNotFound: 28 // fork not reached yet. Block must be pre-fork 29 return vm.ChainVM.GetBlockIDAtHeight(ctx, height) 30 31 default: 32 return ids.Empty, err 33 } 34 } 35 36 func (vm *VM) updateHeightIndex(height uint64, blkID ids.ID) error { 37 forkHeight, err := vm.State.GetForkHeight() 38 switch err { 39 case nil: 40 // The fork was already reached. Just update the index. 41 42 case database.ErrNotFound: 43 // This is the first post fork block, store the fork height. 44 if err := vm.State.SetForkHeight(height); err != nil { 45 return fmt.Errorf("failed storing fork height: %w", err) 46 } 47 forkHeight = height 48 49 default: 50 return fmt.Errorf("failed to load fork height: %w", err) 51 } 52 53 if err := vm.State.SetBlockIDAtHeight(height, blkID); err != nil { 54 return err 55 } 56 57 vm.ctx.Log.Debug("indexed block", 58 zap.Stringer("blkID", blkID), 59 zap.Uint64("height", height), 60 ) 61 62 if vm.NumHistoricalBlocks == 0 { 63 return nil 64 } 65 66 blocksSinceFork := height - forkHeight 67 // Note: The last accepted block is not considered a historical block. Which 68 // is why <= is used rather than <. This prevents the user from only storing 69 // the last accepted block, which can never be safe due to the non-atomic 70 // commits between the proposervm database and the innerVM's database. 71 if blocksSinceFork <= vm.NumHistoricalBlocks { 72 return nil 73 } 74 75 // Note: heightToDelete is >= forkHeight, so it is guaranteed not to 76 // underflow. 77 heightToDelete := height - vm.NumHistoricalBlocks - 1 78 blockToDelete, err := vm.State.GetBlockIDAtHeight(heightToDelete) 79 if err == database.ErrNotFound { 80 // Block may have already been deleted. This can happen due to a 81 // proposervm rollback, the node having recently state-synced, or the 82 // user reconfiguring the node to store more historical blocks than a 83 // prior run. 84 return nil 85 } 86 if err != nil { 87 return err 88 } 89 90 if err := vm.State.DeleteBlockIDAtHeight(heightToDelete); err != nil { 91 return err 92 } 93 if err := vm.State.DeleteBlock(blockToDelete); err != nil { 94 return err 95 } 96 97 vm.ctx.Log.Debug("deleted block", 98 zap.Stringer("blkID", blockToDelete), 99 zap.Uint64("height", heightToDelete), 100 ) 101 return nil 102 } 103 104 // TODO: Support async deletion of old blocks. 105 func (vm *VM) pruneOldBlocks() error { 106 if vm.NumHistoricalBlocks == 0 { 107 return nil 108 } 109 110 height, err := vm.State.GetMinimumHeight() 111 if err == database.ErrNotFound { 112 // Chain hasn't forked yet 113 return nil 114 } 115 116 // TODO: Refactor to use DB iterators. 117 // 118 // Note: vm.lastAcceptedHeight is guaranteed to be >= height, so the 119 // subtraction can never underflow. 120 for vm.lastAcceptedHeight-height > vm.NumHistoricalBlocks { 121 blockToDelete, err := vm.State.GetBlockIDAtHeight(height) 122 if err != nil { 123 return err 124 } 125 126 if err := vm.State.DeleteBlockIDAtHeight(height); err != nil { 127 return err 128 } 129 if err := vm.State.DeleteBlock(blockToDelete); err != nil { 130 return err 131 } 132 133 vm.ctx.Log.Debug("deleted block", 134 zap.Stringer("blkID", blockToDelete), 135 zap.Uint64("height", height), 136 ) 137 138 // Note: height is < vm.lastAcceptedHeight, so it is guaranteed not to 139 // overflow. 140 height++ 141 if height%pruneCommitPeriod != 0 { 142 continue 143 } 144 145 if err := vm.db.Commit(); err != nil { 146 return err 147 } 148 } 149 return vm.db.Commit() 150 }