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  }