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 }