github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/backend/backend_network.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/koko1123/flow-go-1/access"
     8  
     9  	"github.com/koko1123/flow-go-1/engine/common/rpc/convert"
    10  	"github.com/koko1123/flow-go-1/model/flow"
    11  	"github.com/koko1123/flow-go-1/state/protocol"
    12  )
    13  
    14  var SnapshotHistoryLimitErr = fmt.Errorf("reached the snapshot history limit")
    15  
    16  type backendNetwork struct {
    17  	state                protocol.State
    18  	chainID              flow.ChainID
    19  	snapshotHistoryLimit int
    20  }
    21  
    22  /*
    23  NetworkAPI func
    24  
    25  The observer and access nodes need to be able to handle GetNetworkParameters
    26  and GetLatestProtocolStateSnapshot RPCs so this logic was split into
    27  the backendNetwork so that we can ignore the rest of the backend logic
    28  */
    29  func NewNetworkAPI(state protocol.State, chainID flow.ChainID, snapshotHistoryLimit int) *backendNetwork {
    30  	return &backendNetwork{
    31  		state:                state,
    32  		chainID:              chainID,
    33  		snapshotHistoryLimit: snapshotHistoryLimit,
    34  	}
    35  }
    36  
    37  func (b *backendNetwork) GetNetworkParameters(_ context.Context) access.NetworkParameters {
    38  	return access.NetworkParameters{
    39  		ChainID: b.chainID,
    40  	}
    41  }
    42  
    43  // GetLatestProtocolStateSnapshot returns the latest finalized snapshot
    44  func (b *backendNetwork) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) {
    45  	snapshot := b.state.Final()
    46  
    47  	validSnapshot, err := b.getValidSnapshot(snapshot, 0)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return convert.SnapshotToBytes(validSnapshot)
    53  }
    54  
    55  func (b *backendNetwork) isEpochOrPhaseDifferent(counter1, counter2 uint64, phase1, phase2 flow.EpochPhase) bool {
    56  	return counter1 != counter2 || phase1 != phase2
    57  }
    58  
    59  // getValidSnapshot will return a valid snapshot that has a sealing segment which
    60  // 1. does not contain any blocks that span an epoch transition
    61  // 2. does not contain any blocks that span an epoch phase transition
    62  // If a snapshot does contain an invalid sealing segment query the state
    63  // by height of each block in the segment and return a snapshot at the point
    64  // where the transition happens.
    65  func (b *backendNetwork) getValidSnapshot(snapshot protocol.Snapshot, blocksVisited int) (protocol.Snapshot, error) {
    66  	segment, err := snapshot.SealingSegment()
    67  	if err != nil {
    68  		return nil, fmt.Errorf("failed to get sealing segment: %w", err)
    69  	}
    70  
    71  	counterAtHighest, phaseAtHighest, err := b.getCounterAndPhase(segment.Highest().Header.Height)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to get counter and phase at highest block in the segment: %w", err)
    74  	}
    75  
    76  	counterAtLowest, phaseAtLowest, err := b.getCounterAndPhase(segment.Lowest().Header.Height)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("failed to get counter and phase at lowest block in the segment: %w", err)
    79  	}
    80  
    81  	// Check if the counters and phase are different this indicates that the sealing segment
    82  	// of the snapshot requested spans either an epoch transition or phase transition.
    83  	if b.isEpochOrPhaseDifferent(counterAtHighest, counterAtLowest, phaseAtHighest, phaseAtLowest) {
    84  		// Visit each node in strict order of decreasing height starting at head
    85  		// to find the block that straddles the transition boundary.
    86  		for i := len(segment.Blocks) - 1; i >= 0; i-- {
    87  			blocksVisited++
    88  
    89  			// NOTE: Check if we have reached our history limit, in edge cases
    90  			// where the sealing segment is abnormally long we want to short circuit
    91  			// the recursive calls and return an error. The API caller can retry.
    92  			if blocksVisited > b.snapshotHistoryLimit {
    93  				return nil, fmt.Errorf("%w: (%d)", SnapshotHistoryLimitErr, b.snapshotHistoryLimit)
    94  			}
    95  
    96  			counterAtBlock, phaseAtBlock, err := b.getCounterAndPhase(segment.Blocks[i].Header.Height)
    97  			if err != nil {
    98  				return nil, fmt.Errorf("failed to get epoch counter and phase for snapshot at block %s: %w", segment.Blocks[i].ID(), err)
    99  			}
   100  
   101  			// Check if this block straddles the transition boundary, if it does return the snapshot
   102  			// at that block height.
   103  			if b.isEpochOrPhaseDifferent(counterAtHighest, counterAtBlock, phaseAtHighest, phaseAtBlock) {
   104  				return b.getValidSnapshot(b.state.AtHeight(segment.Blocks[i].Header.Height), blocksVisited)
   105  			}
   106  		}
   107  	}
   108  
   109  	return snapshot, nil
   110  }
   111  
   112  // getCounterAndPhase will return the epoch counter and phase at the specified height in state
   113  func (b *backendNetwork) getCounterAndPhase(height uint64) (uint64, flow.EpochPhase, error) {
   114  	snapshot := b.state.AtHeight(height)
   115  
   116  	counter, err := snapshot.Epochs().Current().Counter()
   117  	if err != nil {
   118  		return 0, 0, fmt.Errorf("failed to get counter for block (height=%d): %w", height, err)
   119  	}
   120  
   121  	phase, err := snapshot.Phase()
   122  	if err != nil {
   123  		return 0, 0, fmt.Errorf("failed to get phase for block (height=%d): %w", height, err)
   124  	}
   125  
   126  	return counter, phase, nil
   127  }