github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/snowman/getter/getter.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package getter 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/prometheus/client_golang/prometheus" 11 "go.uber.org/zap" 12 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow/choices" 15 "github.com/MetalBlockchain/metalgo/snow/engine/common" 16 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 17 "github.com/MetalBlockchain/metalgo/utils/constants" 18 "github.com/MetalBlockchain/metalgo/utils/logging" 19 "github.com/MetalBlockchain/metalgo/utils/metric" 20 "github.com/MetalBlockchain/metalgo/utils/set" 21 ) 22 23 // Get requests are always served, regardless node state (bootstrapping or normal operations). 24 var _ common.AllGetsServer = (*getter)(nil) 25 26 func New( 27 vm block.ChainVM, 28 sender common.Sender, 29 log logging.Logger, 30 maxTimeGetAncestors time.Duration, 31 maxContainersGetAncestors int, 32 reg prometheus.Registerer, 33 ) (common.AllGetsServer, error) { 34 ssVM, _ := vm.(block.StateSyncableVM) 35 gh := &getter{ 36 vm: vm, 37 ssVM: ssVM, 38 sender: sender, 39 log: log, 40 maxTimeGetAncestors: maxTimeGetAncestors, 41 maxContainersGetAncestors: maxContainersGetAncestors, 42 } 43 44 var err error 45 gh.getAncestorsBlks, err = metric.NewAverager( 46 "bs_get_ancestors_blks", 47 "blocks fetched in a call to GetAncestors", 48 reg, 49 ) 50 return gh, err 51 } 52 53 type getter struct { 54 vm block.ChainVM 55 ssVM block.StateSyncableVM // can be nil 56 57 sender common.Sender 58 log logging.Logger 59 // Max time to spend fetching a container and its ancestors when responding 60 // to a GetAncestors 61 maxTimeGetAncestors time.Duration 62 // Max number of containers in an ancestors message sent by this node. 63 maxContainersGetAncestors int 64 65 getAncestorsBlks metric.Averager 66 } 67 68 func (gh *getter) GetStateSummaryFrontier(ctx context.Context, nodeID ids.NodeID, requestID uint32) error { 69 // Note: we do not check if gh.ssVM.StateSyncEnabled since we want all 70 // nodes, including those disabling state sync to serve state summaries if 71 // these are available 72 if gh.ssVM == nil { 73 gh.log.Debug("dropping GetStateSummaryFrontier message", 74 zap.String("reason", "state sync not supported"), 75 zap.Stringer("nodeID", nodeID), 76 zap.Uint32("requestID", requestID), 77 ) 78 return nil 79 } 80 81 summary, err := gh.ssVM.GetLastStateSummary(ctx) 82 if err != nil { 83 gh.log.Debug("dropping GetStateSummaryFrontier message", 84 zap.String("reason", "couldn't get state summary frontier"), 85 zap.Stringer("nodeID", nodeID), 86 zap.Uint32("requestID", requestID), 87 zap.Error(err), 88 ) 89 return nil 90 } 91 92 gh.sender.SendStateSummaryFrontier(ctx, nodeID, requestID, summary.Bytes()) 93 return nil 94 } 95 96 func (gh *getter) GetAcceptedStateSummary(ctx context.Context, nodeID ids.NodeID, requestID uint32, heights set.Set[uint64]) error { 97 // If there are no requested heights, then we can return the result 98 // immediately, regardless of if the underlying VM implements state sync. 99 if heights.Len() == 0 { 100 gh.sender.SendAcceptedStateSummary(ctx, nodeID, requestID, nil) 101 return nil 102 } 103 104 // Note: we do not check if gh.ssVM.StateSyncEnabled since we want all 105 // nodes, including those disabling state sync to serve state summaries if 106 // these are available 107 if gh.ssVM == nil { 108 gh.log.Debug("dropping GetAcceptedStateSummary message", 109 zap.String("reason", "state sync not supported"), 110 zap.Stringer("nodeID", nodeID), 111 zap.Uint32("requestID", requestID), 112 ) 113 return nil 114 } 115 116 summaryIDs := make([]ids.ID, 0, heights.Len()) 117 for height := range heights { 118 summary, err := gh.ssVM.GetStateSummary(ctx, height) 119 if err == block.ErrStateSyncableVMNotImplemented { 120 gh.log.Debug("dropping GetAcceptedStateSummary message", 121 zap.String("reason", "state sync not supported"), 122 zap.Stringer("nodeID", nodeID), 123 zap.Uint32("requestID", requestID), 124 ) 125 return nil 126 } 127 if err != nil { 128 gh.log.Debug("couldn't get state summary", 129 zap.Uint64("height", height), 130 zap.Error(err), 131 ) 132 continue 133 } 134 summaryIDs = append(summaryIDs, summary.ID()) 135 } 136 137 gh.sender.SendAcceptedStateSummary(ctx, nodeID, requestID, summaryIDs) 138 return nil 139 } 140 141 func (gh *getter) GetAcceptedFrontier(ctx context.Context, nodeID ids.NodeID, requestID uint32) error { 142 lastAccepted, err := gh.vm.LastAccepted(ctx) 143 if err != nil { 144 return err 145 } 146 gh.sender.SendAcceptedFrontier(ctx, nodeID, requestID, lastAccepted) 147 return nil 148 } 149 150 func (gh *getter) GetAccepted(ctx context.Context, nodeID ids.NodeID, requestID uint32, containerIDs set.Set[ids.ID]) error { 151 acceptedIDs := make([]ids.ID, 0, containerIDs.Len()) 152 for blkID := range containerIDs { 153 blk, err := gh.vm.GetBlock(ctx, blkID) 154 if err == nil && blk.Status() == choices.Accepted { 155 acceptedIDs = append(acceptedIDs, blkID) 156 } 157 } 158 gh.sender.SendAccepted(ctx, nodeID, requestID, acceptedIDs) 159 return nil 160 } 161 162 func (gh *getter) GetAncestors(ctx context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) error { 163 ancestorsBytes, err := block.GetAncestors( 164 ctx, 165 gh.log, 166 gh.vm, 167 blkID, 168 gh.maxContainersGetAncestors, 169 constants.MaxContainersLen, 170 gh.maxTimeGetAncestors, 171 ) 172 if err != nil { 173 gh.log.Verbo("dropping GetAncestors message", 174 zap.String("reason", "couldn't get ancestors"), 175 zap.Stringer("nodeID", nodeID), 176 zap.Uint32("requestID", requestID), 177 zap.Stringer("blkID", blkID), 178 zap.Error(err), 179 ) 180 return nil 181 } 182 183 gh.getAncestorsBlks.Observe(float64(len(ancestorsBytes))) 184 gh.sender.SendAncestors(ctx, nodeID, requestID, ancestorsBytes) 185 return nil 186 } 187 188 func (gh *getter) Get(ctx context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) error { 189 blk, err := gh.vm.GetBlock(ctx, blkID) 190 if err != nil { 191 // If we failed to get the block, that means either an unexpected error 192 // has occurred, [vdr] is not following the protocol, or the 193 // block has been pruned. 194 gh.log.Debug("failed Get request", 195 zap.Stringer("nodeID", nodeID), 196 zap.Uint32("requestID", requestID), 197 zap.Stringer("blkID", blkID), 198 zap.Error(err), 199 ) 200 return nil 201 } 202 203 // Respond to the validator with the fetched block and the same requestID. 204 gh.sender.SendPut(ctx, nodeID, requestID, blk.Bytes()) 205 return nil 206 }