github.com/onflow/flow-go@v0.33.17/state/protocol/inmem/convert.go (about) 1 package inmem 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/onflow/flow-go/model/encodable" 8 "github.com/onflow/flow-go/model/flow" 9 "github.com/onflow/flow-go/model/flow/filter" 10 "github.com/onflow/flow-go/module/signature" 11 "github.com/onflow/flow-go/state/protocol" 12 ) 13 14 // FromSnapshot generates a memory-backed snapshot from the input snapshot. 15 // Typically, this would be used to convert a database-backed snapshot to 16 // one that can easily be serialized to disk or to network. 17 // TODO error docs 18 func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { 19 var ( 20 snap EncodableSnapshot 21 err error 22 ) 23 24 // convert top-level fields 25 snap.Head, err = from.Head() 26 if err != nil { 27 return nil, fmt.Errorf("could not get head: %w", err) 28 } 29 snap.Identities, err = from.Identities(filter.Any) 30 if err != nil { 31 return nil, fmt.Errorf("could not get identities: %w", err) 32 } 33 snap.LatestResult, snap.LatestSeal, err = from.SealedResult() 34 if err != nil { 35 return nil, fmt.Errorf("could not get seal: %w", err) 36 } 37 38 snap.SealingSegment, err = from.SealingSegment() 39 if err != nil { 40 return nil, fmt.Errorf("could not get sealing segment: %w", err) 41 } 42 snap.QuorumCertificate, err = from.QuorumCertificate() 43 if err != nil { 44 return nil, fmt.Errorf("could not get qc: %w", err) 45 } 46 snap.Phase, err = from.Phase() 47 if err != nil { 48 return nil, fmt.Errorf("could not get phase: %w", err) 49 } 50 51 // convert epochs 52 previous, err := FromEpoch(from.Epochs().Previous()) 53 // it is possible for valid snapshots to have no previous epoch 54 if errors.Is(err, protocol.ErrNoPreviousEpoch) { 55 snap.Epochs.Previous = nil 56 } else if err != nil { 57 return nil, fmt.Errorf("could not get previous epoch: %w", err) 58 } else { 59 snap.Epochs.Previous = &previous.enc 60 } 61 62 current, err := FromEpoch(from.Epochs().Current()) 63 if err != nil { 64 return nil, fmt.Errorf("could not get current epoch: %w", err) 65 } 66 snap.Epochs.Current = current.enc 67 68 next, err := FromEpoch(from.Epochs().Next()) 69 // it is possible for valid snapshots to have no next epoch 70 if errors.Is(err, protocol.ErrNextEpochNotSetup) { 71 snap.Epochs.Next = nil 72 } else if err != nil { 73 return nil, fmt.Errorf("could not get next epoch: %w", err) 74 } else { 75 snap.Epochs.Next = &next.enc 76 } 77 78 // convert global state parameters 79 params, err := FromParams(from.Params()) 80 if err != nil { 81 return nil, fmt.Errorf("could not get params: %w", err) 82 } 83 snap.Params = params.enc 84 85 // convert version beacon 86 versionBeacon, err := from.VersionBeacon() 87 if err != nil { 88 return nil, fmt.Errorf("could not get version beacon: %w", err) 89 } 90 91 snap.SealedVersionBeacon = versionBeacon 92 93 return &Snapshot{snap}, nil 94 } 95 96 // FromParams converts any protocol.GlobalParams to a memory-backed Params. 97 // TODO error docs 98 func FromParams(from protocol.GlobalParams) (*Params, error) { 99 var ( 100 params EncodableParams 101 err error 102 ) 103 104 params.ChainID, err = from.ChainID() 105 if err != nil { 106 return nil, fmt.Errorf("could not get chain id: %w", err) 107 } 108 params.SporkID, err = from.SporkID() 109 if err != nil { 110 return nil, fmt.Errorf("could not get spork id: %w", err) 111 } 112 params.SporkRootBlockHeight, err = from.SporkRootBlockHeight() 113 if err != nil { 114 return nil, fmt.Errorf("could not get spork root block height: %w", err) 115 } 116 params.ProtocolVersion, err = from.ProtocolVersion() 117 if err != nil { 118 return nil, fmt.Errorf("could not get protocol version: %w", err) 119 } 120 params.EpochCommitSafetyThreshold, err = from.EpochCommitSafetyThreshold() 121 if err != nil { 122 return nil, fmt.Errorf("could not get protocol version: %w", err) 123 } 124 125 return &Params{params}, nil 126 } 127 128 // FromEpoch converts any protocol.Epoch to a memory-backed Epoch. 129 // Error returns: 130 // * protocol.ErrNoPreviousEpoch - if the epoch represents a previous epoch which does not exist. 131 // * protocol.ErrNextEpochNotSetup - if the epoch represents a next epoch which has not been set up. 132 // * state.ErrUnknownSnapshotReference - if the epoch is queried from an unresolvable snapshot. 133 func FromEpoch(from protocol.Epoch) (*Epoch, error) { 134 var ( 135 epoch EncodableEpoch 136 err error 137 ) 138 139 // convert top-level fields 140 epoch.Counter, err = from.Counter() 141 if err != nil { 142 return nil, fmt.Errorf("could not get counter: %w", err) 143 } 144 epoch.InitialIdentities, err = from.InitialIdentities() 145 if err != nil { 146 return nil, fmt.Errorf("could not get initial identities: %w", err) 147 } 148 epoch.FirstView, err = from.FirstView() 149 if err != nil { 150 return nil, fmt.Errorf("could not get first view: %w", err) 151 } 152 epoch.FinalView, err = from.FinalView() 153 if err != nil { 154 return nil, fmt.Errorf("could not get final view: %w", err) 155 } 156 epoch.RandomSource, err = from.RandomSource() 157 if err != nil { 158 return nil, fmt.Errorf("could not get random source: %w", err) 159 } 160 epoch.DKGPhase1FinalView, epoch.DKGPhase2FinalView, epoch.DKGPhase3FinalView, err = protocol.DKGPhaseViews(from) 161 if err != nil { 162 return nil, fmt.Errorf("could not get dkg final views") 163 } 164 clustering, err := from.Clustering() 165 if err != nil { 166 return nil, fmt.Errorf("could not get clustering: %w", err) 167 } 168 epoch.Clustering = clustering 169 170 // convert dkg 171 dkg, err := from.DKG() 172 // if this epoch hasn't been committed yet, return the epoch as-is 173 if errors.Is(err, protocol.ErrNextEpochNotCommitted) { 174 return &Epoch{epoch}, nil 175 } 176 if err != nil { 177 return nil, fmt.Errorf("could not get dkg: %w", err) 178 } 179 convertedDKG, err := FromDKG(dkg, epoch.InitialIdentities.Filter(filter.HasRole(flow.RoleConsensus))) 180 if err != nil { 181 return nil, err 182 } 183 epoch.DKG = &convertedDKG.enc 184 185 // convert clusters 186 for index := range clustering { 187 cluster, err := from.Cluster(uint(index)) 188 if err != nil { 189 return nil, fmt.Errorf("could not get cluster %d: %w", index, err) 190 } 191 convertedCluster, err := FromCluster(cluster) 192 if err != nil { 193 return nil, fmt.Errorf("could not convert cluster %d: %w", index, err) 194 } 195 epoch.Clusters = append(epoch.Clusters, convertedCluster.enc) 196 } 197 198 // convert height bounds 199 firstHeight, err := from.FirstHeight() 200 if errors.Is(err, protocol.ErrEpochTransitionNotFinalized) { 201 // if this epoch hasn't been started yet, return the epoch as-is 202 return &Epoch{epoch}, nil 203 } 204 if err != nil { 205 return nil, fmt.Errorf("could not get first height: %w", err) 206 } 207 epoch.FirstHeight = &firstHeight 208 finalHeight, err := from.FinalHeight() 209 if errors.Is(err, protocol.ErrEpochTransitionNotFinalized) { 210 // if this epoch hasn't ended yet, return the epoch as-is 211 return &Epoch{epoch}, nil 212 } 213 if err != nil { 214 return nil, fmt.Errorf("could not get final height: %w", err) 215 } 216 epoch.FinalHeight = &finalHeight 217 218 return &Epoch{epoch}, nil 219 } 220 221 // FromCluster converts any protocol.Cluster to a memory-backed Cluster. 222 // No errors are expected during normal operation. 223 func FromCluster(from protocol.Cluster) (*Cluster, error) { 224 cluster := EncodableCluster{ 225 Counter: from.EpochCounter(), 226 Index: from.Index(), 227 Members: from.Members(), 228 RootBlock: from.RootBlock(), 229 RootQC: from.RootQC(), 230 } 231 return &Cluster{cluster}, nil 232 } 233 234 // FromDKG converts any protocol.DKG to a memory-backed DKG. 235 // 236 // The given participant list must exactly match the DKG members. 237 // All errors indicate inconsistent or invalid inputs. 238 // No errors are expected during normal operation. 239 func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { 240 var dkg EncodableDKG 241 dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} 242 243 lookup, err := protocol.ToDKGParticipantLookup(from, participants) 244 if err != nil { 245 return nil, fmt.Errorf("could not generate dkg participant lookup: %w", err) 246 } 247 dkg.Participants = lookup 248 249 return &DKG{dkg}, nil 250 } 251 252 // DKGFromEncodable returns a DKG backed by the given encodable representation. 253 func DKGFromEncodable(enc EncodableDKG) (*DKG, error) { 254 return &DKG{enc}, nil 255 } 256 257 // ClusterFromEncodable returns a Cluster backed by the given encodable representation. 258 func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { 259 return &Cluster{enc}, nil 260 } 261 262 // SnapshotFromBootstrapState generates a protocol.Snapshot representing a 263 // root bootstrap state. This is used to bootstrap the protocol state for 264 // genesis or post-spork states. 265 func SnapshotFromBootstrapState(root *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, qc *flow.QuorumCertificate) (*Snapshot, error) { 266 version := flow.DefaultProtocolVersion 267 threshold, err := protocol.DefaultEpochCommitSafetyThreshold(root.Header.ChainID) 268 if err != nil { 269 return nil, fmt.Errorf("could not get default epoch commit safety threshold: %w", err) 270 } 271 return SnapshotFromBootstrapStateWithParams(root, result, seal, qc, version, threshold) 272 } 273 274 // SnapshotFromBootstrapStateWithParams is SnapshotFromBootstrapState 275 // with a caller-specified protocol version. 276 func SnapshotFromBootstrapStateWithParams( 277 root *flow.Block, 278 result *flow.ExecutionResult, 279 seal *flow.Seal, 280 qc *flow.QuorumCertificate, 281 protocolVersion uint, 282 epochCommitSafetyThreshold uint64, 283 ) (*Snapshot, error) { 284 setup, ok := result.ServiceEvents[0].Event.(*flow.EpochSetup) 285 if !ok { 286 return nil, fmt.Errorf("invalid setup event type (%T)", result.ServiceEvents[0].Event) 287 } 288 commit, ok := result.ServiceEvents[1].Event.(*flow.EpochCommit) 289 if !ok { 290 return nil, fmt.Errorf("invalid commit event type (%T)", result.ServiceEvents[1].Event) 291 } 292 293 clustering, err := ClusteringFromSetupEvent(setup) 294 if err != nil { 295 return nil, fmt.Errorf("setup event has invalid clustering: %w", err) 296 } 297 298 // sanity check the commit event has the same number of cluster QC as the number clusters 299 if len(clustering) != len(commit.ClusterQCs) { 300 return nil, fmt.Errorf("mismatching number of ClusterQCs, expect %v but got %v", 301 len(clustering), len(commit.ClusterQCs)) 302 } 303 304 // sanity check the QC in the commit event, which should be found in the identities in 305 // the setup event 306 for i, cluster := range clustering { 307 rootQCVoteData := commit.ClusterQCs[i] 308 _, err = signature.EncodeSignersToIndices(cluster.NodeIDs(), rootQCVoteData.VoterIDs) 309 if err != nil { 310 return nil, fmt.Errorf("mismatching cluster and qc: %w", err) 311 } 312 } 313 encodable, err := FromEpoch(NewStartedEpoch(setup, commit, root.Header.Height)) 314 if err != nil { 315 return nil, fmt.Errorf("could not convert epoch: %w", err) 316 } 317 epochs := EncodableEpochs{ 318 Current: encodable.enc, 319 } 320 321 params := EncodableParams{ 322 ChainID: root.Header.ChainID, // chain ID must match the root block 323 SporkID: root.ID(), // use root block ID as the unique spork identifier 324 SporkRootBlockHeight: root.Header.Height, // use root block height as the spork root block height 325 ProtocolVersion: protocolVersion, // major software version for this spork 326 EpochCommitSafetyThreshold: epochCommitSafetyThreshold, // see protocol.Params for details 327 } 328 329 snap := SnapshotFromEncodable(EncodableSnapshot{ 330 Head: root.Header, 331 Identities: setup.Participants, 332 LatestSeal: seal, 333 LatestResult: result, 334 SealingSegment: &flow.SealingSegment{ 335 Blocks: []*flow.Block{root}, 336 ExecutionResults: flow.ExecutionResultList{result}, 337 LatestSeals: map[flow.Identifier]flow.Identifier{root.ID(): seal.ID()}, 338 FirstSeal: seal, 339 ExtraBlocks: make([]*flow.Block, 0), 340 }, 341 QuorumCertificate: qc, 342 Phase: flow.EpochPhaseStaking, 343 Epochs: epochs, 344 Params: params, 345 SealedVersionBeacon: nil, 346 }) 347 return snap, nil 348 }