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  }