diff --git a/cmd/bootstrap/cmd/block.go b/cmd/bootstrap/cmd/block.go index 9d770f8097d..f4889740e81 100644 --- a/cmd/bootstrap/cmd/block.go +++ b/cmd/bootstrap/cmd/block.go @@ -34,7 +34,7 @@ func constructRootBlock(rootHeader *flow.Header, setup *flow.EpochSetup, commit Receipts: nil, Results: nil, // TODO: shortcut in bootstrapping; we will probably have to start with a non-empty KV store in the future - ProtocolStateID: kvstore.NewDefaultKVStore(inmem.ProtocolStateFromEpochServiceEvents(setup, commit).ID()).ID(), + ProtocolStateID: kvstore.NewDefaultKVStore(inmem.EpochProtocolStateFromServiceEvents(setup, commit).ID()).ID(), }) return block } diff --git a/cmd/consensus/main.go b/cmd/consensus/main.go index 69fba46895d..e9610f5d005 100644 --- a/cmd/consensus/main.go +++ b/cmd/consensus/main.go @@ -717,7 +717,7 @@ func main() { }). Component("consensus participant", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { mutableProtocolState := protocol_state.NewMutableProtocolState( - node.Storage.EpochProtocolState, + node.Storage.EpochProtocolStateEntries, node.Storage.ProtocolKVStore, node.State.Params(), node.Storage.Headers, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 173283a576c..734e8371b92 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -1181,30 +1181,30 @@ func (fnb *FlowNodeBuilder) initStorage() error { setups := bstorage.NewEpochSetups(fnb.Metrics.Cache, fnb.DB) epochCommits := bstorage.NewEpochCommits(fnb.Metrics.Cache, fnb.DB) commits := bstorage.NewCommits(fnb.Metrics.Cache, fnb.DB) - protocolState := bstorage.NewProtocolState(fnb.Metrics.Cache, setups, epochCommits, fnb.DB, - bstorage.DefaultProtocolStateCacheSize, bstorage.DefaultProtocolStateByBlockIDCacheSize) + protocolState := bstorage.NewEpochProtocolStateEntries(fnb.Metrics.Cache, setups, epochCommits, fnb.DB, + bstorage.DefaultEpochProtocolStateCacheSize, bstorage.DefaultProtocolStateIndexCacheSize) protocolKVStores := bstorage.NewProtocolKVStore(fnb.Metrics.Cache, fnb.DB, bstorage.DefaultProtocolKVStoreCacheSize, bstorage.DefaultProtocolKVStoreByBlockIDCacheSize) versionBeacons := bstorage.NewVersionBeacons(fnb.DB) fnb.Storage = Storage{ - Headers: headers, - Guarantees: guarantees, - Receipts: receipts, - Results: results, - Seals: seals, - Index: index, - Payloads: payloads, - Blocks: blocks, - QuorumCertificates: qcs, - Transactions: transactions, - Collections: collections, - Setups: setups, - EpochCommits: epochCommits, - VersionBeacons: versionBeacons, - EpochProtocolState: protocolState, - ProtocolKVStore: protocolKVStores, - Commits: commits, + Headers: headers, + Guarantees: guarantees, + Receipts: receipts, + Results: results, + Seals: seals, + Index: index, + Payloads: payloads, + Blocks: blocks, + QuorumCertificates: qcs, + Transactions: transactions, + Collections: collections, + Setups: setups, + EpochCommits: epochCommits, + VersionBeacons: versionBeacons, + EpochProtocolStateEntries: protocolState, + ProtocolKVStore: protocolKVStores, + Commits: commits, } return nil @@ -1293,7 +1293,7 @@ func (fnb *FlowNodeBuilder) initState() error { fnb.Storage.QuorumCertificates, fnb.Storage.Setups, fnb.Storage.EpochCommits, - fnb.Storage.EpochProtocolState, + fnb.Storage.EpochProtocolStateEntries, fnb.Storage.ProtocolKVStore, fnb.Storage.VersionBeacons, ) @@ -1342,7 +1342,7 @@ func (fnb *FlowNodeBuilder) initState() error { fnb.Storage.QuorumCertificates, fnb.Storage.Setups, fnb.Storage.EpochCommits, - fnb.Storage.EpochProtocolState, + fnb.Storage.EpochProtocolStateEntries, fnb.Storage.ProtocolKVStore, fnb.Storage.VersionBeacons, fnb.RootSnapshot, diff --git a/cmd/util/cmd/common/state.go b/cmd/util/cmd/common/state.go index 01478ef5b3a..3ca3c82aa54 100644 --- a/cmd/util/cmd/common/state.go +++ b/cmd/util/cmd/common/state.go @@ -24,7 +24,7 @@ func InitProtocolState(db *badger.DB, storages *storage.All) (protocol.State, er storages.QuorumCertificates, storages.Setups, storages.EpochCommits, - storages.EpochProtocolState, + storages.EpochProtocolStateEntries, storages.ProtocolKVStore, storages.VersionBeacons, ) diff --git a/cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go b/cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go index c8f1002962e..353131da505 100644 --- a/cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go +++ b/cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go @@ -30,7 +30,7 @@ var epochProtocolStateCmd = &cobra.Command{ } log.Info().Msgf("getting protocol state by block id: %v", blockID) - protocolState, err := storages.EpochProtocolState.ByBlockID(blockID) + protocolState, err := storages.EpochProtocolStateEntries.ByBlockID(blockID) if err != nil { log.Error().Err(err).Msgf("could not get protocol state for block id: %v", blockID) return diff --git a/consensus/integration/epoch_test.go b/consensus/integration/epoch_test.go index ae56e01befd..afa772e8d9d 100644 --- a/consensus/integration/epoch_test.go +++ b/consensus/integration/epoch_test.go @@ -240,8 +240,8 @@ func withNextEpoch( ActiveIdentities: flow.DynamicIdentityEntryListFromIdentities(nextEpochIdentities), } // Re-construct epoch protocol state with modified events (constructs ActiveIdentity fields) - epochProtocolState, err = flow.NewRichProtocolStateEntry( - epochProtocolState.ProtocolStateEntry, + epochProtocolState, err = flow.NewRichEpochProtocolStateEntry( + epochProtocolState.EpochProtocolStateEntry, epochProtocolState.PreviousEpochSetup, epochProtocolState.PreviousEpochCommit, currEpochSetup, currEpochCommit, nextEpochSetup, nextEpochCommit) diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index 9a8ff0a81e6..4cdf29e5b0e 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -281,7 +281,7 @@ func createRootBlockData(participantData *run.ParticipantData) (*flow.Block, *fl }, ) - epochProtocolStateID := inmem.ProtocolStateFromEpochServiceEvents(setup, commit).ID() + epochProtocolStateID := inmem.EpochProtocolStateFromServiceEvents(setup, commit).ID() root.SetPayload(flow.Payload{ProtocolStateID: kvstore.NewDefaultKVStore(epochProtocolStateID).ID()}) result := unittest.BootstrapExecutionResultFixture(root, unittest.GenesisStateCommitment) result.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent(), commit.ServiceEvent()} @@ -378,8 +378,8 @@ func createNode( qcsDB := storage.NewQuorumCertificates(metricsCollector, db, storage.DefaultCacheSize) setupsDB := storage.NewEpochSetups(metricsCollector, db) commitsDB := storage.NewEpochCommits(metricsCollector, db) - protocolStateDB := storage.NewProtocolState(metricsCollector, setupsDB, commitsDB, db, - storage.DefaultProtocolStateCacheSize, storage.DefaultProtocolStateByBlockIDCacheSize) + protocolStateDB := storage.NewEpochProtocolStateEntries(metricsCollector, setupsDB, commitsDB, db, + storage.DefaultEpochProtocolStateCacheSize, storage.DefaultProtocolStateIndexCacheSize) protocokKVStoreDB := storage.NewProtocolKVStore(metricsCollector, db, storage.DefaultProtocolKVStoreCacheSize, storage.DefaultProtocolKVStoreByBlockIDCacheSize) versionBeaconDB := storage.NewVersionBeacons(db) diff --git a/engine/collection/test/cluster_switchover_test.go b/engine/collection/test/cluster_switchover_test.go index bf8f316e997..83b15292d8d 100644 --- a/engine/collection/test/cluster_switchover_test.go +++ b/engine/collection/test/cluster_switchover_test.go @@ -100,7 +100,7 @@ func NewClusterSwitchoverTestCase(t *testing.T, conf ClusterSwitchoverTestConf) seal.ResultID = result.ID() root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore( - inmem.ProtocolStateFromEpochServiceEvents(setup, commit).ID()).ID() + inmem.EpochProtocolStateFromServiceEvents(setup, commit).ID()).ID() tc.root, err = inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(t, err) diff --git a/engine/common/follower/integration_test.go b/engine/common/follower/integration_test.go index d01b63ef927..dab99be7e0f 100644 --- a/engine/common/follower/integration_test.go +++ b/engine/common/follower/integration_test.go @@ -63,7 +63,7 @@ func TestFollowerHappyPath(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, diff --git a/engine/testutil/mock/nodes.go b/engine/testutil/mock/nodes.go index 8eaf10666d5..d12a409cf57 100644 --- a/engine/testutil/mock/nodes.go +++ b/engine/testutil/mock/nodes.go @@ -82,7 +82,7 @@ type GenericNode struct { Results storage.ExecutionResults Setups storage.EpochSetups EpochCommits storage.EpochCommits - EpochProtocolState storage.ProtocolState + EpochProtocolState storage.EpochProtocolStateEntries ProtocolKVStore storage.ProtocolKVStore State protocol.ParticipantState Index storage.Index diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index 90baf0264bd..b6d1037b500 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -206,7 +206,7 @@ func GenericNodeWithStateFixture(t testing.TB, Results: stateFixture.Storage.Results, Setups: stateFixture.Storage.Setups, EpochCommits: stateFixture.Storage.EpochCommits, - EpochProtocolState: stateFixture.Storage.EpochProtocolState, + EpochProtocolState: stateFixture.Storage.EpochProtocolStateEntries, ProtocolKVStore: stateFixture.Storage.ProtocolKVStore, State: stateFixture.State, Index: stateFixture.Storage.Index, @@ -245,7 +245,7 @@ func CompleteStateFixture( s.QuorumCertificates, s.Setups, s.EpochCommits, - s.EpochProtocolState, + s.EpochProtocolStateEntries, s.ProtocolKVStore, s.VersionBeacons, rootSnapshot, diff --git a/engine/verification/utils/unittest/helper.go b/engine/verification/utils/unittest/helper.go index bc815f40623..e38baa5e8f0 100644 --- a/engine/verification/utils/unittest/helper.go +++ b/engine/verification/utils/unittest/helper.go @@ -654,7 +654,7 @@ func bootstrapSystem( identities = append(identities, verID.Identity()) mutableProtocolState := protocol_state.NewMutableProtocolState( - stateFixture.Storage.EpochProtocolState, + stateFixture.Storage.EpochProtocolStateEntries, stateFixture.Storage.ProtocolKVStore, stateFixture.State.Params(), stateFixture.Storage.Headers, diff --git a/integration/testnet/container.go b/integration/testnet/container.go index 7d310139e8f..e466363b3e4 100644 --- a/integration/testnet/container.go +++ b/integration/testnet/container.go @@ -395,8 +395,8 @@ func (c *Container) OpenState() (*state.State, error) { qcs := storage.NewQuorumCertificates(metrics, db, storage.DefaultCacheSize) setups := storage.NewEpochSetups(metrics, db) commits := storage.NewEpochCommits(metrics, db) - protocolState := storage.NewProtocolState(metrics, setups, commits, db, - storage.DefaultProtocolStateCacheSize, storage.DefaultProtocolStateByBlockIDCacheSize) + protocolState := storage.NewEpochProtocolStateEntries(metrics, setups, commits, db, + storage.DefaultEpochProtocolStateCacheSize, storage.DefaultProtocolStateIndexCacheSize) protocolKVStates := storage.NewProtocolKVStore(metrics, db, storage.DefaultProtocolKVStoreCacheSize, storage.DefaultProtocolKVStoreByBlockIDCacheSize) versionBeacons := storage.NewVersionBeacons(db) diff --git a/integration/testnet/network.go b/integration/testnet/network.go index ca1f98396b6..c7820812942 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -1165,7 +1165,7 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string, chainID fl } root.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID( networkConf.KVStoreFactory( - inmem.ProtocolStateFromEpochServiceEvents(epochSetup, epochCommit).ID(), + inmem.EpochProtocolStateFromServiceEvents(epochSetup, epochCommit).ID(), ).ID(), ))) diff --git a/model/flow/protocol_state.go b/model/flow/protocol_state.go index 3d0542a7293..53798a13530 100644 --- a/model/flow/protocol_state.go +++ b/model/flow/protocol_state.go @@ -14,7 +14,7 @@ type DynamicIdentityEntry struct { type DynamicIdentityEntryList []*DynamicIdentityEntry -// ProtocolStateEntry represents a snapshot of the identity table (incl. the set of all notes authorized to +// EpochProtocolStateEntry represents a snapshot of the identity table (incl. the set of all notes authorized to // be part of the network) at some point in time. It allows to reconstruct the state of identity table using // epoch setup events and dynamic identities. It tracks attempts of invalid state transitions. // It also holds information about the next epoch, if it has been already committed. @@ -23,7 +23,7 @@ type DynamicIdentityEntryList []*DynamicIdentityEntry // Note that the current implementation does not store the identity table directly. Instead, we store // the original events that constituted the _initial_ identity table at the beginning of the epoch // plus some modifiers. We intend to restructure this code soon. -type ProtocolStateEntry struct { +type EpochProtocolStateEntry struct { PreviousEpoch *EpochStateContainer // minimal dynamic properties for previous epoch [optional, nil for first epoch after spork, genesis] CurrentEpoch EpochStateContainer // minimal dynamic properties for current epoch NextEpoch *EpochStateContainer // minimal dynamic properties for next epoch [optional, nil iff we are in staking phase] @@ -93,9 +93,9 @@ func (c *EpochStateContainer) Copy() *EpochStateContainer { } } -// RichProtocolStateEntry is a ProtocolStateEntry which has additional fields that are cached +// RichEpochProtocolStateEntry is a EpochProtocolStateEntry which has additional fields that are cached // from storage layer for convenience. -// Using this structure instead of ProtocolStateEntry allows us to avoid querying +// Using this structure instead of EpochProtocolStateEntry allows us to avoid querying // the database for epoch setups and commits and full identity table. // It holds several invariants, such as: // - CurrentEpochSetup and CurrentEpochCommit are for the same epoch. Never nil. @@ -111,8 +111,8 @@ func (c *EpochStateContainer) Copy() *EpochStateContainer { // the Identity Table additionally contains nodes (with weight zero) from the previous or // upcoming epoch, which are transitioning into / out of the network and are only allowed // to listen but not to actively contribute. -type RichProtocolStateEntry struct { - *ProtocolStateEntry +type RichEpochProtocolStateEntry struct { + *EpochProtocolStateEntry PreviousEpochSetup *EpochSetup PreviousEpochCommit *EpochCommit @@ -124,19 +124,19 @@ type RichProtocolStateEntry struct { NextEpochIdentityTable IdentityList } -// NewRichProtocolStateEntry constructs a rich protocol state entry from a protocol state entry and additional data. +// NewRichEpochProtocolStateEntry constructs a RichEpochProtocolStateEntry from an EpochProtocolStateEntry and additional data. // No errors are expected during normal operation. All errors indicate inconsistent or invalid inputs. -func NewRichProtocolStateEntry( - protocolState *ProtocolStateEntry, +func NewRichEpochProtocolStateEntry( + protocolState *EpochProtocolStateEntry, previousEpochSetup *EpochSetup, previousEpochCommit *EpochCommit, currentEpochSetup *EpochSetup, currentEpochCommit *EpochCommit, nextEpochSetup *EpochSetup, nextEpochCommit *EpochCommit, -) (*RichProtocolStateEntry, error) { - result := &RichProtocolStateEntry{ - ProtocolStateEntry: protocolState, +) (*RichEpochProtocolStateEntry, error) { + result := &RichEpochProtocolStateEntry{ + EpochProtocolStateEntry: protocolState, PreviousEpochSetup: previousEpochSetup, PreviousEpochCommit: previousEpochCommit, CurrentEpochSetup: currentEpochSetup, @@ -147,22 +147,22 @@ func NewRichProtocolStateEntry( NextEpochIdentityTable: IdentityList{}, } - // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `ProtocolStateEntry.PreviousEpoch` + // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `EpochProtocolStateEntry.PreviousEpoch` if protocolState.PreviousEpoch != nil { if protocolState.PreviousEpoch.SetupID != previousEpochSetup.ID() { // calling ID() will panic is EpochSetup event is nil - return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) + return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) } if protocolState.PreviousEpoch.CommitID != previousEpochCommit.ID() { // calling ID() will panic is EpochCommit event is nil - return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) + return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) } } - // For current epoch: ensure respective epoch service events are not nil and consistent with commitments in `ProtocolStateEntry.CurrentEpoch` + // For current epoch: ensure respective epoch service events are not nil and consistent with commitments in `EpochProtocolStateEntry.CurrentEpoch` if protocolState.CurrentEpoch.SetupID != currentEpochSetup.ID() { // calling ID() will panic is EpochSetup event is nil - return nil, fmt.Errorf("supplied current epoch's setup event (%x) does not match commitment (%x) in ProtocolStateEntry", currentEpochSetup.ID(), protocolState.CurrentEpoch.SetupID) + return nil, fmt.Errorf("supplied current epoch's setup event (%x) does not match commitment (%x) in EpochProtocolStateEntry", currentEpochSetup.ID(), protocolState.CurrentEpoch.SetupID) } if protocolState.CurrentEpoch.CommitID != currentEpochCommit.ID() { // calling ID() will panic is EpochCommit event is nil - return nil, fmt.Errorf("supplied current epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", currentEpochCommit.ID(), protocolState.CurrentEpoch.CommitID) + return nil, fmt.Errorf("supplied current epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", currentEpochCommit.ID(), protocolState.CurrentEpoch.CommitID) } // If we are in staking phase (i.e. protocolState.NextEpoch == nil): @@ -192,13 +192,13 @@ func NewRichProtocolStateEntry( return nil, fmt.Errorf("could not build identity table for staking phase: %w", err) } } else { // protocolState.NextEpoch ≠ nil, i.e. we are in epoch setup or epoch commit phase - // ensure respective epoch service events are not nil and consistent with commitments in `ProtocolStateEntry.NextEpoch` + // ensure respective epoch service events are not nil and consistent with commitments in `EpochProtocolStateEntry.NextEpoch` if nextEpoch.SetupID != nextEpochSetup.ID() { - return nil, fmt.Errorf("supplied next epoch's setup event (%x) does not match commitment (%x) in ProtocolStateEntry", nextEpoch.SetupID, nextEpochSetup.ID()) + return nil, fmt.Errorf("supplied next epoch's setup event (%x) does not match commitment (%x) in EpochProtocolStateEntry", nextEpoch.SetupID, nextEpochSetup.ID()) } if nextEpoch.CommitID != ZeroID { if nextEpoch.CommitID != nextEpochCommit.ID() { - return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) + return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) } } @@ -228,7 +228,7 @@ func NewRichProtocolStateEntry( } // ID returns hash of entry by hashing all fields. -func (e *ProtocolStateEntry) ID() Identifier { +func (e *EpochProtocolStateEntry) ID() Identifier { if e == nil { return ZeroID } @@ -248,11 +248,11 @@ func (e *ProtocolStateEntry) ID() Identifier { // Copy returns a full copy of the entry. // Embedded Identities are deep-copied, _except_ for their keys, which are copied by reference. -func (e *ProtocolStateEntry) Copy() *ProtocolStateEntry { +func (e *EpochProtocolStateEntry) Copy() *EpochProtocolStateEntry { if e == nil { return nil } - return &ProtocolStateEntry{ + return &EpochProtocolStateEntry{ PreviousEpoch: e.PreviousEpoch.Copy(), CurrentEpoch: *e.CurrentEpoch.Copy(), NextEpoch: e.NextEpoch.Copy(), @@ -260,15 +260,15 @@ func (e *ProtocolStateEntry) Copy() *ProtocolStateEntry { } } -// Copy returns a full copy of rich protocol state entry. +// Copy returns a full copy of the RichEpochProtocolStateEntry. // - Embedded service events are copied by reference (not deep-copied). // - CurrentEpochIdentityTable and NextEpochIdentityTable are deep-copied, _except_ for their keys, which are copied by reference. -func (e *RichProtocolStateEntry) Copy() *RichProtocolStateEntry { +func (e *RichEpochProtocolStateEntry) Copy() *RichEpochProtocolStateEntry { if e == nil { return nil } - return &RichProtocolStateEntry{ - ProtocolStateEntry: e.ProtocolStateEntry.Copy(), + return &RichEpochProtocolStateEntry{ + EpochProtocolStateEntry: e.EpochProtocolStateEntry.Copy(), PreviousEpochSetup: e.PreviousEpochSetup, PreviousEpochCommit: e.PreviousEpochCommit, CurrentEpochSetup: e.CurrentEpochSetup, @@ -281,8 +281,8 @@ func (e *RichProtocolStateEntry) Copy() *RichProtocolStateEntry { } // EpochPhase returns the current epoch phase. -// The receiver ProtocolStateEntry must be properly constructed. -func (e *ProtocolStateEntry) EpochPhase() EpochPhase { +// The receiver EpochProtocolStateEntry must be properly constructed. +func (e *EpochProtocolStateEntry) EpochPhase() EpochPhase { // The epoch phase is determined by how much information we have about the next epoch if e.NextEpoch == nil { return EpochPhaseStaking // if no information about the next epoch is known, we are in the Staking Phase @@ -295,8 +295,8 @@ func (e *ProtocolStateEntry) EpochPhase() EpochPhase { } // EpochCounter returns the current epoch counter. -// The receiver RichProtocolStateEntry must be properly constructed. -func (e *RichProtocolStateEntry) EpochCounter() uint64 { +// The receiver RichEpochProtocolStateEntry must be properly constructed. +func (e *RichEpochProtocolStateEntry) EpochCounter() uint64 { return e.CurrentEpochSetup.Counter } diff --git a/model/flow/protocol_state_test.go b/model/flow/protocol_state_test.go index 2b54f6a904d..c1bbdba5d4f 100644 --- a/model/flow/protocol_state_test.go +++ b/model/flow/protocol_state_test.go @@ -10,7 +10,7 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestNewRichProtocolStateEntry checks that NewRichProtocolStateEntry creates valid identity tables depending on the state +// TestNewRichProtocolStateEntry checks that NewRichEpochProtocolStateEntry creates valid identity tables depending on the state // of epoch which is derived from the protocol state entry. func TestNewRichProtocolStateEntry(t *testing.T) { // Conditions right after a spork: @@ -26,7 +26,7 @@ func TestNewRichProtocolStateEntry(t *testing.T) { Ejected: false, }) } - stateEntry := &flow.ProtocolStateEntry{ + stateEntry := &flow.EpochProtocolStateEntry{ PreviousEpoch: nil, CurrentEpoch: flow.EpochStateContainer{ SetupID: setup.ID(), @@ -35,7 +35,7 @@ func TestNewRichProtocolStateEntry(t *testing.T) { }, InvalidEpochTransitionAttempted: false, } - entry, err := flow.NewRichProtocolStateEntry( + entry, err := flow.NewRichEpochProtocolStateEntry( stateEntry, nil, nil, @@ -64,8 +64,8 @@ func TestNewRichProtocolStateEntry(t *testing.T) { // * network is currently in the staking phase for the next epoch, hence no service events for the next epoch exist t.Run("staking-phase", func(t *testing.T) { stateEntry := unittest.EpochStateFixture() - richEntry, err := flow.NewRichProtocolStateEntry( - stateEntry.ProtocolStateEntry, + richEntry, err := flow.NewRichEpochProtocolStateEntry( + stateEntry.EpochProtocolStateEntry, stateEntry.PreviousEpochSetup, stateEntry.PreviousEpochCommit, stateEntry.CurrentEpochSetup, @@ -93,13 +93,13 @@ func TestNewRichProtocolStateEntry(t *testing.T) { // * previous epoch N-1 is known (specifically EpochSetup and EpochCommit events) // * network is currently in the setup phase for the next epoch, i.e. EpochSetup event (starting setup phase) has already been observed t.Run("setup-phase", func(t *testing.T) { - stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { + stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichEpochProtocolStateEntry) { entry.NextEpochCommit = nil entry.NextEpoch.CommitID = flow.ZeroID }) - richEntry, err := flow.NewRichProtocolStateEntry( - stateEntry.ProtocolStateEntry, + richEntry, err := flow.NewRichEpochProtocolStateEntry( + stateEntry.EpochProtocolStateEntry, stateEntry.PreviousEpochSetup, stateEntry.PreviousEpochCommit, stateEntry.CurrentEpochSetup, @@ -132,7 +132,7 @@ func TestNewRichProtocolStateEntry(t *testing.T) { }) t.Run("setup-after-spork", func(t *testing.T) { - stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { + stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichEpochProtocolStateEntry) { // no previous epoch since we are in the first epoch entry.PreviousEpochSetup = nil entry.PreviousEpochCommit = nil @@ -147,8 +147,8 @@ func TestNewRichProtocolStateEntry(t *testing.T) { assert.Nil(t, stateEntry.PreviousEpochSetup) assert.Nil(t, stateEntry.PreviousEpochCommit) - richEntry, err := flow.NewRichProtocolStateEntry( - stateEntry.ProtocolStateEntry, + richEntry, err := flow.NewRichEpochProtocolStateEntry( + stateEntry.EpochProtocolStateEntry, stateEntry.PreviousEpochSetup, stateEntry.PreviousEpochCommit, stateEntry.CurrentEpochSetup, @@ -187,8 +187,8 @@ func TestNewRichProtocolStateEntry(t *testing.T) { t.Run("commit-phase", func(t *testing.T) { stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) - richEntry, err := flow.NewRichProtocolStateEntry( - stateEntry.ProtocolStateEntry, + richEntry, err := flow.NewRichEpochProtocolStateEntry( + stateEntry.EpochProtocolStateEntry, stateEntry.PreviousEpochSetup, stateEntry.PreviousEpochCommit, stateEntry.CurrentEpochSetup, @@ -220,7 +220,7 @@ func TestNewRichProtocolStateEntry(t *testing.T) { }) t.Run("commit-after-spork", func(t *testing.T) { - stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { + stateEntry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichEpochProtocolStateEntry) { // no previous epoch since we are in the first epoch entry.PreviousEpochSetup = nil entry.PreviousEpochCommit = nil @@ -231,8 +231,8 @@ func TestNewRichProtocolStateEntry(t *testing.T) { assert.Nil(t, stateEntry.PreviousEpochSetup) assert.Nil(t, stateEntry.PreviousEpochCommit) - richEntry, err := flow.NewRichProtocolStateEntry( - stateEntry.ProtocolStateEntry, + richEntry, err := flow.NewRichEpochProtocolStateEntry( + stateEntry.EpochProtocolStateEntry, stateEntry.PreviousEpochSetup, stateEntry.PreviousEpochCommit, stateEntry.CurrentEpochSetup, @@ -267,7 +267,7 @@ func TestNewRichProtocolStateEntry(t *testing.T) { // TestProtocolStateEntry_Copy tests if the copy method returns a deep copy of the entry. // All changes to copy shouldn't affect the original entry -- except for key changes. func TestProtocolStateEntry_Copy(t *testing.T) { - entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()).ProtocolStateEntry + entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()).EpochProtocolStateEntry cpy := entry.Copy() assert.Equal(t, entry, cpy) assert.NotSame(t, entry.NextEpoch, cpy.NextEpoch) diff --git a/model/flow/sealing_segment.go b/model/flow/sealing_segment.go index 0dc6a088f30..63373e666f5 100644 --- a/model/flow/sealing_segment.go +++ b/model/flow/sealing_segment.go @@ -72,13 +72,13 @@ type SealingSegment struct { // per unique ProtocolStateID field within the segment's blocks. // Currently, although epoch data is conceptually a part of the protocol data entry associated // with each block, it is stored separately as a matter of technical debt (only a hash commitment -// `RichProtocolStateEntry.ID()` is stored within the `KVStoreEntry`. +// `RichEpochProtocolStateEntry.ID()` is stored within the `KVStoreEntry`. // // Deprecated: avoid using this in new code; this is a temporary measure until epoch data is moved into protocol KV store // TODO: move epoch data into the KVStore as part of a future upgrade type ProtocolStateEntryWrapper struct { KVStore PSKeyValueStoreData - EpochEntry *RichProtocolStateEntry + EpochEntry *RichEpochProtocolStateEntry } // Highest is the highest block in the sealing segment and the reference block from snapshot that was diff --git a/module/builder/collection/builder_test.go b/module/builder/collection/builder_test.go index 3b5f081a8e6..24fe9cbbd07 100644 --- a/module/builder/collection/builder_test.go +++ b/module/builder/collection/builder_test.go @@ -87,7 +87,7 @@ func (suite *BuilderSuite) SetupTest() { // ensure we don't enter a new epoch for tests that build many blocks result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 seal.ResultID = result.ID() - root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.ProtocolStateFromEpochServiceEvents( + root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.EpochProtocolStateFromServiceEvents( result.ServiceEvents[0].Event.(*flow.EpochSetup), result.ServiceEvents[1].Event.(*flow.EpochCommit), ).ID()).ID() @@ -96,7 +96,7 @@ func (suite *BuilderSuite) SetupTest() { suite.epochCounter = rootSnapshot.Encodable().SealingSegment.LatestProtocolStateEntry().EpochEntry.EpochCounter() clusterQC := unittest.QuorumCertificateFixture(unittest.QCWithRootBlockID(suite.genesis.ID())) - root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.ProtocolStateFromEpochServiceEvents( + root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.EpochProtocolStateFromServiceEvents( result.ServiceEvents[0].Event.(*flow.EpochSetup), result.ServiceEvents[1].Event.(*flow.EpochCommit), ).ID()).ID() @@ -118,7 +118,7 @@ func (suite *BuilderSuite) SetupTest() { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, diff --git a/module/state_synchronization/requester/execution_data_requester_test.go b/module/state_synchronization/requester/execution_data_requester_test.go index d65b841bdfc..356e4086d97 100644 --- a/module/state_synchronization/requester/execution_data_requester_test.go +++ b/module/state_synchronization/requester/execution_data_requester_test.go @@ -796,13 +796,13 @@ func (m *mockSnapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) func (m *mockSnapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { return nil, nil, nil } -func (m *mockSnapshot) Commit() (flow.StateCommitment, error) { return flow.DummyStateCommitment, nil } -func (m *mockSnapshot) SealingSegment() (*flow.SealingSegment, error) { return nil, nil } -func (m *mockSnapshot) Descendants() ([]flow.Identifier, error) { return nil, nil } -func (m *mockSnapshot) RandomSource() ([]byte, error) { return nil, nil } -func (m *mockSnapshot) Phase() (flow.EpochPhase, error) { return flow.EpochPhaseUndefined, nil } -func (m *mockSnapshot) Epochs() protocol.EpochQuery { return nil } -func (m *mockSnapshot) Params() protocol.GlobalParams { return nil } -func (m *mockSnapshot) EpochProtocolState() (protocol.DynamicProtocolState, error) { return nil, nil } -func (m *mockSnapshot) ProtocolState() (protocol.KVStoreReader, error) { return nil, nil } -func (m *mockSnapshot) VersionBeacon() (*flow.SealedVersionBeacon, error) { return nil, nil } +func (m *mockSnapshot) Commit() (flow.StateCommitment, error) { return flow.DummyStateCommitment, nil } +func (m *mockSnapshot) SealingSegment() (*flow.SealingSegment, error) { return nil, nil } +func (m *mockSnapshot) Descendants() ([]flow.Identifier, error) { return nil, nil } +func (m *mockSnapshot) RandomSource() ([]byte, error) { return nil, nil } +func (m *mockSnapshot) Phase() (flow.EpochPhase, error) { return flow.EpochPhaseUndefined, nil } +func (m *mockSnapshot) Epochs() protocol.EpochQuery { return nil } +func (m *mockSnapshot) Params() protocol.GlobalParams { return nil } +func (m *mockSnapshot) EpochProtocolState() (protocol.EpochProtocolState, error) { return nil, nil } +func (m *mockSnapshot) ProtocolState() (protocol.KVStoreReader, error) { return nil, nil } +func (m *mockSnapshot) VersionBeacon() (*flow.SealedVersionBeacon, error) { return nil, nil } diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 9a3b6b21c13..4db4bc16e0c 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -75,7 +75,7 @@ func (suite *MutatorSuite) SetupTest() { seal.ResultID = result.ID() qc := unittest.QuorumCertificateFixture(unittest.QCWithRootBlockID(genesis.ID())) - genesis.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.ProtocolStateFromEpochServiceEvents( + genesis.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(inmem.EpochProtocolStateFromServiceEvents( result.ServiceEvents[0].Event.(*flow.EpochSetup), result.ServiceEvents[1].Event.(*flow.EpochCommit), ).ID()).ID() @@ -94,7 +94,7 @@ func (suite *MutatorSuite) SetupTest() { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -104,7 +104,7 @@ func (suite *MutatorSuite) SetupTest() { require.NoError(suite.T(), err) suite.mutableProtocolState = protocol_state.NewMutableProtocolState( - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, state.Params(), all.Headers, diff --git a/state/cluster/badger/snapshot_test.go b/state/cluster/badger/snapshot_test.go index 6cd8f8b9b03..136118404fe 100644 --- a/state/cluster/badger/snapshot_test.go +++ b/state/cluster/badger/snapshot_test.go @@ -66,7 +66,7 @@ func (suite *SnapshotSuite) SetupTest() { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, root, diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index da47fa3a415..395aa197ce2 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -691,15 +691,15 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e // We update metrics and emit protocol events for epoch state changes when // the block corresponding to the state change is finalized - parentPsSnapshot, err := m.protocolState.AtBlockID(block.Header.ParentID) + parentEpochState, err := m.protocolState.EpochStateAtBlockID(block.Header.ParentID) if err != nil { return fmt.Errorf("could not retrieve protocol state snapshot for parent: %w", err) } - finalizingPsSnapshot, err := m.protocolState.AtBlockID(blockID) + finalizingEpochState, err := m.protocolState.EpochStateAtBlockID(blockID) if err != nil { return fmt.Errorf("could not retrieve protocol state snapshot: %w", err) } - currentEpochSetup := finalizingPsSnapshot.EpochSetup() + currentEpochSetup := finalizingEpochState.EpochSetup() epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() if err != nil { return fmt.Errorf("could not check persisted epoch emergency fallback flag: %w", err) @@ -707,7 +707,7 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e // if epoch fallback was not previously triggered, check whether this block triggers it // TODO(efm-recovery): remove separate global EFM flag - if !epochFallbackTriggered && finalizingPsSnapshot.InvalidEpochTransitionAttempted() { + if !epochFallbackTriggered && finalizingEpochState.InvalidEpochTransitionAttempted() { epochFallbackTriggered = true // emit the protocol event only the first time epoch fallback is triggered events = append(events, m.consumer.EpochEmergencyFallbackTriggered) @@ -715,7 +715,7 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e } // Determine metric updates and protocol events related to epoch phase changes and epoch transitions. - epochPhaseMetrics, epochPhaseEvents, err := m.epochMetricsAndEventsOnBlockFinalized(parentPsSnapshot, finalizingPsSnapshot, header) + epochPhaseMetrics, epochPhaseEvents, err := m.epochMetricsAndEventsOnBlockFinalized(parentEpochState, finalizingEpochState, header) if err != nil { return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) } @@ -755,7 +755,7 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e } } // TODO(efm-recovery): we should be able to omit the `!epochFallbackTriggered` check here. - if isFirstBlockOfEpoch(parentPsSnapshot, finalizingPsSnapshot) && !epochFallbackTriggered { + if isFirstBlockOfEpoch(parentEpochState, finalizingEpochState) && !epochFallbackTriggered { err = operation.InsertEpochFirstHeight(currentEpochSetup.Counter, header.Height)(tx) if err != nil { return fmt.Errorf("could not insert epoch first block height: %w", err) @@ -824,8 +824,8 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e // isFirstBlockOfEpoch returns true if the given block is the first block of a new epoch // by comparing the block's Protocol State Snapshot to that of its parent. // NOTE: There can be multiple (un-finalized) blocks that qualify as the first block of epoch N. -func isFirstBlockOfEpoch(parentPsSnapshot, blockPsSnapshot protocol.DynamicProtocolState) bool { - return parentPsSnapshot.Epoch() < blockPsSnapshot.Epoch() +func isFirstBlockOfEpoch(parentEpochState, blockEpochState protocol.EpochProtocolState) bool { + return parentEpochState.Epoch() < blockEpochState.Epoch() } // epochMetricsAndEventsOnBlockFinalized determines metrics to update and protocol @@ -835,20 +835,20 @@ func isFirstBlockOfEpoch(parentPsSnapshot, blockPsSnapshot protocol.DynamicProto // // This method must be called for each finalized block. // No errors are expected during normal operation. -func (m *FollowerState) epochMetricsAndEventsOnBlockFinalized(parentPsSnapshot, finalizedPsSnapshot protocol.DynamicProtocolState, finalized *flow.Header) ( +func (m *FollowerState) epochMetricsAndEventsOnBlockFinalized(parentEpochState, finalizedEpochState protocol.EpochProtocolState, finalized *flow.Header) ( metrics []func(), events []func(), err error, ) { - if finalizedPsSnapshot.InvalidEpochTransitionAttempted() { + if finalizedEpochState.InvalidEpochTransitionAttempted() { // No epoch state changes to notify on when EFM is triggered return nil, nil, nil } - parentEpochCounter := parentPsSnapshot.Epoch() - childEpochCounter := finalizedPsSnapshot.Epoch() - parentEpochPhase := parentPsSnapshot.EpochPhase() - childEpochPhase := finalizedPsSnapshot.EpochPhase() + parentEpochCounter := parentEpochState.Epoch() + childEpochCounter := finalizedEpochState.Epoch() + parentEpochPhase := parentEpochState.EpochPhase() + childEpochPhase := finalizedEpochState.EpochPhase() // Same epoch phase -> nothing to do if parentEpochPhase == childEpochPhase { @@ -857,7 +857,7 @@ func (m *FollowerState) epochMetricsAndEventsOnBlockFinalized(parentPsSnapshot, // Different counter -> must be an epoch transition if parentEpochCounter != childEpochCounter { - childEpochSetup := finalizedPsSnapshot.EpochSetup() + childEpochSetup := finalizedEpochState.EpochSetup() events = append(events, func() { m.consumer.EpochTransition(childEpochSetup.Counter, finalized) }) // set current epoch counter corresponding to new epoch metrics = append(metrics, func() { m.metrics.CurrentEpochCounter(childEpochSetup.Counter) }) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 890594c20bd..9900f83fff0 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -109,7 +109,7 @@ func TestExtendValid(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -869,7 +869,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -891,7 +891,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { require.NoError(t, err) mutableProtocolState := protocol_state.NewMutableProtocolState( - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, state.Params(), all.Headers, @@ -2047,7 +2047,7 @@ func TestExtendInvalidSealsInBlock(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -2608,7 +2608,7 @@ func TestHeaderInvalidTimestamp(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index e917f2dfc97..9db428a5bf3 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -82,21 +82,21 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { } func (s *Snapshot) Phase() (flow.EpochPhase, error) { - psSnapshot, err := s.state.protocolState.AtBlockID(s.blockID) + epochState, err := s.state.protocolState.EpochStateAtBlockID(s.blockID) if err != nil { return flow.EpochPhaseUndefined, fmt.Errorf("could not retrieve protocol state snapshot: %w", err) } - return psSnapshot.EpochPhase(), nil + return epochState.EpochPhase(), nil } func (s *Snapshot) Identities(selector flow.IdentityFilter[flow.Identity]) (flow.IdentityList, error) { - psSnapshot, err := s.state.protocolState.AtBlockID(s.blockID) + epochState, err := s.state.protocolState.EpochStateAtBlockID(s.blockID) if err != nil { return nil, err } // apply the filter to the participants - identities := psSnapshot.Identities().Filter(selector) + identities := epochState.Identities().Filter(selector) return identities, nil } @@ -199,7 +199,7 @@ func (s *Snapshot) SealingSegment() (*flow.SealingSegment, error) { if err != nil { return nil, fmt.Errorf("could not decode kv store entry: %w", err) } - epochDataEntry, err := s.state.protocolStateSnapshotsDB.ByID(kvStoreReader.GetEpochStateID()) + epochDataEntry, err := s.state.epochProtocolStateEntriesDB.ByID(kvStoreReader.GetEpochStateID()) if err != nil { return nil, fmt.Errorf("could not get epoch data: %w", err) } @@ -354,8 +354,8 @@ func (s *Snapshot) Params() protocol.GlobalParams { // Returns state.ErrUnknownSnapshotReference if snapshot reference block is unknown. // All other errors should be treated as exceptions. // For each block stored there should be a protocol state stored. -func (s *Snapshot) EpochProtocolState() (protocol.DynamicProtocolState, error) { - return s.state.protocolState.AtBlockID(s.blockID) +func (s *Snapshot) EpochProtocolState() (protocol.EpochProtocolState, error) { + return s.state.protocolState.EpochStateAtBlockID(s.blockID) } // ProtocolState returns the dynamic protocol state that the Head block commits to. @@ -385,13 +385,13 @@ type EpochQuery struct { func (q *EpochQuery) Current() protocol.Epoch { // all errors returned from storage reads here are unexpected, because all // snapshots reside within a current epoch, which must be queryable - psSnapshot, err := q.snap.state.protocolState.AtBlockID(q.snap.blockID) + epochState, err := q.snap.state.protocolState.EpochStateAtBlockID(q.snap.blockID) if err != nil { return invalid.NewEpochf("could not get protocol state snapshot at block %x: %w", q.snap.blockID, err) } - setup := psSnapshot.EpochSetup() - commit := psSnapshot.EpochCommit() + setup := epochState.EpochSetup() + commit := epochState.EpochCommit() firstHeight, _, isFirstHeightKnown, _, err := q.retrieveEpochHeightBounds(setup.Counter) if err != nil { return invalid.NewEpochf("could not get current epoch height bounds: %s", err.Error()) @@ -405,12 +405,12 @@ func (q *EpochQuery) Current() protocol.Epoch { // Next returns the next epoch, if it is available. func (q *EpochQuery) Next() protocol.Epoch { - psSnapshot, err := q.snap.state.protocolState.AtBlockID(q.snap.blockID) + epochState, err := q.snap.state.protocolState.EpochStateAtBlockID(q.snap.blockID) if err != nil { return invalid.NewEpochf("could not get protocol state snapshot at block %x: %w", q.snap.blockID, err) } - phase := psSnapshot.EpochPhase() - entry := psSnapshot.Entry() + phase := epochState.EpochPhase() + entry := epochState.Entry() // if we are in the staking phase, the next epoch is not setup yet if phase == flow.EpochPhaseStaking { @@ -434,15 +434,15 @@ func (q *EpochQuery) Next() protocol.Epoch { // For all other epochs, returns the previous epoch. func (q *EpochQuery) Previous() protocol.Epoch { - psSnapshot, err := q.snap.state.protocolState.AtBlockID(q.snap.blockID) + epochState, err := q.snap.state.protocolState.EpochStateAtBlockID(q.snap.blockID) if err != nil { return invalid.NewEpochf("could not get protocol state snapshot at block %x: %w", q.snap.blockID, err) } - entry := psSnapshot.Entry() + entry := epochState.Entry() // CASE 1: there is no previous epoch - this indicates we are in the first // epoch after a spork root or genesis block - if !psSnapshot.PreviousEpochExists() { + if !epochState.PreviousEpochExists() { return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index f7dd16ac0a6..eff61cbc5f1 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -236,7 +236,7 @@ func TestClusters(t *testing.T) { commit.ClusterQCs = flow.ClusterQCVoteDatasFromQCs(clusterQCs) seal.ResultID = result.ID() root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore( - inmem.ProtocolStateFromEpochServiceEvents(setup, commit).ID()).ID() + inmem.EpochProtocolStateFromServiceEvents(setup, commit).ID()).ID() rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(t, err) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 7ab029ed527..298f0f5c037 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -38,11 +38,11 @@ type State struct { setups storage.EpochSetups commits storage.EpochCommits } - params protocol.Params - protocolKVStoreSnapshotsDB storage.ProtocolKVStore - protocolStateSnapshotsDB storage.ProtocolState // TODO remove when ProtocolStateEntry is stored in KVStore - protocolState protocol.MutableProtocolState - versionBeacons storage.VersionBeacons + params protocol.Params + protocolKVStoreSnapshotsDB storage.ProtocolKVStore + epochProtocolStateEntriesDB storage.EpochProtocolStateEntries // TODO remove when EpochProtocolStateEntry is stored in KVStore + protocolState protocol.MutableProtocolState + versionBeacons storage.VersionBeacons // finalizedRootHeight marks the cutoff of the history this node knows about. We cache it in the state // because it cannot change over the lifecycle of a protocol state instance. It is frequently @@ -95,7 +95,7 @@ func Bootstrap( qcs storage.QuorumCertificates, setups storage.EpochSetups, commits storage.EpochCommits, - epochProtocolStateSnapshots storage.ProtocolState, + epochProtocolStateSnapshots storage.EpochProtocolStateEntries, protocolKVStoreSnapshots storage.ProtocolKVStore, versionBeacons storage.VersionBeacons, root protocol.Snapshot, @@ -237,7 +237,7 @@ func Bootstrap( func bootstrapProtocolState( segment *flow.SealingSegment, params protocol.GlobalParams, - epochProtocolStateSnapshots storage.ProtocolState, + epochProtocolStateSnapshots storage.EpochProtocolStateEntries, protocolKVStoreSnapshots storage.ProtocolKVStore, epochSetups storage.EpochSetups, epochCommits storage.EpochCommits, @@ -253,7 +253,7 @@ func bootstrapProtocolState( } // Store the epoch portion of the protocol state, including underlying EpochSetup/EpochCommit service events - dynamicEpochProtocolState := inmem.NewDynamicProtocolStateAdapter(stateEntry.EpochEntry, params) + dynamicEpochProtocolState := inmem.NewEpochProtocolStateAdapter(stateEntry.EpochEntry, params) err = bootstrapEpochForProtocolStateEntry(epochProtocolStateSnapshots, epochSetups, epochCommits, dynamicEpochProtocolState, verifyNetworkAddress)(tx) if err != nil { return fmt.Errorf("could not store epoch service events for state entry (id=%x): %w", stateEntry.EpochEntry.ID(), err) @@ -484,10 +484,10 @@ func bootstrapStatePointers(root protocol.Snapshot) func(*transaction.Tx) error // epoch information (service events) they reference, which case duplicate writes of // the same data are ignored. func bootstrapEpochForProtocolStateEntry( - epochProtocolStateSnapshots storage.ProtocolState, + epochProtocolStateSnapshots storage.EpochProtocolStateEntries, epochSetups storage.EpochSetups, epochCommits storage.EpochCommits, - epochProtocolStateEntry protocol.DynamicProtocolState, + epochProtocolStateEntry protocol.EpochProtocolState, verifyNetworkAddress bool, ) func(*transaction.Tx) error { return func(tx *transaction.Tx) error { @@ -562,7 +562,7 @@ func bootstrapEpochForProtocolStateEntry( } // insert epoch protocol state entry, which references above service events - err := operation.SkipDuplicatesTx(epochProtocolStateSnapshots.StoreTx(richEntry.ID(), richEntry.ProtocolStateEntry))(tx) + err := operation.SkipDuplicatesTx(epochProtocolStateSnapshots.StoreTx(richEntry.ID(), richEntry.EpochProtocolStateEntry))(tx) if err != nil { return fmt.Errorf("could not store epoch protocol state entry: %w", err) } @@ -655,7 +655,7 @@ func OpenState( qcs storage.QuorumCertificates, setups storage.EpochSetups, commits storage.EpochCommits, - epochProtocolState storage.ProtocolState, + epochProtocolState storage.EpochProtocolStateEntries, protocolKVStoreSnapshots storage.ProtocolKVStore, versionBeacons storage.VersionBeacons, ) (*State, error) { @@ -801,7 +801,7 @@ func newState( qcs storage.QuorumCertificates, setups storage.EpochSetups, commits storage.EpochCommits, - epochProtocolStateSnapshots storage.ProtocolState, + epochProtocolStateSnapshots storage.EpochProtocolStateEntries, protocolKVStoreSnapshots storage.ProtocolKVStore, versionBeacons storage.VersionBeacons, params protocol.Params, @@ -821,9 +821,9 @@ func newState( setups: setups, commits: commits, }, - params: params, - protocolKVStoreSnapshotsDB: protocolKVStoreSnapshots, - protocolStateSnapshotsDB: epochProtocolStateSnapshots, + params: params, + protocolKVStoreSnapshotsDB: protocolKVStoreSnapshots, + epochProtocolStateEntriesDB: epochProtocolStateSnapshots, protocolState: protocol_state. NewMutableProtocolState( epochProtocolStateSnapshots, diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 832dcb5b99a..44bb4a4b8ae 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -71,7 +71,7 @@ func TestBootstrapAndOpen(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, ) @@ -154,7 +154,7 @@ func TestBootstrapAndOpen_EpochCommitted(t *testing.T) { all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, ) @@ -664,7 +664,7 @@ func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.S all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index fa7bb88e24a..bf43928f695 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -183,7 +183,7 @@ func SnapshotFromBootstrapStateWithParams( EpochCommitSafetyThreshold: epochCommitSafetyThreshold, // see protocol.Params for details } - rootEpochState := ProtocolStateFromEpochServiceEvents(setup, commit) + rootEpochState := EpochProtocolStateFromServiceEvents(setup, commit) rootEpochStateID := rootEpochState.ID() rootKvStore := kvStoreFactory(rootEpochStateID) if rootKvStore.ID() != root.Payload.ProtocolStateID { @@ -195,7 +195,7 @@ func SnapshotFromBootstrapStateWithParams( return nil, fmt.Errorf("could not encode kvstore: %w", err) } - richRootEpochState, err := flow.NewRichProtocolStateEntry(rootEpochState, nil, nil, setup, commit, nil, nil) + richRootEpochState, err := flow.NewRichEpochProtocolStateEntry(rootEpochState, nil, nil, setup, commit, nil, nil) if err != nil { return nil, fmt.Errorf("could not construct root rich epoch state entry: %w", err) } @@ -227,7 +227,7 @@ func SnapshotFromBootstrapStateWithParams( return snap, nil } -// ProtocolStateFromEpochServiceEvents generates a protocol.ProtocolStateEntry for a root protocol state which is used for bootstrapping. +// EpochProtocolStateFromServiceEvents generates a protocol.EpochProtocolStateEntry for a root protocol state which is used for bootstrapping. // // CONTEXT: The EpochSetup event contains the IdentitySkeletons for each participant, thereby specifying active epoch members. // While ejection status is not part of the EpochSetup event, we can supplement this information as follows: @@ -237,7 +237,7 @@ func SnapshotFromBootstrapStateWithParams( // that happened before should be reflected in the EpochSetup event. Specifically, ejected // nodes should be no longer listed in the EpochSetup event. // Hence, when the EpochSetup event is emitted / processed, the ejected flag is false for all epoch participants. -func ProtocolStateFromEpochServiceEvents(setup *flow.EpochSetup, commit *flow.EpochCommit) *flow.ProtocolStateEntry { +func EpochProtocolStateFromServiceEvents(setup *flow.EpochSetup, commit *flow.EpochCommit) *flow.EpochProtocolStateEntry { identities := make(flow.DynamicIdentityEntryList, 0, len(setup.Participants)) for _, identity := range setup.Participants { identities = append(identities, &flow.DynamicIdentityEntry{ @@ -245,7 +245,7 @@ func ProtocolStateFromEpochServiceEvents(setup *flow.EpochSetup, commit *flow.Ep Ejected: false, }) } - return &flow.ProtocolStateEntry{ + return &flow.EpochProtocolStateEntry{ PreviousEpoch: nil, CurrentEpoch: flow.EpochStateContainer{ SetupID: setup.ID(), diff --git a/state/protocol/inmem/dynamic_protocol_state.go b/state/protocol/inmem/dynamic_protocol_state.go deleted file mode 100644 index a480e3984f2..00000000000 --- a/state/protocol/inmem/dynamic_protocol_state.go +++ /dev/null @@ -1,50 +0,0 @@ -package inmem - -import ( - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol" -) - -// DynamicProtocolStateAdapter implements protocol.DynamicProtocolState by wrapping an InitialProtocolStateAdapter. -type DynamicProtocolStateAdapter struct { - InitialProtocolStateAdapter - params protocol.GlobalParams -} - -var _ protocol.DynamicProtocolState = (*DynamicProtocolStateAdapter)(nil) - -func NewDynamicProtocolStateAdapter(entry *flow.RichProtocolStateEntry, params protocol.GlobalParams) *DynamicProtocolStateAdapter { - return &DynamicProtocolStateAdapter{ - InitialProtocolStateAdapter: InitialProtocolStateAdapter{ - RichProtocolStateEntry: entry, - }, - params: params, - } -} - -func (s *DynamicProtocolStateAdapter) Identities() flow.IdentityList { - return s.RichProtocolStateEntry.CurrentEpochIdentityTable -} - -func (s *DynamicProtocolStateAdapter) GlobalParams() protocol.GlobalParams { - return s.params -} - -// InvalidEpochTransitionAttempted denotes whether an invalid epoch state transition was attempted -// on the fork ending this block. Once the first block where this flag is true is finalized, epoch -// fallback mode is triggered. -// TODO for 'leaving Epoch Fallback via special service event': at the moment, this is a one-way transition and requires a spork to recover - need to revisit for sporkless EFM recovery -func (s *DynamicProtocolStateAdapter) InvalidEpochTransitionAttempted() bool { - return s.ProtocolStateEntry.InvalidEpochTransitionAttempted -} - -// PreviousEpochExists returns true if a previous epoch exists. This is true for all epoch -// except those immediately following a spork. -func (s *DynamicProtocolStateAdapter) PreviousEpochExists() bool { - return s.PreviousEpoch != nil -} - -// EpochPhase returns the epoch phase for the current epoch. -func (s *DynamicProtocolStateAdapter) EpochPhase() flow.EpochPhase { - return s.Entry().EpochPhase() -} diff --git a/state/protocol/inmem/dynamic_protocol_state_test.go b/state/protocol/inmem/dynamic_protocol_state_test.go deleted file mode 100644 index 703e58d96e5..00000000000 --- a/state/protocol/inmem/dynamic_protocol_state_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package inmem_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol/inmem" - "github.com/onflow/flow-go/state/protocol/mock" - "github.com/onflow/flow-go/utils/unittest" -) - -// TestDynamicProtocolStateAdapter tests if the DynamicProtocolStateAdapter returns expected values when created -// using constructor passing a RichProtocolStateEntry. -func TestDynamicProtocolStateAdapter(t *testing.T) { - // construct a valid protocol state entry that has semantically correct DKGParticipantKeys - entry := unittest.EpochStateFixture(unittest.WithValidDKG()) - - globalParams := mock.NewGlobalParams(t) - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - - t.Run("identities", func(t *testing.T) { - assert.Equal(t, entry.CurrentEpochIdentityTable, adapter.Identities()) - }) - t.Run("global-params", func(t *testing.T) { - expectedChainID := flow.Testnet - globalParams.On("ChainID").Return(expectedChainID, nil).Once() - actualChainID := adapter.GlobalParams().ChainID() - assert.Equal(t, expectedChainID, actualChainID) - }) - t.Run("epoch-phase-staking", func(t *testing.T) { - entry := unittest.EpochStateFixture() - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - assert.Equal(t, flow.EpochPhaseStaking, adapter.EpochPhase()) - assert.True(t, adapter.PreviousEpochExists()) - assert.False(t, adapter.InvalidEpochTransitionAttempted()) - }) - t.Run("epoch-phase-setup", func(t *testing.T) { - entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) - // cleanup the commit event, so we are in setup phase - entry.NextEpoch.CommitID = flow.ZeroID - - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - assert.Equal(t, flow.EpochPhaseSetup, adapter.EpochPhase()) - assert.True(t, adapter.PreviousEpochExists()) - assert.False(t, adapter.InvalidEpochTransitionAttempted()) - }) - t.Run("epoch-phase-commit", func(t *testing.T) { - entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - assert.Equal(t, flow.EpochPhaseCommitted, adapter.EpochPhase()) - assert.True(t, adapter.PreviousEpochExists()) - assert.False(t, adapter.InvalidEpochTransitionAttempted()) - }) - t.Run("invalid-state-transition-attempted", func(t *testing.T) { - entry := unittest.EpochStateFixture(func(entry *flow.RichProtocolStateEntry) { - entry.InvalidEpochTransitionAttempted = true - }) - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - assert.True(t, adapter.InvalidEpochTransitionAttempted()) - }) - t.Run("no-previous-epoch", func(t *testing.T) { - entry := unittest.EpochStateFixture(func(entry *flow.RichProtocolStateEntry) { - entry.PreviousEpoch = nil - entry.PreviousEpochSetup = nil - entry.PreviousEpochCommit = nil - }) - adapter := inmem.NewDynamicProtocolStateAdapter(entry, globalParams) - assert.False(t, adapter.PreviousEpochExists()) - }) -} diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 94627b4dc86..f6f4756231d 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -12,9 +12,9 @@ import ( "github.com/onflow/flow-go/state/protocol/invalid" ) -// Epochs provides access to epoch data, backed by a rich protocol state entry. +// Epochs provides access to epoch data, backed by a rich epoch protocol state entry. type Epochs struct { - entry flow.RichProtocolStateEntry + entry flow.RichEpochProtocolStateEntry } var _ protocol.EpochQuery = (*Epochs)(nil) diff --git a/state/protocol/inmem/epoch_protocol_state.go b/state/protocol/inmem/epoch_protocol_state.go new file mode 100644 index 00000000000..150246ab027 --- /dev/null +++ b/state/protocol/inmem/epoch_protocol_state.go @@ -0,0 +1,97 @@ +package inmem + +import ( + "fmt" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" +) + +// EpochProtocolStateAdapter implements protocol.EpochProtocolState by wrapping a flow.RichEpochProtocolStateEntry. +type EpochProtocolStateAdapter struct { + *flow.RichEpochProtocolStateEntry + params protocol.GlobalParams +} + +var _ protocol.EpochProtocolState = (*EpochProtocolStateAdapter)(nil) + +func NewEpochProtocolStateAdapter(entry *flow.RichEpochProtocolStateEntry, params protocol.GlobalParams) *EpochProtocolStateAdapter { + return &EpochProtocolStateAdapter{ + RichEpochProtocolStateEntry: entry, + params: params, + } +} + +// Epoch returns the current epoch counter. +func (s *EpochProtocolStateAdapter) Epoch() uint64 { + return s.CurrentEpochSetup.Counter +} + +// Clustering returns the cluster assignment for the current epoch. +// No errors are expected during normal operations. +func (s *EpochProtocolStateAdapter) Clustering() (flow.ClusterList, error) { + clustering, err := ClusteringFromSetupEvent(s.CurrentEpochSetup) + if err != nil { + return nil, fmt.Errorf("could not extract cluster list from setup event: %w", err) + } + return clustering, nil +} + +// EpochSetup returns the flow.EpochSetup service event which partly defines the +// initial epoch state for the current epoch. +func (s *EpochProtocolStateAdapter) EpochSetup() *flow.EpochSetup { + return s.CurrentEpochSetup +} + +// EpochCommit returns the flow.EpochCommit service event which partly defines the +// initial epoch state for the current epoch. +func (s *EpochProtocolStateAdapter) EpochCommit() *flow.EpochCommit { + return s.CurrentEpochCommit +} + +// DKG returns the DKG information for the current epoch. +// No errors are expected during normal operations. +func (s *EpochProtocolStateAdapter) DKG() (protocol.DKG, error) { + dkg, err := EncodableDKGFromEvents(s.CurrentEpochSetup, s.CurrentEpochCommit) + if err != nil { + return nil, fmt.Errorf("could not construct encodable DKG from events: %w", err) + } + + return NewDKG(dkg), nil +} + +// Entry Returns low-level protocol state entry that was used to initialize this object. +// It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. +// Prefer using other methods to access protocol state. +func (s *EpochProtocolStateAdapter) Entry() *flow.RichEpochProtocolStateEntry { + return s.RichEpochProtocolStateEntry.Copy() +} + +// Identities returns the identity table as of the current block. +func (s *EpochProtocolStateAdapter) Identities() flow.IdentityList { + return s.RichEpochProtocolStateEntry.CurrentEpochIdentityTable +} + +// GlobalParams returns spork-scoped global network parameters. +func (s *EpochProtocolStateAdapter) GlobalParams() protocol.GlobalParams { + return s.params +} + +// InvalidEpochTransitionAttempted denotes whether an invalid epoch state transition was attempted +// on the fork ending this block. Once the first block where this flag is true is finalized, epoch +// fallback mode is triggered. +// TODO for 'leaving Epoch Fallback via special service event': at the moment, this is a one-way transition and requires a spork to recover - need to revisit for sporkless EFM recovery +func (s *EpochProtocolStateAdapter) InvalidEpochTransitionAttempted() bool { + return s.EpochProtocolStateEntry.InvalidEpochTransitionAttempted +} + +// PreviousEpochExists returns true if a previous epoch exists. This is true for all epoch +// except those immediately following a spork. +func (s *EpochProtocolStateAdapter) PreviousEpochExists() bool { + return s.PreviousEpoch != nil +} + +// EpochPhase returns the epoch phase for the current epoch. +func (s *EpochProtocolStateAdapter) EpochPhase() flow.EpochPhase { + return s.Entry().EpochPhase() +} diff --git a/state/protocol/inmem/epoch_protocol_state_test.go b/state/protocol/inmem/epoch_protocol_state_test.go new file mode 100644 index 00000000000..6cfc14752a5 --- /dev/null +++ b/state/protocol/inmem/epoch_protocol_state_test.go @@ -0,0 +1,110 @@ +package inmem_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/model/flow/filter" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol/inmem" + "github.com/onflow/flow-go/state/protocol/mock" + "github.com/onflow/flow-go/utils/unittest" +) + +// TestEpochProtocolStateAdapter tests if the EpochProtocolStateAdapter returns expected values when created +// using constructor passing a RichEpochProtocolStateEntry. +func TestEpochProtocolStateAdapter(t *testing.T) { + // construct a valid protocol state entry that has semantically correct DKGParticipantKeys + entry := unittest.EpochStateFixture(unittest.WithValidDKG()) + + globalParams := mock.NewGlobalParams(t) + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + + t.Run("clustering", func(t *testing.T) { + clustering, err := inmem.ClusteringFromSetupEvent(entry.CurrentEpochSetup) + require.NoError(t, err) + actual, err := adapter.Clustering() + require.NoError(t, err) + assert.Equal(t, clustering, actual) + }) + t.Run("epoch", func(t *testing.T) { + assert.Equal(t, entry.CurrentEpochSetup.Counter, adapter.Epoch()) + }) + t.Run("setup", func(t *testing.T) { + assert.Equal(t, entry.CurrentEpochSetup, adapter.EpochSetup()) + }) + t.Run("commit", func(t *testing.T) { + assert.Equal(t, entry.CurrentEpochCommit, adapter.EpochCommit()) + }) + t.Run("dkg", func(t *testing.T) { + dkg, err := adapter.DKG() + require.NoError(t, err) + assert.Equal(t, entry.CurrentEpochCommit.DKGGroupKey, dkg.GroupKey()) + assert.Equal(t, len(entry.CurrentEpochCommit.DKGParticipantKeys), int(dkg.Size())) + dkgParticipants := entry.CurrentEpochSetup.Participants.Filter(filter.IsValidDKGParticipant) + for _, identity := range dkgParticipants { + keyShare, err := dkg.KeyShare(identity.NodeID) + require.NoError(t, err) + index, err := dkg.Index(identity.NodeID) + require.NoError(t, err) + assert.Equal(t, entry.CurrentEpochCommit.DKGParticipantKeys[index], keyShare) + } + }) + t.Run("entry", func(t *testing.T) { + actualEntry := adapter.Entry() + assert.Equal(t, entry, actualEntry, "entry should be equal to the one passed to the constructor") + assert.NotSame(t, entry, actualEntry, "entry should be a copy of the one passed to the constructor") + }) + t.Run("identities", func(t *testing.T) { + assert.Equal(t, entry.CurrentEpochIdentityTable, adapter.Identities()) + }) + t.Run("global-params", func(t *testing.T) { + expectedChainID := flow.Testnet + globalParams.On("ChainID").Return(expectedChainID, nil).Once() + actualChainID := adapter.GlobalParams().ChainID() + assert.Equal(t, expectedChainID, actualChainID) + }) + t.Run("epoch-phase-staking", func(t *testing.T) { + entry := unittest.EpochStateFixture() + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + assert.Equal(t, flow.EpochPhaseStaking, adapter.EpochPhase()) + assert.True(t, adapter.PreviousEpochExists()) + assert.False(t, adapter.InvalidEpochTransitionAttempted()) + }) + t.Run("epoch-phase-setup", func(t *testing.T) { + entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) + // cleanup the commit event, so we are in setup phase + entry.NextEpoch.CommitID = flow.ZeroID + + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + assert.Equal(t, flow.EpochPhaseSetup, adapter.EpochPhase()) + assert.True(t, adapter.PreviousEpochExists()) + assert.False(t, adapter.InvalidEpochTransitionAttempted()) + }) + t.Run("epoch-phase-commit", func(t *testing.T) { + entry := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + assert.Equal(t, flow.EpochPhaseCommitted, adapter.EpochPhase()) + assert.True(t, adapter.PreviousEpochExists()) + assert.False(t, adapter.InvalidEpochTransitionAttempted()) + }) + t.Run("invalid-state-transition-attempted", func(t *testing.T) { + entry := unittest.EpochStateFixture(func(entry *flow.RichEpochProtocolStateEntry) { + entry.InvalidEpochTransitionAttempted = true + }) + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + assert.True(t, adapter.InvalidEpochTransitionAttempted()) + }) + t.Run("no-previous-epoch", func(t *testing.T) { + entry := unittest.EpochStateFixture(func(entry *flow.RichEpochProtocolStateEntry) { + entry.PreviousEpoch = nil + entry.PreviousEpochSetup = nil + entry.PreviousEpochCommit = nil + }) + adapter := inmem.NewEpochProtocolStateAdapter(entry, globalParams) + assert.False(t, adapter.PreviousEpochExists()) + }) +} diff --git a/state/protocol/inmem/initial_protocol_state.go b/state/protocol/inmem/initial_protocol_state.go deleted file mode 100644 index 1b54f2b47a8..00000000000 --- a/state/protocol/inmem/initial_protocol_state.go +++ /dev/null @@ -1,62 +0,0 @@ -package inmem - -import ( - "fmt" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol" -) - -// InitialProtocolStateAdapter implements protocol.InitialProtocolState by wrapping a RichProtocolStateEntry. -// TODO(yuraolex): for sake of avoiding errors in return values in interface methods this adapter pre-caches -// some values. This is debatable as clustering for instance is not accessed frequently and could be lazily loaded. -// The problem with lazy loading is handling error value from `inmem.ClusteringFromSetupEvent`. There are two ways to avoid it: -// 1. Return error from interface method. -// 2. Inject irrecoverable.Signaler into the adapter and panic on error since any error in that method has to be a severe implementation bug. -type InitialProtocolStateAdapter struct { - *flow.RichProtocolStateEntry -} - -var _ protocol.InitialProtocolState = (*InitialProtocolStateAdapter)(nil) - -func NewInitialProtocolStateAdapter(entry *flow.RichProtocolStateEntry) *InitialProtocolStateAdapter { - return &InitialProtocolStateAdapter{ - RichProtocolStateEntry: entry, - } -} - -func (s *InitialProtocolStateAdapter) Epoch() uint64 { - return s.CurrentEpochSetup.Counter -} - -func (s *InitialProtocolStateAdapter) Clustering() (flow.ClusterList, error) { - clustering, err := ClusteringFromSetupEvent(s.CurrentEpochSetup) - if err != nil { - return nil, fmt.Errorf("could not extract cluster list from setup event: %w", err) - } - return clustering, nil -} - -func (s *InitialProtocolStateAdapter) EpochSetup() *flow.EpochSetup { - return s.CurrentEpochSetup -} - -func (s *InitialProtocolStateAdapter) EpochCommit() *flow.EpochCommit { - return s.CurrentEpochCommit -} - -func (s *InitialProtocolStateAdapter) DKG() (protocol.DKG, error) { - dkg, err := EncodableDKGFromEvents(s.CurrentEpochSetup, s.CurrentEpochCommit) - if err != nil { - return nil, fmt.Errorf("could not construct encodable DKG from events: %w", err) - } - - return NewDKG(dkg), nil -} - -// Entry Returns low-level protocol state entry that was used to initialize this object. -// It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. -// Prefer using other methods to access protocol state. -func (s *InitialProtocolStateAdapter) Entry() *flow.RichProtocolStateEntry { - return s.RichProtocolStateEntry.Copy() -} diff --git a/state/protocol/inmem/initial_protocol_state_test.go b/state/protocol/inmem/initial_protocol_state_test.go deleted file mode 100644 index b84693adb5a..00000000000 --- a/state/protocol/inmem/initial_protocol_state_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package inmem_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/state/protocol/inmem" - "github.com/onflow/flow-go/utils/unittest" -) - -// TestInitialProtocolStateAdapter tests if the InitialProtocolStateAdapter returns expected values when created -// using constructor passing a RichProtocolStateEntry. -func TestInitialProtocolStateAdapter(t *testing.T) { - // construct a valid protocol state entry that has semantically correct DKGParticipantKeys - entry := unittest.EpochStateFixture(unittest.WithValidDKG()) - - adapter := inmem.NewInitialProtocolStateAdapter(entry) - - t.Run("clustering", func(t *testing.T) { - clustering, err := inmem.ClusteringFromSetupEvent(entry.CurrentEpochSetup) - require.NoError(t, err) - actual, err := adapter.Clustering() - require.NoError(t, err) - assert.Equal(t, clustering, actual) - }) - t.Run("epoch", func(t *testing.T) { - assert.Equal(t, entry.CurrentEpochSetup.Counter, adapter.Epoch()) - }) - t.Run("setup", func(t *testing.T) { - assert.Equal(t, entry.CurrentEpochSetup, adapter.EpochSetup()) - }) - t.Run("commit", func(t *testing.T) { - assert.Equal(t, entry.CurrentEpochCommit, adapter.EpochCommit()) - }) - t.Run("dkg", func(t *testing.T) { - dkg, err := adapter.DKG() - require.NoError(t, err) - assert.Equal(t, entry.CurrentEpochCommit.DKGGroupKey, dkg.GroupKey()) - assert.Equal(t, len(entry.CurrentEpochCommit.DKGParticipantKeys), int(dkg.Size())) - dkgParticipants := entry.CurrentEpochSetup.Participants.Filter(filter.IsValidDKGParticipant) - for _, identity := range dkgParticipants { - keyShare, err := dkg.KeyShare(identity.NodeID) - require.NoError(t, err) - index, err := dkg.Index(identity.NodeID) - require.NoError(t, err) - assert.Equal(t, entry.CurrentEpochCommit.DKGParticipantKeys[index], keyShare) - } - }) - t.Run("entry", func(t *testing.T) { - actualEntry := adapter.Entry() - assert.Equal(t, entry, actualEntry, "entry should be equal to the one passed to the constructor") - assert.NotSame(t, entry, actualEntry, "entry should be a copy of the one passed to the constructor") - }) -} diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 3559912b13c..2a667a4d439 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -104,9 +104,9 @@ func (s Snapshot) Encodable() EncodableSnapshot { return s.enc } -func (s Snapshot) EpochProtocolState() (protocol.DynamicProtocolState, error) { +func (s Snapshot) EpochProtocolState() (protocol.EpochProtocolState, error) { entry := s.enc.SealingSegment.LatestProtocolStateEntry() - return NewDynamicProtocolStateAdapter(entry.EpochEntry, s.Params()), nil + return NewEpochProtocolStateAdapter(entry.EpochEntry, s.Params()), nil } func (s Snapshot) ProtocolState() (protocol.KVStoreReader, error) { diff --git a/state/protocol/invalid/snapshot.go b/state/protocol/invalid/snapshot.go index 7f453a763f2..0fff98a06f4 100644 --- a/state/protocol/invalid/snapshot.go +++ b/state/protocol/invalid/snapshot.go @@ -78,7 +78,7 @@ func (u *Snapshot) Params() protocol.GlobalParams { return Params{u.err} } -func (u *Snapshot) EpochProtocolState() (protocol.DynamicProtocolState, error) { +func (u *Snapshot) EpochProtocolState() (protocol.EpochProtocolState, error) { return nil, u.err } diff --git a/state/protocol/mock/dynamic_protocol_state.go b/state/protocol/mock/epoch_protocol_state.go similarity index 70% rename from state/protocol/mock/dynamic_protocol_state.go rename to state/protocol/mock/epoch_protocol_state.go index 11843fe2ecd..bffa1c26b76 100644 --- a/state/protocol/mock/dynamic_protocol_state.go +++ b/state/protocol/mock/epoch_protocol_state.go @@ -9,13 +9,13 @@ import ( protocol "github.com/onflow/flow-go/state/protocol" ) -// DynamicProtocolState is an autogenerated mock type for the DynamicProtocolState type -type DynamicProtocolState struct { +// EpochProtocolState is an autogenerated mock type for the EpochProtocolState type +type EpochProtocolState struct { mock.Mock } // Clustering provides a mock function with given fields: -func (_m *DynamicProtocolState) Clustering() (flow.ClusterList, error) { +func (_m *EpochProtocolState) Clustering() (flow.ClusterList, error) { ret := _m.Called() var r0 flow.ClusterList @@ -41,7 +41,7 @@ func (_m *DynamicProtocolState) Clustering() (flow.ClusterList, error) { } // DKG provides a mock function with given fields: -func (_m *DynamicProtocolState) DKG() (protocol.DKG, error) { +func (_m *EpochProtocolState) DKG() (protocol.DKG, error) { ret := _m.Called() var r0 protocol.DKG @@ -67,15 +67,15 @@ func (_m *DynamicProtocolState) DKG() (protocol.DKG, error) { } // Entry provides a mock function with given fields: -func (_m *DynamicProtocolState) Entry() *flow.RichProtocolStateEntry { +func (_m *EpochProtocolState) Entry() *flow.RichEpochProtocolStateEntry { ret := _m.Called() - var r0 *flow.RichProtocolStateEntry - if rf, ok := ret.Get(0).(func() *flow.RichProtocolStateEntry); ok { + var r0 *flow.RichEpochProtocolStateEntry + if rf, ok := ret.Get(0).(func() *flow.RichEpochProtocolStateEntry); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.RichProtocolStateEntry) + r0 = ret.Get(0).(*flow.RichEpochProtocolStateEntry) } } @@ -83,7 +83,7 @@ func (_m *DynamicProtocolState) Entry() *flow.RichProtocolStateEntry { } // Epoch provides a mock function with given fields: -func (_m *DynamicProtocolState) Epoch() uint64 { +func (_m *EpochProtocolState) Epoch() uint64 { ret := _m.Called() var r0 uint64 @@ -97,7 +97,7 @@ func (_m *DynamicProtocolState) Epoch() uint64 { } // EpochCommit provides a mock function with given fields: -func (_m *DynamicProtocolState) EpochCommit() *flow.EpochCommit { +func (_m *EpochProtocolState) EpochCommit() *flow.EpochCommit { ret := _m.Called() var r0 *flow.EpochCommit @@ -113,7 +113,7 @@ func (_m *DynamicProtocolState) EpochCommit() *flow.EpochCommit { } // EpochPhase provides a mock function with given fields: -func (_m *DynamicProtocolState) EpochPhase() flow.EpochPhase { +func (_m *EpochProtocolState) EpochPhase() flow.EpochPhase { ret := _m.Called() var r0 flow.EpochPhase @@ -127,7 +127,7 @@ func (_m *DynamicProtocolState) EpochPhase() flow.EpochPhase { } // EpochSetup provides a mock function with given fields: -func (_m *DynamicProtocolState) EpochSetup() *flow.EpochSetup { +func (_m *EpochProtocolState) EpochSetup() *flow.EpochSetup { ret := _m.Called() var r0 *flow.EpochSetup @@ -143,7 +143,7 @@ func (_m *DynamicProtocolState) EpochSetup() *flow.EpochSetup { } // GlobalParams provides a mock function with given fields: -func (_m *DynamicProtocolState) GlobalParams() protocol.GlobalParams { +func (_m *EpochProtocolState) GlobalParams() protocol.GlobalParams { ret := _m.Called() var r0 protocol.GlobalParams @@ -159,7 +159,7 @@ func (_m *DynamicProtocolState) GlobalParams() protocol.GlobalParams { } // Identities provides a mock function with given fields: -func (_m *DynamicProtocolState) Identities() flow.GenericIdentityList[flow.Identity] { +func (_m *EpochProtocolState) Identities() flow.GenericIdentityList[flow.Identity] { ret := _m.Called() var r0 flow.GenericIdentityList[flow.Identity] @@ -175,7 +175,7 @@ func (_m *DynamicProtocolState) Identities() flow.GenericIdentityList[flow.Ident } // InvalidEpochTransitionAttempted provides a mock function with given fields: -func (_m *DynamicProtocolState) InvalidEpochTransitionAttempted() bool { +func (_m *EpochProtocolState) InvalidEpochTransitionAttempted() bool { ret := _m.Called() var r0 bool @@ -189,7 +189,7 @@ func (_m *DynamicProtocolState) InvalidEpochTransitionAttempted() bool { } // PreviousEpochExists provides a mock function with given fields: -func (_m *DynamicProtocolState) PreviousEpochExists() bool { +func (_m *EpochProtocolState) PreviousEpochExists() bool { ret := _m.Called() var r0 bool @@ -202,14 +202,14 @@ func (_m *DynamicProtocolState) PreviousEpochExists() bool { return r0 } -type mockConstructorTestingTNewDynamicProtocolState interface { +type mockConstructorTestingTNewEpochProtocolState interface { mock.TestingT Cleanup(func()) } -// NewDynamicProtocolState creates a new instance of DynamicProtocolState. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDynamicProtocolState(t mockConstructorTestingTNewDynamicProtocolState) *DynamicProtocolState { - mock := &DynamicProtocolState{} +// NewEpochProtocolState creates a new instance of EpochProtocolState. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEpochProtocolState(t mockConstructorTestingTNewEpochProtocolState) *EpochProtocolState { + mock := &EpochProtocolState{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/state/protocol/mock/initial_protocol_state.go b/state/protocol/mock/initial_protocol_state.go deleted file mode 100644 index 7d667ae11ed..00000000000 --- a/state/protocol/mock/initial_protocol_state.go +++ /dev/null @@ -1,144 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mock - -import ( - flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" - - protocol "github.com/onflow/flow-go/state/protocol" -) - -// InitialProtocolState is an autogenerated mock type for the InitialProtocolState type -type InitialProtocolState struct { - mock.Mock -} - -// Clustering provides a mock function with given fields: -func (_m *InitialProtocolState) Clustering() (flow.ClusterList, error) { - ret := _m.Called() - - var r0 flow.ClusterList - var r1 error - if rf, ok := ret.Get(0).(func() (flow.ClusterList, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() flow.ClusterList); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(flow.ClusterList) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DKG provides a mock function with given fields: -func (_m *InitialProtocolState) DKG() (protocol.DKG, error) { - ret := _m.Called() - - var r0 protocol.DKG - var r1 error - if rf, ok := ret.Get(0).(func() (protocol.DKG, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() protocol.DKG); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(protocol.DKG) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Entry provides a mock function with given fields: -func (_m *InitialProtocolState) Entry() *flow.RichProtocolStateEntry { - ret := _m.Called() - - var r0 *flow.RichProtocolStateEntry - if rf, ok := ret.Get(0).(func() *flow.RichProtocolStateEntry); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.RichProtocolStateEntry) - } - } - - return r0 -} - -// Epoch provides a mock function with given fields: -func (_m *InitialProtocolState) Epoch() uint64 { - ret := _m.Called() - - var r0 uint64 - if rf, ok := ret.Get(0).(func() uint64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint64) - } - - return r0 -} - -// EpochCommit provides a mock function with given fields: -func (_m *InitialProtocolState) EpochCommit() *flow.EpochCommit { - ret := _m.Called() - - var r0 *flow.EpochCommit - if rf, ok := ret.Get(0).(func() *flow.EpochCommit); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.EpochCommit) - } - } - - return r0 -} - -// EpochSetup provides a mock function with given fields: -func (_m *InitialProtocolState) EpochSetup() *flow.EpochSetup { - ret := _m.Called() - - var r0 *flow.EpochSetup - if rf, ok := ret.Get(0).(func() *flow.EpochSetup); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.EpochSetup) - } - } - - return r0 -} - -type mockConstructorTestingTNewInitialProtocolState interface { - mock.TestingT - Cleanup(func()) -} - -// NewInitialProtocolState creates a new instance of InitialProtocolState. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewInitialProtocolState(t mockConstructorTestingTNewInitialProtocolState) *InitialProtocolState { - mock := &InitialProtocolState{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/state/protocol/mock/mutable_protocol_state.go b/state/protocol/mock/mutable_protocol_state.go index 3c9d2f8aa6d..dd240ad0f30 100644 --- a/state/protocol/mock/mutable_protocol_state.go +++ b/state/protocol/mock/mutable_protocol_state.go @@ -16,20 +16,20 @@ type MutableProtocolState struct { mock.Mock } -// AtBlockID provides a mock function with given fields: blockID -func (_m *MutableProtocolState) AtBlockID(blockID flow.Identifier) (protocol.DynamicProtocolState, error) { +// EpochStateAtBlockID provides a mock function with given fields: blockID +func (_m *MutableProtocolState) EpochStateAtBlockID(blockID flow.Identifier) (protocol.EpochProtocolState, error) { ret := _m.Called(blockID) - var r0 protocol.DynamicProtocolState + var r0 protocol.EpochProtocolState var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) (protocol.DynamicProtocolState, error)); ok { + if rf, ok := ret.Get(0).(func(flow.Identifier) (protocol.EpochProtocolState, error)); ok { return rf(blockID) } - if rf, ok := ret.Get(0).(func(flow.Identifier) protocol.DynamicProtocolState); ok { + if rf, ok := ret.Get(0).(func(flow.Identifier) protocol.EpochProtocolState); ok { r0 = rf(blockID) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(protocol.DynamicProtocolState) + r0 = ret.Get(0).(protocol.EpochProtocolState) } } diff --git a/state/protocol/mock/protocol_state.go b/state/protocol/mock/protocol_state.go index fc167663027..65b75ccd00f 100644 --- a/state/protocol/mock/protocol_state.go +++ b/state/protocol/mock/protocol_state.go @@ -14,20 +14,20 @@ type ProtocolState struct { mock.Mock } -// AtBlockID provides a mock function with given fields: blockID -func (_m *ProtocolState) AtBlockID(blockID flow.Identifier) (protocol.DynamicProtocolState, error) { +// EpochStateAtBlockID provides a mock function with given fields: blockID +func (_m *ProtocolState) EpochStateAtBlockID(blockID flow.Identifier) (protocol.EpochProtocolState, error) { ret := _m.Called(blockID) - var r0 protocol.DynamicProtocolState + var r0 protocol.EpochProtocolState var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) (protocol.DynamicProtocolState, error)); ok { + if rf, ok := ret.Get(0).(func(flow.Identifier) (protocol.EpochProtocolState, error)); ok { return rf(blockID) } - if rf, ok := ret.Get(0).(func(flow.Identifier) protocol.DynamicProtocolState); ok { + if rf, ok := ret.Get(0).(func(flow.Identifier) protocol.EpochProtocolState); ok { r0 = rf(blockID) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(protocol.DynamicProtocolState) + r0 = ret.Get(0).(protocol.EpochProtocolState) } } diff --git a/state/protocol/mock/snapshot.go b/state/protocol/mock/snapshot.go index 304bc491777..f56505c3236 100644 --- a/state/protocol/mock/snapshot.go +++ b/state/protocol/mock/snapshot.go @@ -67,19 +67,19 @@ func (_m *Snapshot) Descendants() ([]flow.Identifier, error) { } // EpochProtocolState provides a mock function with given fields: -func (_m *Snapshot) EpochProtocolState() (protocol.DynamicProtocolState, error) { +func (_m *Snapshot) EpochProtocolState() (protocol.EpochProtocolState, error) { ret := _m.Called() - var r0 protocol.DynamicProtocolState + var r0 protocol.EpochProtocolState var r1 error - if rf, ok := ret.Get(0).(func() (protocol.DynamicProtocolState, error)); ok { + if rf, ok := ret.Get(0).(func() (protocol.EpochProtocolState, error)); ok { return rf() } - if rf, ok := ret.Get(0).(func() protocol.DynamicProtocolState); ok { + if rf, ok := ret.Get(0).(func() protocol.EpochProtocolState); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(protocol.DynamicProtocolState) + r0 = ret.Get(0).(protocol.EpochProtocolState) } } diff --git a/state/protocol/protocol_state.go b/state/protocol/protocol_state.go index c339958e725..2dbbb457ab5 100644 --- a/state/protocol/protocol_state.go +++ b/state/protocol/protocol_state.go @@ -5,79 +5,86 @@ import ( "github.com/onflow/flow-go/storage/badger/transaction" ) -// InitialProtocolState returns constant data for given epoch. -// This interface can be only obtained for epochs that have progressed to epoch commit event. -type InitialProtocolState interface { - // Epoch returns counter of epoch. +// EpochProtocolState represents the subset of the Protocol State KVStore related to epochs: +// the Identity Table, DKG, cluster assignment, etc. +// EpochProtocolState is fork-aware and can change on a block-by-block basis. +// Each EpochProtocolState instance refers to the state with respect to some reference block. +type EpochProtocolState interface { + // Epoch returns the current epoch counter. Epoch() uint64 + // Clustering returns initial clustering from epoch setup. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. Clustering() (flow.ClusterList, error) + // EpochSetup returns original epoch setup event that was used to initialize the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochSetup() *flow.EpochSetup + // EpochCommit returns original epoch commit event that was used to update the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochCommit() *flow.EpochCommit + // DKG returns information about DKG that was obtained from EpochCommit event. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. DKG() (DKG, error) - // Entry Returns low-level protocol state entry that was used to initialize this object. - // It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. - // Prefer using other methods to access protocol state. - Entry() *flow.RichProtocolStateEntry -} - -// DynamicProtocolState extends the InitialProtocolState with data that can change from block to block. -// It can be used to access the identity table at given block. -type DynamicProtocolState interface { - InitialProtocolState // InvalidEpochTransitionAttempted denotes whether an invalid epoch state transition was attempted // on the fork ending this block. Once the first block where this flag is true is finalized, epoch // fallback mode is triggered. // TODO for 'leaving Epoch Fallback via special service event': at the moment, this is a one-way transition and requires a spork to recover - need to revisit for sporkless EFM recovery InvalidEpochTransitionAttempted() bool + // PreviousEpochExists returns true if a previous epoch exists. This is true for all epoch // except those immediately following a spork. PreviousEpochExists() bool + // EpochPhase returns the epoch phase for the current epoch. EpochPhase() flow.EpochPhase - // Identities returns identities (in canonical ordering) that can participate in the current or previous - // or next epochs. Let P be the set of identities in the previous epoch, C be the set of identities in - // the current epoch, and N be the set of identities in the next epoch. + // Identities returns identities (in canonical ordering) that can participate in the current or + // previous or next epochs. Let P be the set of identities in the previous epoch, C be the set + // of identities in the current epoch, and S be the set of identities in the subsequent epoch. + // Let `\` denote the relative set complement (also called 'set difference'). // The set of authorized identities this function returns is different depending on epoch state: // EpochStaking phase: // - nodes in C with status `flow.EpochParticipationStatusActive` - // - nodes in P-C with status `flow.EpochParticipationStatusLeaving` + // - nodes in P\C with status `flow.EpochParticipationStatusLeaving` // EpochSetup/EpochCommitted phase: // - nodes in C with status `flow.EpochParticipationStatusActive` - // - nodes in N-C with status `flow.EpochParticipationStatusJoining` + // - nodes in S\C with status `flow.EpochParticipationStatusJoining` Identities() flow.IdentityList - // GlobalParams returns params that are same for all nodes in the network. + + // GlobalParams returns global, static network params that are same for all nodes in the network. GlobalParams() GlobalParams + + // Entry returns low-level protocol state entry that was used to initialize this object. + // It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. + // Prefer using other methods to access protocol state. + Entry() *flow.RichEpochProtocolStateEntry } -// ProtocolState is the read-only interface for protocol state, it allows to query information -// on a per-block and per-epoch basis. +// ProtocolState is the read-only interface for protocol state. It allows querying the +// Protocol KVStore or Epoch sub-state by block, and retrieving global network params. type ProtocolState interface { - // ByEpoch returns an object with static protocol state information by epoch number. - // To be able to use this interface we need to observe both epoch setup and commit events. - // Not available for next epoch unless we have observed an EpochCommit event. - // No errors are expected during normal operations. - // TODO(yuraolex): check return types - // TODO(yuraolex): decide if we really need this approach. It's unclear if it's useful to query - // by epoch counter. To implement it we need an additional index by epoch counter. Alternatively we need a way to map - // epoch counter -> block ID. It gets worse if we consider that we need a way to get the epoch counter itself at caller side. - //ByEpoch(epoch uint64) (InitialProtocolState, error) - - // AtBlockID returns epoch protocol state at block ID. + // EpochStateAtBlockID returns epoch protocol state at block ID. // The resulting epoch protocol state is returned AFTER applying updates that are contained in block. // Can be queried for any block that has been added to the block tree. // Returns: - // - (DynamicProtocolState, nil) - if there is an epoch protocol state associated with given block ID. + // - (EpochProtocolState, nil) - if there is an epoch protocol state associated with given block ID. // - (nil, storage.ErrNotFound) - if there is no epoch protocol state associated with given block ID. // - (nil, exception) - any other error should be treated as exception. - AtBlockID(blockID flow.Identifier) (DynamicProtocolState, error) + EpochStateAtBlockID(blockID flow.Identifier) (EpochProtocolState, error) // KVStoreAtBlockID returns protocol state at block ID. // The resulting protocol state is returned AFTER applying updates that are contained in block. diff --git a/state/protocol/protocol_state/epochs/base_statemachine.go b/state/protocol/protocol_state/epochs/base_statemachine.go index 6b61f784ddd..6e525adf277 100644 --- a/state/protocol/protocol_state/epochs/base_statemachine.go +++ b/state/protocol/protocol_state/epochs/base_statemachine.go @@ -8,8 +8,8 @@ import ( // baseStateMachine implements common logic for evolving protocol state both in happy path and epoch fallback // operation modes. It partially implements `StateMachine` and is used as building block for more complex implementations. type baseStateMachine struct { - parentState *flow.RichProtocolStateEntry - state *flow.ProtocolStateEntry + parentState *flow.RichEpochProtocolStateEntry + state *flow.EpochProtocolStateEntry view uint64 // The following fields are maps from NodeID → DynamicIdentityEntry for the nodes that are *active* in the respective epoch. @@ -27,7 +27,7 @@ type baseStateMachine struct { // Do NOT call Build, if the baseStateMachine instance has returned a `protocol.InvalidServiceEventError` // at any time during its lifetime. After this error, the baseStateMachine is left with a potentially // dysfunctional state and should be discarded. -func (u *baseStateMachine) Build() (updatedState *flow.ProtocolStateEntry, stateID flow.Identifier, hasChanges bool) { +func (u *baseStateMachine) Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, hasChanges bool) { updatedState = u.state.Copy() stateID = updatedState.ID() hasChanges = stateID != u.parentState.ID() @@ -41,7 +41,7 @@ func (u *baseStateMachine) View() uint64 { } // ParentState returns parent protocol state associated with this state machine. -func (u *baseStateMachine) ParentState() *flow.RichProtocolStateEntry { +func (u *baseStateMachine) ParentState() *flow.RichEpochProtocolStateEntry { return u.parentState } diff --git a/state/protocol/protocol_state/epochs/factory.go b/state/protocol/protocol_state/epochs/factory.go index 00af747e672..4abce702adb 100644 --- a/state/protocol/protocol_state/epochs/factory.go +++ b/state/protocol/protocol_state/epochs/factory.go @@ -13,7 +13,7 @@ type EpochStateMachineFactory struct { params protocol.GlobalParams setups storage.EpochSetups commits storage.EpochCommits - epochProtocolStateDB storage.ProtocolState + epochProtocolStateDB storage.EpochProtocolStateEntries } var _ protocol_state.KeyValueStoreStateMachineFactory = (*EpochStateMachineFactory)(nil) @@ -22,7 +22,7 @@ func NewEpochStateMachineFactory( params protocol.GlobalParams, setups storage.EpochSetups, commits storage.EpochCommits, - epochProtocolStateDB storage.ProtocolState, + epochProtocolStateDB storage.EpochProtocolStateEntries, ) *EpochStateMachineFactory { return &EpochStateMachineFactory{ params: params, @@ -44,10 +44,10 @@ func (f *EpochStateMachineFactory) Create(candidateView uint64, parentBlockID fl f.epochProtocolStateDB, parentState, mutator, - func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { return NewHappyPathStateMachine(candidateView, parentState) }, - func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { return NewFallbackStateMachine(candidateView, parentState), nil }, ) diff --git a/state/protocol/protocol_state/epochs/fallback_statemachine.go b/state/protocol/protocol_state/epochs/fallback_statemachine.go index e9162297bfe..e8719615c57 100644 --- a/state/protocol/protocol_state/epochs/fallback_statemachine.go +++ b/state/protocol/protocol_state/epochs/fallback_statemachine.go @@ -18,8 +18,8 @@ var _ StateMachine = (*FallbackStateMachine)(nil) // NewFallbackStateMachine constructs a state machine for epoch fallback, it automatically sets // InvalidEpochTransitionAttempted to true, thereby recording that we have entered epoch fallback mode. -func NewFallbackStateMachine(view uint64, parentState *flow.RichProtocolStateEntry) *FallbackStateMachine { - state := parentState.ProtocolStateEntry.Copy() +func NewFallbackStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) *FallbackStateMachine { + state := parentState.EpochProtocolStateEntry.Copy() state.InvalidEpochTransitionAttempted = true return &FallbackStateMachine{ baseStateMachine: baseStateMachine{ diff --git a/state/protocol/protocol_state/epochs/happy_path_statemachine.go b/state/protocol/protocol_state/epochs/happy_path_statemachine.go index 8bbc38e3076..fdd2518338c 100644 --- a/state/protocol/protocol_state/epochs/happy_path_statemachine.go +++ b/state/protocol/protocol_state/epochs/happy_path_statemachine.go @@ -30,7 +30,7 @@ var _ StateMachine = (*HappyPathStateMachine)(nil) // NewHappyPathStateMachine creates a new HappyPathStateMachine. // An exception is returned in case the `InvalidEpochTransitionAttempted` flag is set in the `parentState`. This means that // the protocol state evolution has reached an undefined state from the perspective of the happy path state machine. -func NewHappyPathStateMachine(view uint64, parentState *flow.RichProtocolStateEntry) (*HappyPathStateMachine, error) { +func NewHappyPathStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) (*HappyPathStateMachine, error) { if parentState.InvalidEpochTransitionAttempted { return nil, irrecoverable.NewExceptionf("cannot create happy path protocol state machine at view (%d) for a parent state"+ "which is in Epoch Fallback Mode", view) @@ -38,7 +38,7 @@ func NewHappyPathStateMachine(view uint64, parentState *flow.RichProtocolStateEn return &HappyPathStateMachine{ baseStateMachine: baseStateMachine{ parentState: parentState, - state: parentState.ProtocolStateEntry.Copy(), + state: parentState.EpochProtocolStateEntry.Copy(), view: view, }, }, nil @@ -57,7 +57,7 @@ func NewHappyPathStateMachine(view uint64, parentState *flow.RichProtocolStateEn // CAUTION: the HappyPathStateMachine is left with a potentially dysfunctional state when this error occurs. Do NOT call the Build method // after such error and discard the HappyPathStateMachine! func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error) { - err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState.ProtocolStateEntry, u.parentState.CurrentEpochSetup) + err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState.EpochProtocolStateEntry, u.parentState.CurrentEpochSetup) if err != nil { return false, fmt.Errorf("invalid epoch setup event: %w", err) } @@ -65,7 +65,7 @@ func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) ( return false, protocol.NewInvalidServiceEventErrorf("repeated setup for epoch %d", epochSetup.Counter) } - // When observing setup event for subsequent epoch, construct the EpochStateContainer for `ProtocolStateEntry.NextEpoch`. + // When observing setup event for subsequent epoch, construct the EpochStateContainer for `EpochProtocolStateEntry.NextEpoch`. // Context: // Note that the `EpochStateContainer.ActiveIdentities` only contains the nodes that are *active* in the next epoch. Active means // that these nodes are authorized to contribute to extending the chain. Nodes are listed in `ActiveIdentities` if and only if @@ -144,7 +144,7 @@ func (u *HappyPathStateMachine) ProcessEpochCommit(epochCommit *flow.EpochCommit if u.state.NextEpoch.CommitID != flow.ZeroID { return false, protocol.NewInvalidServiceEventErrorf("protocol state has already a commit event") } - err := protocol.IsValidExtendingEpochCommit(epochCommit, u.parentState.ProtocolStateEntry, u.parentState.NextEpochSetup) + err := protocol.IsValidExtendingEpochCommit(epochCommit, u.parentState.EpochProtocolStateEntry, u.parentState.NextEpochSetup) if err != nil { return false, fmt.Errorf("invalid epoch commit event: %w", err) } @@ -174,7 +174,7 @@ func (u *HappyPathStateMachine) TransitionToNextEpoch() error { if u.view < u.parentState.NextEpochSetup.FirstView { return fmt.Errorf("protocol state transition is only allowed when enterring next epoch") } - u.state = &flow.ProtocolStateEntry{ + u.state = &flow.EpochProtocolStateEntry{ PreviousEpoch: &u.state.CurrentEpoch, CurrentEpoch: *u.state.NextEpoch, InvalidEpochTransitionAttempted: false, diff --git a/state/protocol/protocol_state/epochs/happy_path_statemachine_test.go b/state/protocol/protocol_state/epochs/happy_path_statemachine_test.go index ac14f9d6b03..4d169fae071 100644 --- a/state/protocol/protocol_state/epochs/happy_path_statemachine_test.go +++ b/state/protocol/protocol_state/epochs/happy_path_statemachine_test.go @@ -21,7 +21,7 @@ func TestProtocolStateMachine(t *testing.T) { type BaseStateMachineSuite struct { suite.Suite - parentProtocolState *flow.RichProtocolStateEntry + parentProtocolState *flow.RichEpochProtocolStateEntry parentBlock *flow.Header candidate *flow.Header } @@ -89,7 +89,7 @@ func (s *ProtocolStateMachineSuite) TestTransitionToNextEpochNotAllowed() { require.Error(s.T(), err, "should not allow transition to next epoch if there is no next epoch protocol state") }) s.Run("next epoch not committed", func() { - protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { + protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichEpochProtocolStateEntry) { entry.NextEpoch.CommitID = flow.ZeroID entry.NextEpochCommit = nil }) @@ -178,7 +178,7 @@ func (s *ProtocolStateMachineSuite) TestProcessEpochCommit() { updatedState, _, _ := s.stateMachine.Build() - parentState, err := flow.NewRichProtocolStateEntry(updatedState, + parentState, err := flow.NewRichEpochProtocolStateEntry(updatedState, s.parentProtocolState.PreviousEpochSetup, s.parentProtocolState.PreviousEpochCommit, s.parentProtocolState.CurrentEpochSetup, @@ -223,7 +223,7 @@ func (s *ProtocolStateMachineSuite) TestProcessEpochCommit() { require.Equal(s.T(), updatedState.ID(), stateID) require.Equal(s.T(), s.parentProtocolState.ID(), s.stateMachine.ParentState().ID(), "should not modify parent protocol state") - parentState, err := flow.NewRichProtocolStateEntry(updatedState, + parentState, err := flow.NewRichEpochProtocolStateEntry(updatedState, s.parentProtocolState.PreviousEpochSetup, s.parentProtocolState.PreviousEpochCommit, s.parentProtocolState.CurrentEpochSetup, @@ -347,7 +347,7 @@ func (s *ProtocolStateMachineSuite) TestProcessEpochSetupInvariants() { require.True(s.T(), protocol.IsInvalidServiceEventError(err)) }) s.Run("epoch setup state conflicts with protocol state", func() { - conflictingIdentity := s.parentProtocolState.ProtocolStateEntry.CurrentEpoch.ActiveIdentities[0] + conflictingIdentity := s.parentProtocolState.EpochProtocolStateEntry.CurrentEpoch.ActiveIdentities[0] conflictingIdentity.Ejected = true stateMachine, err := NewHappyPathStateMachine(s.candidate.View, s.parentProtocolState.Copy()) @@ -454,10 +454,10 @@ func (s *ProtocolStateMachineSuite) TestEpochSetupAfterIdentityChange() { } updatedState, _, _ := s.stateMachine.Build() - // Construct a valid flow.RichProtocolStateEntry for next block + // Construct a valid flow.RichEpochProtocolStateEntry for next block // We do this by copying the parent protocol state and updating the identities manually - updatedRichProtocolState := &flow.RichProtocolStateEntry{ - ProtocolStateEntry: updatedState, + updatedRichProtocolState := &flow.RichEpochProtocolStateEntry{ + EpochProtocolStateEntry: updatedState, PreviousEpochSetup: s.parentProtocolState.PreviousEpochSetup, PreviousEpochCommit: s.parentProtocolState.PreviousEpochCommit, CurrentEpochSetup: s.parentProtocolState.CurrentEpochSetup, diff --git a/state/protocol/protocol_state/epochs/mock/state_machine.go b/state/protocol/protocol_state/epochs/mock/state_machine.go index d518b50e831..5020829d141 100644 --- a/state/protocol/protocol_state/epochs/mock/state_machine.go +++ b/state/protocol/protocol_state/epochs/mock/state_machine.go @@ -13,20 +13,20 @@ type StateMachine struct { } // Build provides a mock function with given fields: -func (_m *StateMachine) Build() (*flow.ProtocolStateEntry, flow.Identifier, bool) { +func (_m *StateMachine) Build() (*flow.EpochProtocolStateEntry, flow.Identifier, bool) { ret := _m.Called() - var r0 *flow.ProtocolStateEntry + var r0 *flow.EpochProtocolStateEntry var r1 flow.Identifier var r2 bool - if rf, ok := ret.Get(0).(func() (*flow.ProtocolStateEntry, flow.Identifier, bool)); ok { + if rf, ok := ret.Get(0).(func() (*flow.EpochProtocolStateEntry, flow.Identifier, bool)); ok { return rf() } - if rf, ok := ret.Get(0).(func() *flow.ProtocolStateEntry); ok { + if rf, ok := ret.Get(0).(func() *flow.EpochProtocolStateEntry); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.ProtocolStateEntry) + r0 = ret.Get(0).(*flow.EpochProtocolStateEntry) } } @@ -62,15 +62,15 @@ func (_m *StateMachine) EjectIdentity(nodeID flow.Identifier) error { } // ParentState provides a mock function with given fields: -func (_m *StateMachine) ParentState() *flow.RichProtocolStateEntry { +func (_m *StateMachine) ParentState() *flow.RichEpochProtocolStateEntry { ret := _m.Called() - var r0 *flow.RichProtocolStateEntry - if rf, ok := ret.Get(0).(func() *flow.RichProtocolStateEntry); ok { + var r0 *flow.RichEpochProtocolStateEntry + if rf, ok := ret.Get(0).(func() *flow.RichEpochProtocolStateEntry); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.RichProtocolStateEntry) + r0 = ret.Get(0).(*flow.RichEpochProtocolStateEntry) } } diff --git a/state/protocol/protocol_state/epochs/mock/state_machine_factory_method.go b/state/protocol/protocol_state/epochs/mock/state_machine_factory_method.go index e0edd388a92..a5dbbf1906b 100644 --- a/state/protocol/protocol_state/epochs/mock/state_machine_factory_method.go +++ b/state/protocol/protocol_state/epochs/mock/state_machine_factory_method.go @@ -15,15 +15,15 @@ type StateMachineFactoryMethod struct { } // Execute provides a mock function with given fields: candidateView, parentState -func (_m *StateMachineFactoryMethod) Execute(candidateView uint64, parentState *flow.RichProtocolStateEntry) (epochs.StateMachine, error) { +func (_m *StateMachineFactoryMethod) Execute(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (epochs.StateMachine, error) { ret := _m.Called(candidateView, parentState) var r0 epochs.StateMachine var r1 error - if rf, ok := ret.Get(0).(func(uint64, *flow.RichProtocolStateEntry) (epochs.StateMachine, error)); ok { + if rf, ok := ret.Get(0).(func(uint64, *flow.RichEpochProtocolStateEntry) (epochs.StateMachine, error)); ok { return rf(candidateView, parentState) } - if rf, ok := ret.Get(0).(func(uint64, *flow.RichProtocolStateEntry) epochs.StateMachine); ok { + if rf, ok := ret.Get(0).(func(uint64, *flow.RichEpochProtocolStateEntry) epochs.StateMachine); ok { r0 = rf(candidateView, parentState) } else { if ret.Get(0) != nil { @@ -31,7 +31,7 @@ func (_m *StateMachineFactoryMethod) Execute(candidateView uint64, parentState * } } - if rf, ok := ret.Get(1).(func(uint64, *flow.RichProtocolStateEntry) error); ok { + if rf, ok := ret.Get(1).(func(uint64, *flow.RichEpochProtocolStateEntry) error); ok { r1 = rf(candidateView, parentState) } else { r1 = ret.Error(1) diff --git a/state/protocol/protocol_state/epochs/statemachine.go b/state/protocol/protocol_state/epochs/statemachine.go index 8f5b73ab1a5..a35e50c2d37 100644 --- a/state/protocol/protocol_state/epochs/statemachine.go +++ b/state/protocol/protocol_state/epochs/statemachine.go @@ -26,7 +26,7 @@ type StateMachine interface { // Do NOT call Build, if the StateMachine instance has returned a `protocol.InvalidServiceEventError` // at any time during its lifetime. After this error, the StateMachine is left with a potentially // dysfunctional state and should be discarded. - Build() (updatedState *flow.ProtocolStateEntry, stateID flow.Identifier, hasChanges bool) + Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, hasChanges bool) // ProcessEpochSetup updates the internally-maintained interim Epoch state with data from epoch setup event. // Processing an epoch setup event also affects the identity table for the current epoch. @@ -71,13 +71,13 @@ type StateMachine interface { View() uint64 // ParentState returns parent protocol state associated with this state machine. - ParentState() *flow.RichProtocolStateEntry + ParentState() *flow.RichEpochProtocolStateEntry } // StateMachineFactoryMethod is a factory method to create state machines for evolving the protocol's epoch state. // Currently, we have `HappyPathStateMachine` and `FallbackStateMachine` as StateMachine // implementations, whose constructors both have the same signature as StateMachineFactoryMethod. -type StateMachineFactoryMethod func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) +type StateMachineFactoryMethod func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) // EpochStateMachine is a hierarchical state machine that encapsulates the logic for protocol-compliant evolution of Epoch-related sub-state. // EpochStateMachine processes a subset of service events that are relevant for the Epoch state, and ignores all other events. @@ -92,7 +92,7 @@ type EpochStateMachine struct { setups storage.EpochSetups commits storage.EpochCommits - epochProtocolStateDB storage.ProtocolState + epochProtocolStateDB storage.EpochProtocolStateEntries pendingDbUpdates *transaction.DeferredBlockPersist } @@ -109,7 +109,7 @@ func NewEpochStateMachine( params protocol.GlobalParams, setups storage.EpochSetups, commits storage.EpochCommits, - epochProtocolStateDB storage.ProtocolState, + epochProtocolStateDB storage.EpochProtocolStateEntries, parentState protocol.KVStoreReader, mutator protocol_state.KVStoreMutator, happyPathStateMachineFactory StateMachineFactoryMethod, @@ -302,7 +302,7 @@ func (e *EpochStateMachine) transitionToEpochFallbackMode(orderedUpdates []flow. // - The seal for block A was included in some block C, s.t C is an ancestor of B. // // For further details see `params.EpochCommitSafetyThreshold()`. -func epochFallbackTriggeredByIncorporatingCandidate(candidateView uint64, params protocol.GlobalParams, parentState *flow.RichProtocolStateEntry) bool { +func epochFallbackTriggeredByIncorporatingCandidate(candidateView uint64, params protocol.GlobalParams, parentState *flow.RichEpochProtocolStateEntry) bool { if parentState.EpochPhase() == flow.EpochPhaseCommitted { // Requirement 1 return false } diff --git a/state/protocol/protocol_state/epochs/statemachine_test.go b/state/protocol/protocol_state/epochs/statemachine_test.go index 738f01ceb6e..386e8d17a93 100644 --- a/state/protocol/protocol_state/epochs/statemachine_test.go +++ b/state/protocol/protocol_state/epochs/statemachine_test.go @@ -30,12 +30,12 @@ func TestEpochStateMachine(t *testing.T) { // Tests in this suite are designed to rely on automatic assertions when leaving the scope of the test. type EpochStateMachineSuite struct { suite.Suite - epochStateDB *storagemock.ProtocolState + epochStateDB *storagemock.EpochProtocolStateEntries setupsDB *storagemock.EpochSetups commitsDB *storagemock.EpochCommits globalParams *protocolmock.GlobalParams parentState *protocolmock.KVStoreReader - parentEpochState *flow.RichProtocolStateEntry + parentEpochState *flow.RichEpochProtocolStateEntry mutator *protocol_statemock.KVStoreMutator happyPathStateMachine *mock.StateMachine happyPathStateMachineFactory *mock.StateMachineFactoryMethod @@ -46,7 +46,7 @@ type EpochStateMachineSuite struct { } func (s *EpochStateMachineSuite) SetupTest() { - s.epochStateDB = storagemock.NewProtocolState(s.T()) + s.epochStateDB = storagemock.NewEpochProtocolStateEntries(s.T()) s.setupsDB = storagemock.NewEpochSetups(s.T()) s.commitsDB = storagemock.NewEpochCommits(s.T()) s.globalParams = protocolmock.NewGlobalParams(s.T()) @@ -59,7 +59,7 @@ func (s *EpochStateMachineSuite) SetupTest() { s.happyPathStateMachineFactory = mock.NewStateMachineFactoryMethod(s.T()) s.fallbackPathStateMachineFactory = mock.NewStateMachineFactoryMethod(s.T()) - s.epochStateDB.On("ByBlockID", mocks.Anything).Return(func(_ flow.Identifier) *flow.RichProtocolStateEntry { + s.epochStateDB.On("ByBlockID", mocks.Anything).Return(func(_ flow.Identifier) *flow.RichEpochProtocolStateEntry { return s.parentEpochState }, func(_ flow.Identifier) error { return nil @@ -93,7 +93,7 @@ func (s *EpochStateMachineSuite) SetupTest() { // epoch state ID in the KV store even when there were no events to process. func (s *EpochStateMachineSuite) TestBuild_NoChanges() { s.happyPathStateMachine.On("ParentState").Return(s.parentEpochState) - s.happyPathStateMachine.On("Build").Return(s.parentEpochState.ProtocolStateEntry, s.parentEpochState.ID(), false).Once() + s.happyPathStateMachine.On("Build").Return(s.parentEpochState.EpochProtocolStateEntry, s.parentEpochState.ID(), false).Once() err := s.stateMachine.EvolveState(nil) require.NoError(s.T(), err) @@ -117,7 +117,7 @@ func (s *EpochStateMachineSuite) TestBuild_NoChanges() { // This test also ensures that updated state ID is committed in the KV store. func (s *EpochStateMachineSuite) TestBuild_HappyPath() { s.happyPathStateMachine.On("ParentState").Return(s.parentEpochState) - updatedState := unittest.EpochStateFixture().ProtocolStateEntry + updatedState := unittest.EpochStateFixture().EpochProtocolStateEntry updatedStateID := updatedState.ID() s.happyPathStateMachine.On("Build").Return(updatedState, updatedStateID, true).Once() @@ -515,7 +515,7 @@ func (s *EpochStateMachineSuite) TestEvolveState_EventsAreFiltered() { // TestEvolveStateTransitionToNextEpoch_WithInvalidStateTransition tests that EpochStateMachine transitions to the next epoch // if an invalid state transition has been detected in a block which triggers transitioning to the next epoch. // In such situation, we still need to enter the next epoch (because it has already been committed), but persist in the -// state that we have entered Epoch fallback mode (`flow.ProtocolStateEntry.InvalidEpochTransitionAttempted` is set to `true`). +// state that we have entered Epoch fallback mode (`flow.EpochProtocolStateEntry.InvalidEpochTransitionAttempted` is set to `true`). // This test ensures that we don't drop previously committed next epoch. func (s *EpochStateMachineSuite) TestEvolveStateTransitionToNextEpoch_WithInvalidStateTransition() { unittest.SkipUnless(s.T(), unittest.TEST_TODO, @@ -539,7 +539,7 @@ func (s *EpochStateMachineSuite) TestEvolveStateTransitionToNextEpoch_WithInvali indexTxDeferredUpdate.On("Execute", mocks.Anything).Return(nil).Once() s.epochStateDB.On("Index", s.candidate.ID(), mocks.Anything).Return(indexTxDeferredUpdate.Execute, nil).Once() - expectedEpochState := &flow.ProtocolStateEntry{ + expectedEpochState := &flow.EpochProtocolStateEntry{ PreviousEpoch: s.parentEpochState.CurrentEpoch.Copy(), CurrentEpoch: *s.parentEpochState.NextEpoch.Copy(), NextEpoch: nil, diff --git a/state/protocol/protocol_state/state/mutable_protocol_state_test.go b/state/protocol/protocol_state/state/mutable_protocol_state_test.go index dea674a86b2..ebb914d499b 100644 --- a/state/protocol/protocol_state/state/mutable_protocol_state_test.go +++ b/state/protocol/protocol_state/state/mutable_protocol_state_test.go @@ -33,7 +33,7 @@ type StateMutatorSuite struct { headersDB storagemock.Headers resultsDB storagemock.ExecutionResults protocolKVStoreDB protocol_statemock.ProtocolKVStore - epochProtocolStateDB storagemock.ProtocolState + epochProtocolStateDB storagemock.EpochProtocolStateEntries globalParams psmock.GlobalParams kvStateMachines []protocol_statemock.OrthogonalStoreStateMachine[protocol.KVStoreReader] kvStateMachineFactories []protocol_statemock.KeyValueStoreStateMachineFactory @@ -48,7 +48,7 @@ type StateMutatorSuite struct { } func (s *StateMutatorSuite) SetupTest() { - s.epochProtocolStateDB = *storagemock.NewProtocolState(s.T()) + s.epochProtocolStateDB = *storagemock.NewEpochProtocolStateEntries(s.T()) s.protocolKVStoreDB = *protocol_statemock.NewProtocolKVStore(s.T()) s.globalParams = *psmock.NewGlobalParams(s.T()) s.headersDB = *storagemock.NewHeaders(s.T()) diff --git a/state/protocol/protocol_state/state/protocol_state.go b/state/protocol/protocol_state/state/protocol_state.go index d119c0477a8..55056bcd2f1 100644 --- a/state/protocol/protocol_state/state/protocol_state.go +++ b/state/protocol/protocol_state/state/protocol_state.go @@ -18,23 +18,23 @@ import ( // ProtocolState is an implementation of the read-only interface for protocol state, it allows querying information // on a per-block and per-epoch basis. -// It is backed by a storage.ProtocolState and an in-memory protocol.GlobalParams. +// It is backed by a storage.EpochProtocolStateEntries and an in-memory protocol.GlobalParams. type ProtocolState struct { - epochProtocolStateDB storage.ProtocolState + epochProtocolStateDB storage.EpochProtocolStateEntries kvStoreSnapshots protocol_state.ProtocolKVStore globalParams protocol.GlobalParams } var _ protocol.ProtocolState = (*ProtocolState)(nil) -func NewProtocolState(epochProtocolStateDB storage.ProtocolState, kvStoreSnapshots storage.ProtocolKVStore, globalParams protocol.GlobalParams) *ProtocolState { +func NewProtocolState(epochProtocolStateDB storage.EpochProtocolStateEntries, kvStoreSnapshots storage.ProtocolKVStore, globalParams protocol.GlobalParams) *ProtocolState { return newProtocolState(epochProtocolStateDB, kvstore.NewProtocolKVStore(kvStoreSnapshots), globalParams) } // newProtocolState creates a new ProtocolState instance. The exported constructor `NewProtocolState` only requires the // lower-level `storage.ProtocolKVStore` as input. Though, internally we use the higher-level `protocol_state.ProtocolKVStore`, // which wraps the lower-level ProtocolKVStore. -func newProtocolState(epochProtocolStateDB storage.ProtocolState, kvStoreSnapshots protocol_state.ProtocolKVStore, globalParams protocol.GlobalParams) *ProtocolState { +func newProtocolState(epochProtocolStateDB storage.EpochProtocolStateEntries, kvStoreSnapshots protocol_state.ProtocolKVStore, globalParams protocol.GlobalParams) *ProtocolState { return &ProtocolState{ epochProtocolStateDB: epochProtocolStateDB, kvStoreSnapshots: kvStoreSnapshots, @@ -42,19 +42,19 @@ func newProtocolState(epochProtocolStateDB storage.ProtocolState, kvStoreSnapsho } } -// AtBlockID returns epoch protocol state at block ID. +// EpochStateAtBlockID returns epoch protocol state at block ID. // The resulting epoch protocol state is returned AFTER applying updates that are contained in block. // Can be queried for any block that has been added to the block tree. // Returns: -// - (DynamicProtocolState, nil) - if there is an epoch protocol state associated with given block ID. +// - (EpochProtocolState, nil) - if there is an epoch protocol state associated with given block ID. // - (nil, storage.ErrNotFound) - if there is no epoch protocol state associated with given block ID. // - (nil, exception) - any other error should be treated as exception. -func (s *ProtocolState) AtBlockID(blockID flow.Identifier) (protocol.DynamicProtocolState, error) { +func (s *ProtocolState) EpochStateAtBlockID(blockID flow.Identifier) (protocol.EpochProtocolState, error) { protocolStateEntry, err := s.epochProtocolStateDB.ByBlockID(blockID) if err != nil { return nil, fmt.Errorf("could not query epoch protocol state at block (%x): %w", blockID, err) } - return inmem.NewDynamicProtocolStateAdapter(protocolStateEntry, s.globalParams), nil + return inmem.NewEpochProtocolStateAdapter(protocolStateEntry, s.globalParams), nil } // KVStoreAtBlockID returns protocol state at block ID. @@ -86,7 +86,7 @@ var _ protocol.MutableProtocolState = (*MutableProtocolState)(nil) // NewMutableProtocolState creates a new instance of MutableProtocolState. func NewMutableProtocolState( - epochProtocolStateDB storage.ProtocolState, + epochProtocolStateDB storage.EpochProtocolStateEntries, kvStoreSnapshots storage.ProtocolKVStore, globalParams protocol.GlobalParams, headers storage.Headers, @@ -109,7 +109,7 @@ func NewMutableProtocolState( // implement. Therefore, we test it independently of the state machines required for production. In comparison, the // constructor `NewMutableProtocolState` is intended for production use, where the list of state machines is hard-coded. func newMutableProtocolState( - epochProtocolStateDB storage.ProtocolState, + epochProtocolStateDB storage.EpochProtocolStateEntries, kvStoreSnapshots protocol_state.ProtocolKVStore, globalParams protocol.GlobalParams, headers storage.Headers, diff --git a/state/protocol/protocol_state/state/protocol_state_test.go b/state/protocol/protocol_state/state/protocol_state_test.go index 42f4c858fb9..8fd880edfd1 100644 --- a/state/protocol/protocol_state/state/protocol_state_test.go +++ b/state/protocol/protocol_state/state/protocol_state_test.go @@ -17,20 +17,20 @@ import ( ) // Test_ProtocolState verifies the different scenarios of retrieving a protocol state, global parameters -// and KV store snapshots by block ID for the `ProtocolState`. Happy and unhappy paths are covered. +// and KV store snapshots by block ID for the `EpochProtocolStateEntries`. Happy and unhappy paths are covered. func Test_ProtocolState(t *testing.T) { - epochProtocolStateDB := storagemock.NewProtocolState(t) + epochProtocolStateDB := storagemock.NewEpochProtocolStateEntries(t) protocolKVStoreDB := storagemock.NewProtocolKVStore(t) globalParams := psmock.NewGlobalParams(t) protocolState := NewProtocolState(epochProtocolStateDB, protocolKVStoreDB, globalParams) - t.Run("testing `ProtocolState.AtBlockID`", func(t *testing.T) { + t.Run("testing `EpochProtocolStateEntries.AtBlockID`", func(t *testing.T) { test_AtBlockID(t, protocolState, epochProtocolStateDB) }) - t.Run("testing `ProtocolState.GlobalParams`", func(t *testing.T) { + t.Run("testing `EpochProtocolStateEntries.GlobalParams`", func(t *testing.T) { test_GlobalParams(t, protocolState, globalParams) }) - t.Run("testing `ProtocolState.KVStoreAtBlockID`", func(t *testing.T) { + t.Run("testing `EpochProtocolStateEntries.KVStoreAtBlockID`", func(t *testing.T) { test_KVStoreAtBlockID(t, protocolState, protocolKVStoreDB) }) } @@ -38,7 +38,7 @@ func Test_ProtocolState(t *testing.T) { // Test_MutableProtocolState verifies the different scenarios of retrieving a protocol state, global parameters // and KV store snapshots by block ID for the `MutableProtocolState`. Happy and unhappy paths are covered. func Test_MutableProtocolState(t *testing.T) { - epochProtocolStateDB := storagemock.NewProtocolState(t) + epochProtocolStateDB := storagemock.NewEpochProtocolStateEntries(t) protocolKVStoreDB := storagemock.NewProtocolKVStore(t) globalParams := psmock.NewGlobalParams(t) headersDB := storagemock.NewHeaders(t) @@ -66,26 +66,26 @@ func Test_MutableProtocolState(t *testing.T) { }) } -func test_AtBlockID(t *testing.T, protocolState protocol.ProtocolState, epochProtocolStateDB *storagemock.ProtocolState) { +func test_AtBlockID(t *testing.T, protocolState protocol.ProtocolState, epochProtocolStateDB *storagemock.EpochProtocolStateEntries) { blockID := unittest.IdentifierFixture() t.Run("retrieve epoch state for existing blocks", func(t *testing.T) { epochState := unittest.EpochStateFixture(unittest.WithValidDKG()) epochProtocolStateDB.On("ByBlockID", blockID).Return(epochState, nil).Once() - dynamicProtocolState, err := protocolState.AtBlockID(blockID) + epochProtocolState, err := protocolState.EpochStateAtBlockID(blockID) require.NoError(t, err) - assert.Equal(t, epochState.CurrentEpochIdentityTable, dynamicProtocolState.Identities()) + assert.Equal(t, epochState.CurrentEpochIdentityTable, epochProtocolState.Identities()) }) t.Run("retrieving epoch state for non-existing block yields storage.ErrNotFound error", func(t *testing.T) { epochProtocolStateDB.On("ByBlockID", blockID).Return(nil, storage.ErrNotFound).Once() - _, err := protocolState.AtBlockID(blockID) + _, err := protocolState.EpochStateAtBlockID(blockID) require.ErrorIs(t, err, storage.ErrNotFound) }) t.Run("exception during retrieve is propagated", func(t *testing.T) { exception := errors.New("exception") epochProtocolStateDB.On("ByBlockID", blockID).Return(nil, exception).Once() - _, err := protocolState.AtBlockID(blockID) + _, err := protocolState.EpochStateAtBlockID(blockID) require.ErrorIs(t, err, exception) }) } diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 6db3603c442..571c1286cab 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -149,7 +149,7 @@ type Snapshot interface { // The compliance layer guarantees that only valid blocks are appended to the protocol state. // Returns state.ErrUnknownSnapshotReference if snapshot reference block is unknown. // All other errors should be treated as exceptions. - EpochProtocolState() (DynamicProtocolState, error) + EpochProtocolState() (EpochProtocolState, error) // ProtocolState returns the dynamic protocol state that the Head block commits to. // The compliance layer guarantees that only valid blocks are appended to the protocol state. diff --git a/state/protocol/util/testing.go b/state/protocol/util/testing.go index ed7c1cef2ec..d637fe4a330 100644 --- a/state/protocol/util/testing.go +++ b/state/protocol/util/testing.go @@ -78,7 +78,7 @@ func RunWithBootstrapState(t testing.TB, rootSnapshot protocol.Snapshot, f func( all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -105,7 +105,7 @@ func RunWithFullProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, f fu all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -146,7 +146,7 @@ func RunWithFullProtocolStateAndMetrics(t testing.TB, rootSnapshot protocol.Snap all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -188,7 +188,7 @@ func RunWithFullProtocolStateAndValidator(t testing.TB, rootSnapshot protocol.Sn all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -229,7 +229,7 @@ func RunWithFollowerProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -266,7 +266,7 @@ func RunWithFullProtocolStateAndConsumer(t testing.TB, rootSnapshot protocol.Sna all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -306,7 +306,7 @@ func RunWithFullProtocolStateAndMetricsAndConsumer(t testing.TB, rootSnapshot pr all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -328,7 +328,7 @@ func RunWithFullProtocolStateAndMetricsAndConsumer(t testing.TB, rootSnapshot pr ) require.NoError(t, err) mutableProtocolState := protocol_state.NewMutableProtocolState( - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, state.Params(), all.Headers, @@ -357,7 +357,7 @@ func RunWithFollowerProtocolStateAndHeaders(t testing.TB, rootSnapshot protocol. all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -395,7 +395,7 @@ func RunWithFullProtocolStateAndMutator(t testing.TB, rootSnapshot protocol.Snap all.QuorumCertificates, all.Setups, all.EpochCommits, - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, all.VersionBeacons, rootSnapshot, @@ -417,7 +417,7 @@ func RunWithFullProtocolStateAndMutator(t testing.TB, rootSnapshot protocol.Snap ) require.NoError(t, err) mutableProtocolState := protocol_state.NewMutableProtocolState( - all.EpochProtocolState, + all.EpochProtocolStateEntries, all.ProtocolKVStore, state.Params(), all.Headers, diff --git a/state/protocol/validity.go b/state/protocol/validity.go index 52e6254a51c..bdaf06ff89a 100644 --- a/state/protocol/validity.go +++ b/state/protocol/validity.go @@ -14,7 +14,7 @@ import ( // CAUTION: This function assumes that all inputs besides extendingCommit are already validated. // Expected errors during normal operations: // * protocol.InvalidServiceEventError if the input service event is invalid to extend the currently active epoch status -func IsValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, protocolStateEntry *flow.ProtocolStateEntry, currentEpochSetupEvent *flow.EpochSetup) error { +func IsValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, protocolStateEntry *flow.EpochProtocolStateEntry, currentEpochSetupEvent *flow.EpochSetup) error { // Enforce EpochSetup is valid w.r.t to current epoch state if protocolStateEntry.NextEpoch != nil { // We should only have a single epoch setup event per epoch. // true iff EpochSetup event for NEXT epoch was already included before @@ -129,7 +129,7 @@ func IsValidEpochSetup(setup *flow.EpochSetup, verifyNetworkAddress bool) error // CAUTION: This function assumes that all inputs besides extendingCommit are already validated. // Expected errors during normal operations: // * protocol.InvalidServiceEventError if the input service event is invalid to extend the currently active epoch -func IsValidExtendingEpochCommit(extendingCommit *flow.EpochCommit, protocolStateEntry *flow.ProtocolStateEntry, nextEpochSetupEvent *flow.EpochSetup) error { +func IsValidExtendingEpochCommit(extendingCommit *flow.EpochCommit, protocolStateEntry *flow.EpochProtocolStateEntry, nextEpochSetupEvent *flow.EpochSetup) error { // The epoch setup event needs to happen before the commit. if protocolStateEntry.NextEpoch == nil { return NewInvalidServiceEventErrorf("missing epoch setup for epoch commit") diff --git a/state/protocol/validity_test.go b/state/protocol/validity_test.go index 5a73e3dba8d..a12f53e6b96 100644 --- a/state/protocol/validity_test.go +++ b/state/protocol/validity_test.go @@ -148,7 +148,7 @@ func TestIsValidExtendingEpochSetup(t *testing.T) { unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) - err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) require.NoError(t, err) }) t.Run("(a) We should only have a single epoch setup event per epoch.", func(t *testing.T) { @@ -160,7 +160,7 @@ func TestIsValidExtendingEpochSetup(t *testing.T) { unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) - err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) require.Error(t, err) }) t.Run("(b) The setup event should have the counter increased by one", func(t *testing.T) { @@ -172,7 +172,7 @@ func TestIsValidExtendingEpochSetup(t *testing.T) { unittest.SetupWithCounter(currentEpochSetup.Counter+2), unittest.WithParticipants(participants.ToSkeleton()), ) - err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) require.Error(t, err) }) t.Run("(c) The first view needs to be exactly one greater than the current epoch final view", func(t *testing.T) { @@ -184,7 +184,7 @@ func TestIsValidExtendingEpochSetup(t *testing.T) { unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) - err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) require.Error(t, err) }) } @@ -195,7 +195,7 @@ func TestIsValidExtendingEpochSetup(t *testing.T) { // additionally we require other conditions, but they are tested by separate test `TestEpochCommitValidity`. func TestIsValidExtendingEpochCommit(t *testing.T) { t.Run("happy path", func(t *testing.T) { - protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { + protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichEpochProtocolStateEntry) { entry.NextEpochCommit = nil entry.NextEpoch.CommitID = flow.ZeroID }) @@ -205,7 +205,7 @@ func TestIsValidExtendingEpochCommit(t *testing.T) { unittest.CommitWithCounter(nextEpochSetup.Counter), unittest.WithDKGFromParticipants(nextEpochSetup.Participants), ) - err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) + err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.EpochProtocolStateEntry, nextEpochSetup) require.NoError(t, err) }) t.Run("(a) The epoch setup event needs to happen before the commit", func(t *testing.T) { @@ -221,7 +221,7 @@ func TestIsValidExtendingEpochCommit(t *testing.T) { unittest.CommitWithCounter(nextEpochSetup.Counter), unittest.WithDKGFromParticipants(nextEpochSetup.Participants), ) - err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) + err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.EpochProtocolStateEntry, nextEpochSetup) require.Error(t, err) }) t.Run("We should only have a single epoch commit event per epoch", func(t *testing.T) { @@ -232,7 +232,7 @@ func TestIsValidExtendingEpochCommit(t *testing.T) { unittest.CommitWithCounter(nextEpochSetup.Counter), unittest.WithDKGFromParticipants(nextEpochSetup.Participants), ) - err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) + err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.EpochProtocolStateEntry, nextEpochSetup) require.Error(t, err) }) } diff --git a/storage/all.go b/storage/all.go index 650b0652ee5..2d0075aa8be 100644 --- a/storage/all.go +++ b/storage/all.go @@ -2,26 +2,26 @@ package storage // All includes all the storage modules type All struct { - Headers Headers - Guarantees Guarantees - Seals Seals - Index Index - Payloads Payloads - Blocks Blocks - QuorumCertificates QuorumCertificates - Setups EpochSetups - EpochCommits EpochCommits - Results ExecutionResults - Receipts ExecutionReceipts - ChunkDataPacks ChunkDataPacks - Commits Commits - Transactions Transactions - LightTransactionResults LightTransactionResults - TransactionResults TransactionResults - Collections Collections - Events Events - EpochProtocolState ProtocolState - ProtocolKVStore ProtocolKVStore - VersionBeacons VersionBeacons - RegisterIndex RegisterIndex + Headers Headers + Guarantees Guarantees + Seals Seals + Index Index + Payloads Payloads + Blocks Blocks + QuorumCertificates QuorumCertificates + Setups EpochSetups + EpochCommits EpochCommits + Results ExecutionResults + Receipts ExecutionReceipts + ChunkDataPacks ChunkDataPacks + Commits Commits + Transactions Transactions + LightTransactionResults LightTransactionResults + TransactionResults TransactionResults + Collections Collections + Events Events + EpochProtocolStateEntries EpochProtocolStateEntries + ProtocolKVStore ProtocolKVStore + VersionBeacons VersionBeacons + RegisterIndex RegisterIndex } diff --git a/storage/badger/all.go b/storage/badger/all.go index bbaaa7666db..0d68d963032 100644 --- a/storage/badger/all.go +++ b/storage/badger/all.go @@ -19,8 +19,8 @@ func InitAll(metrics module.CacheMetrics, db *badger.DB) *storage.All { qcs := NewQuorumCertificates(metrics, db, DefaultCacheSize) setups := NewEpochSetups(metrics, db) epochCommits := NewEpochCommits(metrics, db) - protocolState := NewProtocolState(metrics, setups, epochCommits, db, - DefaultProtocolStateCacheSize, DefaultProtocolStateByBlockIDCacheSize) + epochProtocolStateEntries := NewEpochProtocolStateEntries(metrics, setups, epochCommits, db, + DefaultEpochProtocolStateCacheSize, DefaultProtocolStateIndexCacheSize) protocolKVStore := NewProtocolKVStore(metrics, db, DefaultProtocolKVStoreCacheSize, DefaultProtocolKVStoreByBlockIDCacheSize) versionBeacons := NewVersionBeacons(db) @@ -32,25 +32,25 @@ func InitAll(metrics module.CacheMetrics, db *badger.DB) *storage.All { chunkDataPacks := NewChunkDataPacks(metrics, db, collections, 1000) return &storage.All{ - Headers: headers, - Guarantees: guarantees, - Seals: seals, - Index: index, - Payloads: payloads, - Blocks: blocks, - QuorumCertificates: qcs, - Setups: setups, - EpochCommits: epochCommits, - EpochProtocolState: protocolState, - ProtocolKVStore: protocolKVStore, - VersionBeacons: versionBeacons, - Results: results, - Receipts: receipts, - ChunkDataPacks: chunkDataPacks, - Commits: commits, - Transactions: transactions, - TransactionResults: transactionResults, - Collections: collections, - Events: events, + Headers: headers, + Guarantees: guarantees, + Seals: seals, + Index: index, + Payloads: payloads, + Blocks: blocks, + QuorumCertificates: qcs, + Setups: setups, + EpochCommits: epochCommits, + EpochProtocolStateEntries: epochProtocolStateEntries, + ProtocolKVStore: protocolKVStore, + VersionBeacons: versionBeacons, + Results: results, + Receipts: receipts, + ChunkDataPacks: chunkDataPacks, + Commits: commits, + Transactions: transactions, + TransactionResults: transactionResults, + Collections: collections, + Events: events, } } diff --git a/storage/badger/protocol_state.go b/storage/badger/epoch_protocol_state.go similarity index 56% rename from storage/badger/protocol_state.go rename to storage/badger/epoch_protocol_state.go index 101ed11104a..80d70a22209 100644 --- a/storage/badger/protocol_state.go +++ b/storage/badger/epoch_protocol_state.go @@ -14,44 +14,44 @@ import ( "github.com/onflow/flow-go/storage/badger/transaction" ) -// DefaultProtocolStateCacheSize is the default size for primary protocol state cache. +// DefaultEpochProtocolStateCacheSize is the default size for primary epoch protocol state entry cache. // Minimally, we have 3 entries per epoch (one on epoch Switchover, one on receiving the Epoch Setup and one when seeing the Epoch Commit event). -// Lets be generous and assume we have 20 different Protocol States per epoch. -var DefaultProtocolStateCacheSize uint = 20 +// Let's be generous and assume we have 20 different epoch state entries per epoch. +var DefaultEpochProtocolStateCacheSize uint = 20 -// DefaultProtocolStateByBlockIDCacheSize is the default value for secondary byBlockIdCache. +// DefaultProtocolStateIndexCacheSize is the default value for secondary byBlockIdCache. // We want to be able to cover a broad interval of views without cache misses, so we use a bigger value. -var DefaultProtocolStateByBlockIDCacheSize uint = 1000 +var DefaultProtocolStateIndexCacheSize uint = 1000 -// ProtocolState implements persistent storage for storing Protocol States. -// Protocol state uses an embedded cache without storing capabilities(store happens on first retrieval) to avoid unnecessary -// operations and to speed up access to frequently used Protocol State. -type ProtocolState struct { +// EpochProtocolStateEntries implements a persistent, fork-aware storage for the Epoch-related +// sub-states of the overall of the overall Protocol State (KV Store). It uses an embedded cache +// which is populated on first retrieval to speed up access to frequently used epoch sub-state. +type EpochProtocolStateEntries struct { db *badger.DB - // cache is essentially an in-memory map from `ProtocolStateEntry.ID()` -> `RichProtocolStateEntry` - // We do _not_ populate this cache which holds the RichProtocolStateEntrys on store. This is because - // (i) we don't have the RichProtocolStateEntry on store readily available and - // (ii) new RichProtocolStateEntry are really rare throughout an epoch, so the total cost of populating + // cache is essentially an in-memory map from `EpochProtocolStateEntry.ID()` -> `RichEpochProtocolStateEntry` + // We do _not_ populate this cache which holds the RichEpochProtocolStateEntry's on store. This is because + // (i) we don't have the RichEpochProtocolStateEntry on store readily available and + // (ii) new RichEpochProtocolStateEntry are really rare throughout an epoch, so the total cost of populating // the cache becomes negligible over several views. // In the future, we might want to populate the cache on store, if we want to maintain frequently-changing // information in the protocol state, like the latest sealed block. This should be a smaller amount of work, - // because the `ProtocolStateEntry` is generated by `StateMutator.Build()`. The `StateMutator` should already - // have the needed Epoch Setup and Commit events, since it starts with a RichProtocolStateEntry for the parent + // because the `EpochProtocolStateEntry` is generated by `StateMutator.Build()`. The `StateMutator` should already + // have the needed Epoch Setup and Commit events, since it starts with a RichEpochProtocolStateEntry for the parent // state and consumes Epoch Setup and Epoch Commit events. Though, we leave this optimization for later. // - // `cache` only holds the distinct Protocol States. On the happy path, we expect something like 3 entries per epoch. + // `cache` only holds the distinct state entries. On the happy path, we expect something like 3 entries per epoch. // On the optimal happy path we have 3 entries per epoch: one entry on epoch Switchover, one on receiving the Epoch Setup - // and one when seeing the Epoch Commit event. Let's be generous and assume we have 20 different Protocol States per epoch. + // and one when seeing the Epoch Commit event. Let's be generous and assume we have 20 different state entries per epoch. // Beyond that, we are certainly leaving the domain of normal operations that we optimize for. Therefore, a cache size of // roughly 100 is a reasonable balance between performance and memory consumption. - cache *Cache[flow.Identifier, *flow.RichProtocolStateEntry] + cache *Cache[flow.Identifier, *flow.RichEpochProtocolStateEntry] - // byBlockIdCache is essentially an in-memory map from `Block.ID()` -> `ProtocolStateEntry.ID()`. The full - // Protocol state can be retrieved from the `cache` above. + // byBlockIdCache is essentially an in-memory map from `Block.ID()` -> `EpochProtocolStateEntry.ID()`. The full + // flow.RichEpochProtocolStateEntry can be retrieved from the `cache` above. // We populate the `byBlockIdCache` on store, because a new entry is added for every block and we probably also // query the Protocol state for every block. So argument (ii) from above does not apply here. Furthermore, - // argument (i) from above also does not apply, because we already have the Protocol State's ID on store, + // argument (i) from above also does not apply, because we already have the state entry's ID on store, // so populating the cache is easy. // // `byBlockIdCache` will contain an entry for every block. We want to be able to cover a broad interval of views @@ -59,37 +59,38 @@ type ProtocolState struct { byBlockIdCache *Cache[flow.Identifier, flow.Identifier] } -var _ storage.ProtocolState = (*ProtocolState)(nil) +var _ storage.EpochProtocolStateEntries = (*EpochProtocolStateEntries)(nil) -// NewProtocolState creates a ProtocolState instance, which is a database of Protocol State. +// NewEpochProtocolStateEntries creates a EpochProtocolStateEntries instance, which stores a subset of the +// state stored by the Dynamic Protocol State. // It supports storing, caching and retrieving by ID or the additionally indexed block ID. -func NewProtocolState(collector module.CacheMetrics, +func NewEpochProtocolStateEntries(collector module.CacheMetrics, epochSetups storage.EpochSetups, epochCommits storage.EpochCommits, db *badger.DB, stateCacheSize uint, stateByBlockIDCacheSize uint, -) *ProtocolState { - retrieveByProtocolStateID := func(protocolStateID flow.Identifier) func(tx *badger.Txn) (*flow.RichProtocolStateEntry, error) { - var protocolStateEntry flow.ProtocolStateEntry - return func(tx *badger.Txn) (*flow.RichProtocolStateEntry, error) { - err := operation.RetrieveProtocolState(protocolStateID, &protocolStateEntry)(tx) +) *EpochProtocolStateEntries { + retrieveByEntryID := func(epochProtocolStateEntryID flow.Identifier) func(tx *badger.Txn) (*flow.RichEpochProtocolStateEntry, error) { + var entry flow.EpochProtocolStateEntry + return func(tx *badger.Txn) (*flow.RichEpochProtocolStateEntry, error) { + err := operation.RetrieveEpochProtocolState(epochProtocolStateEntryID, &entry)(tx) if err != nil { return nil, err } - result, err := newRichProtocolStateEntry(&protocolStateEntry, epochSetups, epochCommits) + result, err := newRichEpochProtocolStateEntry(&entry, epochSetups, epochCommits) if err != nil { - return nil, fmt.Errorf("could not create rich protocol state entry: %w", err) + return nil, fmt.Errorf("could not create RichEpochProtocolStateEntry: %w", err) } return result, nil } } - storeByBlockID := func(blockID flow.Identifier, protocolStateID flow.Identifier) func(*transaction.Tx) error { + storeByBlockID := func(blockID flow.Identifier, epochProtocolStateEntryID flow.Identifier) func(*transaction.Tx) error { return func(tx *transaction.Tx) error { - err := transaction.WithTx(operation.IndexProtocolState(blockID, protocolStateID))(tx) + err := transaction.WithTx(operation.IndexEpochProtocolState(blockID, epochProtocolStateEntryID))(tx) if err != nil { - return fmt.Errorf("could not index protocol state for block (%x): %w", blockID[:], err) + return fmt.Errorf("could not index EpochProtocolState for block (%x): %w", blockID[:], err) } return nil } @@ -97,21 +98,21 @@ func NewProtocolState(collector module.CacheMetrics, retrieveByBlockID := func(blockID flow.Identifier) func(tx *badger.Txn) (flow.Identifier, error) { return func(tx *badger.Txn) (flow.Identifier, error) { - var protocolStateID flow.Identifier - err := operation.LookupProtocolState(blockID, &protocolStateID)(tx) + var entryID flow.Identifier + err := operation.LookupEpochProtocolState(blockID, &entryID)(tx) if err != nil { - return flow.ZeroID, fmt.Errorf("could not lookup protocol state ID for block (%x): %w", blockID[:], err) + return flow.ZeroID, fmt.Errorf("could not lookup epoch protocol state entry ID for block (%x): %w", blockID[:], err) } - return protocolStateID, nil + return entryID, nil } } - return &ProtocolState{ + return &EpochProtocolStateEntries{ db: db, - cache: newCache[flow.Identifier, *flow.RichProtocolStateEntry](collector, metrics.ResourceProtocolState, - withLimit[flow.Identifier, *flow.RichProtocolStateEntry](stateCacheSize), - withStore(noopStore[flow.Identifier, *flow.RichProtocolStateEntry]), - withRetrieve(retrieveByProtocolStateID)), + cache: newCache[flow.Identifier, *flow.RichEpochProtocolStateEntry](collector, metrics.ResourceProtocolState, + withLimit[flow.Identifier, *flow.RichEpochProtocolStateEntry](stateCacheSize), + withStore(noopStore[flow.Identifier, *flow.RichEpochProtocolStateEntry]), + withRetrieve(retrieveByEntryID)), byBlockIdCache: newCache[flow.Identifier, flow.Identifier](collector, metrics.ResourceProtocolStateByBlockID, withLimit[flow.Identifier, flow.Identifier](stateByBlockIDCacheSize), withStore(storeByBlockID), @@ -120,27 +121,27 @@ func NewProtocolState(collector module.CacheMetrics, } // StoreTx returns an anonymous function (intended to be executed as part of a badger transaction), -// which persists the given protocol state as part of a DB tx. Per convention, the identities in -// the Protocol State must be in canonical order for the current and next epoch (if present), +// which persists the given epoch protocol state entry as part of a DB tx. Per convention, the identities in +// the flow.EpochProtocolStateEntry must be in canonical order for the current and next epoch (if present), // otherwise an exception is returned. // Expected errors of the returned anonymous function: -// - storage.ErrAlreadyExists if a Protocol State with the given id is already stored -func (s *ProtocolState) StoreTx(protocolStateID flow.Identifier, protocolState *flow.ProtocolStateEntry) func(*transaction.Tx) error { +// - storage.ErrAlreadyExists if a state entry with the given id is already stored +func (s *EpochProtocolStateEntries) StoreTx(epochProtocolStateEntryID flow.Identifier, epochStateEntry *flow.EpochProtocolStateEntry) func(*transaction.Tx) error { // front-load sanity checks: - if !protocolState.CurrentEpoch.ActiveIdentities.Sorted(flow.IdentifierCanonical) { + if !epochStateEntry.CurrentEpoch.ActiveIdentities.Sorted(flow.IdentifierCanonical) { return transaction.Fail(fmt.Errorf("sanity check failed: identities are not sorted")) } - if protocolState.NextEpoch != nil && !protocolState.NextEpoch.ActiveIdentities.Sorted(flow.IdentifierCanonical) { + if epochStateEntry.NextEpoch != nil && !epochStateEntry.NextEpoch.ActiveIdentities.Sorted(flow.IdentifierCanonical) { return transaction.Fail(fmt.Errorf("sanity check failed: next epoch identities are not sorted")) } - // happy path: return anonymous function, whose future execution (as part of a transaction) will store the protocolState - return transaction.WithTx(operation.InsertProtocolState(protocolStateID, protocolState)) + // happy path: return anonymous function, whose future execution (as part of a transaction) will store the state entry. + return transaction.WithTx(operation.InsertEpochProtocolState(epochProtocolStateEntryID, epochStateEntry)) } // Index returns an anonymous function that is intended to be executed as part of a database transaction. -// In a nutshell, we want to maintain a map from `blockID` to `protocolStateID`, where `blockID` references the -// block that _proposes_ the Protocol State. +// In a nutshell, we want to maintain a map from `blockID` to `epochStateEntry`, where `blockID` references the +// block that _proposes_ the referenced epoch protocol state entry. // Upon call, the anonymous function persists the specific map entry in the node's database. // Protocol convention: // - Consider block B, whose ingestion might potentially lead to an updated protocol state. For example, @@ -151,21 +152,21 @@ func (s *ProtocolState) StoreTx(protocolStateID flow.Identifier, protocolState * // _after_ validating the QC. // // Expected errors during normal operations: -// - storage.ErrAlreadyExists if a Protocol State for the given blockID has already been indexed -func (s *ProtocolState) Index(blockID flow.Identifier, protocolStateID flow.Identifier) func(*transaction.Tx) error { - return s.byBlockIdCache.PutTx(blockID, protocolStateID) +// - storage.ErrAlreadyExists if a state entry for the given blockID has already been indexed +func (s *EpochProtocolStateEntries) Index(blockID flow.Identifier, epochProtocolStateEntryID flow.Identifier) func(*transaction.Tx) error { + return s.byBlockIdCache.PutTx(blockID, epochProtocolStateEntryID) } -// ByID returns the protocol state by its ID. +// ByID returns the epoch protocol state entry by its ID. // Expected errors during normal operations: // - storage.ErrNotFound if no protocol state with the given Identifier is known. -func (s *ProtocolState) ByID(protocolStateID flow.Identifier) (*flow.RichProtocolStateEntry, error) { +func (s *EpochProtocolStateEntries) ByID(epochProtocolStateEntryID flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) { tx := s.db.NewTransaction(false) defer tx.Discard() - return s.cache.Get(protocolStateID)(tx) + return s.cache.Get(epochProtocolStateEntryID)(tx) } -// ByBlockID retrieves the Protocol State that the block with the given ID proposes. +// ByBlockID retrieves the epoch protocol state entry that the block with the given ID proposes. // CAUTION: this protocol state requires confirmation by a QC and will only become active at the child block, // _after_ validating the QC. Protocol convention: // - Consider block B, whose ingestion might potentially lead to an updated protocol state. For example, @@ -176,25 +177,25 @@ func (s *ProtocolState) ByID(protocolStateID flow.Identifier) (*flow.RichProtoco // _after_ validating the QC. // // Expected errors during normal operations: -// - storage.ErrNotFound if no protocol state has been indexed for the given block. -func (s *ProtocolState) ByBlockID(blockID flow.Identifier) (*flow.RichProtocolStateEntry, error) { +// - storage.ErrNotFound if no state entry has been indexed for the given block. +func (s *EpochProtocolStateEntries) ByBlockID(blockID flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) { tx := s.db.NewTransaction(false) defer tx.Discard() - protocolStateID, err := s.byBlockIdCache.Get(blockID)(tx) + epochProtocolStateEntryID, err := s.byBlockIdCache.Get(blockID)(tx) if err != nil { - return nil, fmt.Errorf("could not lookup protocol state ID for block (%x): %w", blockID[:], err) + return nil, fmt.Errorf("could not lookup epoch protocol state ID for block (%x): %w", blockID[:], err) } - return s.cache.Get(protocolStateID)(tx) + return s.cache.Get(epochProtocolStateEntryID)(tx) } -// newRichProtocolStateEntry constructs a rich protocol state entry from a protocol state entry. +// newRichEpochProtocolStateEntry constructs a RichEpochProtocolStateEntry from an epoch sub-state entry. // It queries and fills in epoch setups and commits for previous and current epochs and possibly next epoch. // No errors are expected during normal operation. -func newRichProtocolStateEntry( - protocolState *flow.ProtocolStateEntry, +func newRichEpochProtocolStateEntry( + protocolState *flow.EpochProtocolStateEntry, setups storage.EpochSetups, commits storage.EpochCommits, -) (*flow.RichProtocolStateEntry, error) { +) (*flow.RichEpochProtocolStateEntry, error) { var ( previousEpochSetup *flow.EpochSetup previousEpochCommit *flow.EpochCommit @@ -238,7 +239,7 @@ func newRichProtocolStateEntry( } } - result, err := flow.NewRichProtocolStateEntry( + result, err := flow.NewRichEpochProtocolStateEntry( protocolState, previousEpochSetup, previousEpochCommit, @@ -250,7 +251,7 @@ func newRichProtocolStateEntry( if err != nil { // observing an error here would be an indication of severe data corruption or bug in our code since // all data should be available and correctly structured at this point. - return nil, irrecoverable.NewExceptionf("critical failure while instantiating RichProtocolStateEntry: %w", err) + return nil, irrecoverable.NewExceptionf("critical failure while instantiating RichEpochProtocolStateEntry: %w", err) } return result, nil } diff --git a/storage/badger/protocol_state_test.go b/storage/badger/epoch_protocol_state_test.go similarity index 88% rename from storage/badger/protocol_state_test.go rename to storage/badger/epoch_protocol_state_test.go index 477a7751bfe..dcd0cf3536e 100644 --- a/storage/badger/protocol_state_test.go +++ b/storage/badger/epoch_protocol_state_test.go @@ -21,7 +21,7 @@ func TestProtocolStateStorage(t *testing.T) { setups := NewEpochSetups(metrics, db) commits := NewEpochCommits(metrics, db) - store := NewProtocolState(metrics, setups, commits, db, DefaultProtocolStateCacheSize, DefaultProtocolStateByBlockIDCacheSize) + store := NewEpochProtocolStateEntries(metrics, setups, commits, db, DefaultEpochProtocolStateCacheSize, DefaultProtocolStateIndexCacheSize) expected := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) protocolStateID := expected.ID() @@ -43,7 +43,7 @@ func TestProtocolStateStorage(t *testing.T) { err = commits.StoreTx(expected.NextEpochCommit)(tx) require.NoError(t, err) - err = store.StoreTx(protocolStateID, expected.ProtocolStateEntry)(tx) + err = store.StoreTx(protocolStateID, expected.EpochProtocolStateEntry)(tx) require.NoError(t, err) return store.Index(blockID, protocolStateID)(tx) }) @@ -72,15 +72,15 @@ func TestProtocolStateStoreInvalidProtocolState(t *testing.T) { metrics := metrics.NewNoopCollector() setups := NewEpochSetups(metrics, db) commits := NewEpochCommits(metrics, db) - store := NewProtocolState(metrics, setups, commits, db, DefaultProtocolStateCacheSize, DefaultProtocolStateByBlockIDCacheSize) - invalid := unittest.EpochStateFixture().ProtocolStateEntry + store := NewEpochProtocolStateEntries(metrics, setups, commits, db, DefaultEpochProtocolStateCacheSize, DefaultProtocolStateIndexCacheSize) + invalid := unittest.EpochStateFixture().EpochProtocolStateEntry // swap first and second elements to break canonical order invalid.CurrentEpoch.ActiveIdentities[0], invalid.CurrentEpoch.ActiveIdentities[1] = invalid.CurrentEpoch.ActiveIdentities[1], invalid.CurrentEpoch.ActiveIdentities[0] err := transaction.Update(db, store.StoreTx(invalid.ID(), invalid)) require.Error(t, err) - invalid = unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()).ProtocolStateEntry + invalid = unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()).EpochProtocolStateEntry // swap first and second elements to break canonical order invalid.NextEpoch.ActiveIdentities[0], invalid.NextEpoch.ActiveIdentities[1] = invalid.NextEpoch.ActiveIdentities[1], invalid.NextEpoch.ActiveIdentities[0] @@ -98,7 +98,7 @@ func TestProtocolStateMergeParticipants(t *testing.T) { setups := NewEpochSetups(metrics, db) commits := NewEpochCommits(metrics, db) - store := NewProtocolState(metrics, setups, commits, db, DefaultProtocolStateCacheSize, DefaultProtocolStateByBlockIDCacheSize) + store := NewEpochProtocolStateEntries(metrics, setups, commits, db, DefaultEpochProtocolStateCacheSize, DefaultProtocolStateIndexCacheSize) stateEntry := unittest.EpochStateFixture() // change address of participant in current epoch, so we can distinguish it from the one in previous epoch @@ -123,7 +123,7 @@ func TestProtocolStateMergeParticipants(t *testing.T) { err = commits.StoreTx(stateEntry.CurrentEpochCommit)(tx) require.NoError(t, err) - return store.StoreTx(protocolStateID, stateEntry.ProtocolStateEntry)(tx) + return store.StoreTx(protocolStateID, stateEntry.EpochProtocolStateEntry)(tx) }) require.NoError(t, err) @@ -147,8 +147,8 @@ func TestProtocolStateRootSnapshot(t *testing.T) { setups := NewEpochSetups(metrics, db) commits := NewEpochCommits(metrics, db) - store := NewProtocolState(metrics, setups, commits, db, DefaultProtocolStateCacheSize, DefaultProtocolStateByBlockIDCacheSize) - expected := unittest.RootProtocolStateFixture() + store := NewEpochProtocolStateEntries(metrics, setups, commits, db, DefaultEpochProtocolStateCacheSize, DefaultProtocolStateIndexCacheSize) + expected := unittest.RootEpochProtocolStateFixture() protocolStateID := expected.ID() blockID := unittest.IdentifierFixture() @@ -161,7 +161,7 @@ func TestProtocolStateRootSnapshot(t *testing.T) { err = commits.StoreTx(expected.CurrentEpochCommit)(tx) require.NoError(t, err) - err = store.StoreTx(protocolStateID, expected.ProtocolStateEntry)(tx) + err = store.StoreTx(protocolStateID, expected.EpochProtocolStateEntry)(tx) require.NoError(t, err) return store.Index(blockID, protocolStateID)(tx) }) @@ -184,13 +184,13 @@ func TestProtocolStateRootSnapshot(t *testing.T) { } // assertRichProtocolStateValidity checks if RichProtocolState holds its invariant and is correctly populated by storage layer. -func assertRichProtocolStateValidity(t *testing.T, state *flow.RichProtocolStateEntry) { +func assertRichProtocolStateValidity(t *testing.T, state *flow.RichEpochProtocolStateEntry) { // invariants: // - CurrentEpochSetup and CurrentEpochCommit are for the same epoch. Never nil. - // - CurrentEpochSetup and CurrentEpochCommit IDs match respective commitments in the `ProtocolStateEntry`. + // - CurrentEpochSetup and CurrentEpochCommit IDs match respective commitments in the `EpochProtocolStateEntry`. assert.Equal(t, state.CurrentEpochSetup.Counter, state.CurrentEpochCommit.Counter, "current epoch setup and commit should be for the same epoch") - assert.Equal(t, state.CurrentEpochSetup.ID(), state.ProtocolStateEntry.CurrentEpoch.SetupID, "epoch setup should be for correct event ID") - assert.Equal(t, state.CurrentEpochCommit.ID(), state.ProtocolStateEntry.CurrentEpoch.CommitID, "epoch commit should be for correct event ID") + assert.Equal(t, state.CurrentEpochSetup.ID(), state.EpochProtocolStateEntry.CurrentEpoch.SetupID, "epoch setup should be for correct event ID") + assert.Equal(t, state.CurrentEpochCommit.ID(), state.EpochProtocolStateEntry.CurrentEpoch.CommitID, "epoch commit should be for correct event ID") var ( previousEpochParticipants flow.IdentityList @@ -203,8 +203,8 @@ func assertRichProtocolStateValidity(t *testing.T, state *flow.RichProtocolState assert.Equal(t, state.PreviousEpochSetup.Counter, state.PreviousEpochCommit.Counter, "previous epoch setup and commit should be for the same epoch") // invariant: PreviousEpochSetup and PreviousEpochCommit IDs are the equal to the ID of the protocol state entry. Never nil. - assert.Equal(t, state.PreviousEpochSetup.ID(), state.ProtocolStateEntry.PreviousEpoch.SetupID, "epoch setup should be for correct event ID") - assert.Equal(t, state.PreviousEpochCommit.ID(), state.ProtocolStateEntry.PreviousEpoch.CommitID, "epoch commit should be for correct event ID") + assert.Equal(t, state.PreviousEpochSetup.ID(), state.EpochProtocolStateEntry.PreviousEpoch.SetupID, "epoch setup should be for correct event ID") + assert.Equal(t, state.PreviousEpochCommit.ID(), state.EpochProtocolStateEntry.PreviousEpoch.CommitID, "epoch commit should be for correct event ID") // invariant: ComposeFullIdentities ensures that we can build full identities of previous epoch's active participants. This step also confirms that the // previous epoch's `Participants` [IdentitySkeletons] and `ActiveIdentities` [DynamicIdentity properties] list the same nodes in canonical ordering. @@ -260,7 +260,7 @@ func assertRichProtocolStateValidity(t *testing.T, state *flow.RichProtocolState // invariants: // - NextEpochSetup and NextEpochCommit are for the same epoch. Never nil. - // - NextEpochSetup and NextEpochCommit IDs match respective commitments in the `ProtocolStateEntry`. + // - NextEpochSetup and NextEpochCommit IDs match respective commitments in the `EpochProtocolStateEntry`. assert.Equal(t, state.CurrentEpochSetup.Counter+1, state.NextEpochSetup.Counter, "next epoch (%d) should be following right after current epoch (%d)", state.NextEpochSetup.Counter, state.CurrentEpochSetup.Counter) assert.Equal(t, state.NextEpochSetup.Counter, state.NextEpochCommit.Counter, "next epoch setup and commit should be for the same epoch") assert.Equal(t, state.NextEpochSetup.ID(), state.NextEpoch.SetupID, "epoch setup should be for correct event ID") diff --git a/storage/badger/operation/epoch_protocol_state.go b/storage/badger/operation/epoch_protocol_state.go new file mode 100644 index 00000000000..dd28a1359e7 --- /dev/null +++ b/storage/badger/operation/epoch_protocol_state.go @@ -0,0 +1,39 @@ +package operation + +import ( + "github.com/dgraph-io/badger/v2" + + "github.com/onflow/flow-go/model/flow" +) + +// InsertEpochProtocolState inserts an epoch protocol state entry by ID. +// Error returns: +// - storage.ErrAlreadyExists if the key already exists in the database. +// - generic error in case of unexpected failure from the database layer or encoding failure. +func InsertEpochProtocolState(entryID flow.Identifier, entry *flow.EpochProtocolStateEntry) func(*badger.Txn) error { + return insert(makePrefix(codeEpochProtocolState, entryID), entry) +} + +// RetrieveEpochProtocolState retrieves an epoch protocol state entry by ID. +// Error returns: +// - storage.ErrNotFound if the key does not exist in the database +// - generic error in case of unexpected failure from the database layer +func RetrieveEpochProtocolState(entryID flow.Identifier, entry *flow.EpochProtocolStateEntry) func(*badger.Txn) error { + return retrieve(makePrefix(codeEpochProtocolState, entryID), entry) +} + +// IndexEpochProtocolState indexes an epoch protocol state entry by block ID. +// Error returns: +// - storage.ErrAlreadyExists if the key already exists in the database. +// - generic error in case of unexpected failure from the database layer or encoding failure. +func IndexEpochProtocolState(blockID flow.Identifier, epochProtocolStateEntryID flow.Identifier) func(*badger.Txn) error { + return insert(makePrefix(codeEpochProtocolStateByBlockID, blockID), epochProtocolStateEntryID) +} + +// LookupEpochProtocolState finds an epoch protocol state entry ID by block ID. +// Error returns: +// - storage.ErrNotFound if the key does not exist in the database +// - generic error in case of unexpected failure from the database layer +func LookupEpochProtocolState(blockID flow.Identifier, epochProtocolStateEntryID *flow.Identifier) func(*badger.Txn) error { + return retrieve(makePrefix(codeEpochProtocolStateByBlockID, blockID), epochProtocolStateEntryID) +} diff --git a/storage/badger/operation/protocol_state_test.go b/storage/badger/operation/epoch_protocol_state_test.go similarity index 51% rename from storage/badger/operation/protocol_state_test.go rename to storage/badger/operation/epoch_protocol_state_test.go index 02917bcb5c1..8ae52298a60 100644 --- a/storage/badger/operation/protocol_state_test.go +++ b/storage/badger/operation/epoch_protocol_state_test.go @@ -12,28 +12,28 @@ import ( ) // TestInsertProtocolState tests if basic badger operations on EpochProtocolState work as expected. -func TestInsertProtocolState(t *testing.T) { +func TestInsertEpochProtocolState(t *testing.T) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { - expected := unittest.EpochStateFixture().ProtocolStateEntry + expected := unittest.EpochStateFixture().EpochProtocolStateEntry - protocolStateID := expected.ID() - err := db.Update(InsertProtocolState(protocolStateID, expected)) + epochProtocolStateEntryID := expected.ID() + err := db.Update(InsertEpochProtocolState(epochProtocolStateEntryID, expected)) require.Nil(t, err) - var actual flow.ProtocolStateEntry - err = db.View(RetrieveProtocolState(protocolStateID, &actual)) + var actual flow.EpochProtocolStateEntry + err = db.View(RetrieveEpochProtocolState(epochProtocolStateEntryID, &actual)) require.Nil(t, err) assert.Equal(t, expected, &actual) blockID := unittest.IdentifierFixture() - err = db.Update(IndexProtocolState(blockID, protocolStateID)) + err = db.Update(IndexEpochProtocolState(blockID, epochProtocolStateEntryID)) require.Nil(t, err) var actualProtocolStateID flow.Identifier - err = db.View(LookupProtocolState(blockID, &actualProtocolStateID)) + err = db.View(LookupEpochProtocolState(blockID, &actualProtocolStateID)) require.Nil(t, err) - assert.Equal(t, protocolStateID, actualProtocolStateID) + assert.Equal(t, epochProtocolStateEntryID, actualProtocolStateID) }) } diff --git a/storage/badger/operation/prefix.go b/storage/badger/operation/prefix.go index a6c01af4b06..ad909faf394 100644 --- a/storage/badger/operation/prefix.go +++ b/storage/badger/operation/prefix.go @@ -46,14 +46,14 @@ const ( codeExecutionReceiptMeta = 39 // NOTE: prior to Mainnet25, this erroneously had the same value as codeExecutionResult (36) // codes for indexing single identifier by identifier/integer - codeHeightToBlock = 40 // index mapping height to block ID - codeBlockIDToLatestSealID = 41 // index mapping a block its last payload seal - codeClusterBlockToRefBlock = 42 // index cluster block ID to reference block ID - codeRefHeightToClusterBlock = 43 // index reference block height to cluster block IDs - codeBlockIDToFinalizedSeal = 44 // index _finalized_ seal by sealed block ID - codeBlockIDToQuorumCertificate = 45 // index of quorum certificates by block ID - codeProtocolStateByBlockID = 46 // index of protocol state entry by block ID - codeProtocolKVStoreByBlockID = 47 // index of protocol KV store entry by block ID + codeHeightToBlock = 40 // index mapping height to block ID + codeBlockIDToLatestSealID = 41 // index mapping a block its last payload seal + codeClusterBlockToRefBlock = 42 // index cluster block ID to reference block ID + codeRefHeightToClusterBlock = 43 // index reference block height to cluster block IDs + codeBlockIDToFinalizedSeal = 44 // index _finalized_ seal by sealed block ID + codeBlockIDToQuorumCertificate = 45 // index of quorum certificates by block ID + codeEpochProtocolStateByBlockID = 46 // index of epoch protocol state entry by block ID + codeProtocolKVStoreByBlockID = 47 // index of protocol KV store entry by block ID // codes for indexing multiple identifiers by identifier codeBlockChildren = 50 // index mapping block ID to children blocks @@ -69,14 +69,14 @@ const ( codePayloadProtocolStateID = 60 // index mapping block ID to payload protocol state ID // codes related to protocol level information - codeEpochSetup = 61 // EpochSetup service event, keyed by ID - codeEpochCommit = 62 // EpochCommit service event, keyed by ID - codeBeaconPrivateKey = 63 // BeaconPrivateKey, keyed by epoch counter - codeDKGStarted = 64 // flag that the DKG for an epoch has been started - codeDKGEnded = 65 // flag that the DKG for an epoch has ended (stores end state) - codeVersionBeacon = 67 // flag for storing version beacons - codeProtocolState = 68 - codeProtocolKVStore = 69 + codeEpochSetup = 61 // EpochSetup service event, keyed by ID + codeEpochCommit = 62 // EpochCommit service event, keyed by ID + codeBeaconPrivateKey = 63 // BeaconPrivateKey, keyed by epoch counter + codeDKGStarted = 64 // flag that the DKG for an epoch has been started + codeDKGEnded = 65 // flag that the DKG for an epoch has ended (stores end state) + codeVersionBeacon = 67 // flag for storing version beacons + codeEpochProtocolState = 68 + codeProtocolKVStore = 69 // code for ComputationResult upload status storage // NOTE: for now only GCP uploader is supported. When other uploader (AWS e.g.) needs to diff --git a/storage/badger/operation/protocol_state.go b/storage/badger/operation/protocol_state.go deleted file mode 100644 index 3534a5b4679..00000000000 --- a/storage/badger/operation/protocol_state.go +++ /dev/null @@ -1,39 +0,0 @@ -package operation - -import ( - "github.com/dgraph-io/badger/v2" - - "github.com/onflow/flow-go/model/flow" -) - -// InsertProtocolState inserts a protocol state by ID. -// Error returns: -// - storage.ErrAlreadyExists if the key already exists in the database. -// - generic error in case of unexpected failure from the database layer or encoding failure. -func InsertProtocolState(protocolStateID flow.Identifier, protocolState *flow.ProtocolStateEntry) func(*badger.Txn) error { - return insert(makePrefix(codeProtocolState, protocolStateID), protocolState) -} - -// RetrieveProtocolState retrieves a protocol state by ID. -// Error returns: -// - storage.ErrNotFound if the key does not exist in the database -// - generic error in case of unexpected failure from the database layer -func RetrieveProtocolState(protocolStateID flow.Identifier, protocolState *flow.ProtocolStateEntry) func(*badger.Txn) error { - return retrieve(makePrefix(codeProtocolState, protocolStateID), protocolState) -} - -// IndexProtocolState indexes a protocol state by block ID. -// Error returns: -// - storage.ErrAlreadyExists if the key already exists in the database. -// - generic error in case of unexpected failure from the database layer or encoding failure. -func IndexProtocolState(blockID flow.Identifier, protocolStateID flow.Identifier) func(*badger.Txn) error { - return insert(makePrefix(codeProtocolStateByBlockID, blockID), protocolStateID) -} - -// LookupProtocolState finds protocol state ID by block ID. -// Error returns: -// - storage.ErrNotFound if the key does not exist in the database -// - generic error in case of unexpected failure from the database layer -func LookupProtocolState(blockID flow.Identifier, protocolStateID *flow.Identifier) func(*badger.Txn) error { - return retrieve(makePrefix(codeProtocolStateByBlockID, blockID), protocolStateID) -} diff --git a/storage/protocol_state.go b/storage/epoch_protocol_state.go similarity index 61% rename from storage/protocol_state.go rename to storage/epoch_protocol_state.go index 9fa905d3924..b3c241963d2 100644 --- a/storage/protocol_state.go +++ b/storage/epoch_protocol_state.go @@ -5,19 +5,20 @@ import ( "github.com/onflow/flow-go/storage/badger/transaction" ) -// ProtocolState represents persistent storage for protocol state entries. -type ProtocolState interface { +// EpochProtocolStateEntries represents persistent, fork-aware storage for the Epoch-related +// sub-state of the overall of the overall Protocol State (KV Store). +type EpochProtocolStateEntries interface { // StoreTx returns an anonymous function (intended to be executed as part of a badger transaction), - // which persists the given protocol state as part of a DB tx. Per convention, the identities in + // which persists the given epoch sub-state as part of a DB tx. Per convention, the identities in // the Protocol State must be in canonical order for the current and next epoch (if present), // otherwise an exception is returned. // Expected errors of the returned anonymous function: - // - storage.ErrAlreadyExists if a Protocol State with the given id is already stored - StoreTx(protocolStateID flow.Identifier, protocolState *flow.ProtocolStateEntry) func(*transaction.Tx) error + // - storage.ErrAlreadyExists if an epoch sub-state with the given id is already stored + StoreTx(epochProtocolStateID flow.Identifier, epochProtocolStateEntry *flow.EpochProtocolStateEntry) func(*transaction.Tx) error // Index returns an anonymous function that is intended to be executed as part of a database transaction. - // In a nutshell, we want to maintain a map from `blockID` to `protocolStateID`, where `blockID` references the - // block that _proposes_ the Protocol State. + // In a nutshell, we want to maintain a map from `blockID` to `epochProtocolStateID`, where `blockID` references the + // block that _proposes_ the epoch sub-state. // Upon call, the anonymous function persists the specific map entry in the node's database. // Protocol convention: // - Consider block B, whose ingestion might potentially lead to an updated protocol state. For example, @@ -28,15 +29,15 @@ type ProtocolState interface { // _after_ validating the QC. // // Expected errors during normal operations: - // - storage.ErrAlreadyExists if a Protocol State for the given blockID has already been indexed - Index(blockID flow.Identifier, protocolStateID flow.Identifier) func(*transaction.Tx) error + // - storage.ErrAlreadyExists if a epoch sub-state for the given blockID has already been indexed + Index(blockID flow.Identifier, epochProtocolStateID flow.Identifier) func(*transaction.Tx) error - // ByID returns the protocol state by its ID. + // ByID returns the flow.RichEpochProtocolStateEntry by its ID. // Expected errors during normal operations: - // - storage.ErrNotFound if no protocol state with the given Identifier is known. - ByID(id flow.Identifier) (*flow.RichProtocolStateEntry, error) + // - storage.ErrNotFound if no epoch state entry with the given Identifier is known. + ByID(id flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) - // ByBlockID retrieves the Protocol State that the block with the given ID proposes. + // ByBlockID retrieves the flow.RichEpochProtocolStateEntry that the block with the given ID proposes. // CAUTION: this protocol state requires confirmation by a QC and will only become active at the child block, // _after_ validating the QC. Protocol convention: // - Consider block B, whose ingestion might potentially lead to an updated protocol state. For example, @@ -47,6 +48,6 @@ type ProtocolState interface { // _after_ validating the QC. // // Expected errors during normal operations: - // - storage.ErrNotFound if no protocol state has been indexed for the given block. - ByBlockID(blockID flow.Identifier) (*flow.RichProtocolStateEntry, error) + // - storage.ErrNotFound if no epoch state entry has been indexed for the given block. + ByBlockID(blockID flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) } diff --git a/storage/mock/epoch_protocol_state_entries.go b/storage/mock/epoch_protocol_state_entries.go new file mode 100644 index 00000000000..8e9f546616a --- /dev/null +++ b/storage/mock/epoch_protocol_state_entries.go @@ -0,0 +1,114 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" + + transaction "github.com/onflow/flow-go/storage/badger/transaction" +) + +// EpochProtocolStateEntries is an autogenerated mock type for the EpochProtocolStateEntries type +type EpochProtocolStateEntries struct { + mock.Mock +} + +// ByBlockID provides a mock function with given fields: blockID +func (_m *EpochProtocolStateEntries) ByBlockID(blockID flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) { + ret := _m.Called(blockID) + + var r0 *flow.RichEpochProtocolStateEntry + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier) (*flow.RichEpochProtocolStateEntry, error)); ok { + return rf(blockID) + } + if rf, ok := ret.Get(0).(func(flow.Identifier) *flow.RichEpochProtocolStateEntry); ok { + r0 = rf(blockID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.RichEpochProtocolStateEntry) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { + r1 = rf(blockID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ByID provides a mock function with given fields: id +func (_m *EpochProtocolStateEntries) ByID(id flow.Identifier) (*flow.RichEpochProtocolStateEntry, error) { + ret := _m.Called(id) + + var r0 *flow.RichEpochProtocolStateEntry + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier) (*flow.RichEpochProtocolStateEntry, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(flow.Identifier) *flow.RichEpochProtocolStateEntry); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.RichEpochProtocolStateEntry) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Index provides a mock function with given fields: blockID, epochProtocolStateID +func (_m *EpochProtocolStateEntries) Index(blockID flow.Identifier, epochProtocolStateID flow.Identifier) func(*transaction.Tx) error { + ret := _m.Called(blockID, epochProtocolStateID) + + var r0 func(*transaction.Tx) error + if rf, ok := ret.Get(0).(func(flow.Identifier, flow.Identifier) func(*transaction.Tx) error); ok { + r0 = rf(blockID, epochProtocolStateID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(func(*transaction.Tx) error) + } + } + + return r0 +} + +// StoreTx provides a mock function with given fields: epochProtocolStateID, epochProtocolStateEntry +func (_m *EpochProtocolStateEntries) StoreTx(epochProtocolStateID flow.Identifier, epochProtocolStateEntry *flow.EpochProtocolStateEntry) func(*transaction.Tx) error { + ret := _m.Called(epochProtocolStateID, epochProtocolStateEntry) + + var r0 func(*transaction.Tx) error + if rf, ok := ret.Get(0).(func(flow.Identifier, *flow.EpochProtocolStateEntry) func(*transaction.Tx) error); ok { + r0 = rf(epochProtocolStateID, epochProtocolStateEntry) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(func(*transaction.Tx) error) + } + } + + return r0 +} + +type mockConstructorTestingTNewEpochProtocolStateEntries interface { + mock.TestingT + Cleanup(func()) +} + +// NewEpochProtocolStateEntries creates a new instance of EpochProtocolStateEntries. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEpochProtocolStateEntries(t mockConstructorTestingTNewEpochProtocolStateEntries) *EpochProtocolStateEntries { + mock := &EpochProtocolStateEntries{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/storage/mock/protocol_state.go b/storage/mock/protocol_state.go deleted file mode 100644 index 407bbc8a699..00000000000 --- a/storage/mock/protocol_state.go +++ /dev/null @@ -1,114 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mock - -import ( - flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" - - transaction "github.com/onflow/flow-go/storage/badger/transaction" -) - -// ProtocolState is an autogenerated mock type for the ProtocolState type -type ProtocolState struct { - mock.Mock -} - -// ByBlockID provides a mock function with given fields: blockID -func (_m *ProtocolState) ByBlockID(blockID flow.Identifier) (*flow.RichProtocolStateEntry, error) { - ret := _m.Called(blockID) - - var r0 *flow.RichProtocolStateEntry - var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) (*flow.RichProtocolStateEntry, error)); ok { - return rf(blockID) - } - if rf, ok := ret.Get(0).(func(flow.Identifier) *flow.RichProtocolStateEntry); ok { - r0 = rf(blockID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.RichProtocolStateEntry) - } - } - - if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { - r1 = rf(blockID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ByID provides a mock function with given fields: id -func (_m *ProtocolState) ByID(id flow.Identifier) (*flow.RichProtocolStateEntry, error) { - ret := _m.Called(id) - - var r0 *flow.RichProtocolStateEntry - var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) (*flow.RichProtocolStateEntry, error)); ok { - return rf(id) - } - if rf, ok := ret.Get(0).(func(flow.Identifier) *flow.RichProtocolStateEntry); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.RichProtocolStateEntry) - } - } - - if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Index provides a mock function with given fields: blockID, protocolStateID -func (_m *ProtocolState) Index(blockID flow.Identifier, protocolStateID flow.Identifier) func(*transaction.Tx) error { - ret := _m.Called(blockID, protocolStateID) - - var r0 func(*transaction.Tx) error - if rf, ok := ret.Get(0).(func(flow.Identifier, flow.Identifier) func(*transaction.Tx) error); ok { - r0 = rf(blockID, protocolStateID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(func(*transaction.Tx) error) - } - } - - return r0 -} - -// StoreTx provides a mock function with given fields: protocolStateID, protocolState -func (_m *ProtocolState) StoreTx(protocolStateID flow.Identifier, protocolState *flow.ProtocolStateEntry) func(*transaction.Tx) error { - ret := _m.Called(protocolStateID, protocolState) - - var r0 func(*transaction.Tx) error - if rf, ok := ret.Get(0).(func(flow.Identifier, *flow.ProtocolStateEntry) func(*transaction.Tx) error); ok { - r0 = rf(protocolStateID, protocolState) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(func(*transaction.Tx) error) - } - } - - return r0 -} - -type mockConstructorTestingTNewProtocolState interface { - mock.TestingT - Cleanup(func()) -} - -// NewProtocolState creates a new instance of ProtocolState. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProtocolState(t mockConstructorTestingTNewProtocolState) *ProtocolState { - mock := &ProtocolState{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index cfde1db2b01..cc2a176b920 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -2228,7 +2228,7 @@ func BootstrapFixtureWithChainID( WithDKGFromParticipants(participants.ToSkeleton()), ) - rootEpochState := inmem.ProtocolStateFromEpochServiceEvents(setup, commit) + rootEpochState := inmem.EpochProtocolStateFromServiceEvents(setup, commit) rootProtocolStateID := kvstore.NewDefaultKVStore(rootEpochState.ID()).ID() root.SetPayload(flow.Payload{ProtocolStateID: rootProtocolStateID}) stateCommit := GenesisStateCommitmentByChainID(chainID) @@ -2641,9 +2641,10 @@ func ChunkExecutionDataFixture(t *testing.T, minSize int, opts ...func(*executio } } -// RootProtocolStateFixture creates a fixture with correctly structured data for root protocol state. +// RootEpochProtocolStateFixture creates a fixture with correctly structured Epoch sub-state. +// The epoch substate is part of the overall protocol state (KV store). // This can be useful for testing bootstrap when there is no previous epoch. -func RootProtocolStateFixture() *flow.RichProtocolStateEntry { +func RootEpochProtocolStateFixture() *flow.RichEpochProtocolStateEntry { currentEpochSetup := EpochSetupFixture(func(setup *flow.EpochSetup) { setup.Counter = 1 }) @@ -2660,8 +2661,8 @@ func RootProtocolStateFixture() *flow.RichProtocolStateEntry { }, }) } - return &flow.RichProtocolStateEntry{ - ProtocolStateEntry: &flow.ProtocolStateEntry{ + return &flow.RichEpochProtocolStateEntry{ + EpochProtocolStateEntry: &flow.EpochProtocolStateEntry{ PreviousEpoch: nil, CurrentEpoch: flow.EpochStateContainer{ SetupID: currentEpochSetup.ID(), @@ -2693,7 +2694,7 @@ func RootProtocolStateFixture() *flow.RichProtocolStateEntry { // - Epoch setup and commit counters are set to match. // - Identities are constructed from setup events. // - Identities are sorted in canonical order. -func EpochStateFixture(options ...func(*flow.RichProtocolStateEntry)) *flow.RichProtocolStateEntry { +func EpochStateFixture(options ...func(*flow.RichEpochProtocolStateEntry)) *flow.RichEpochProtocolStateEntry { prevEpochSetup := EpochSetupFixture() prevEpochCommit := EpochCommitFixture(func(commit *flow.EpochCommit) { commit.Counter = prevEpochSetup.Counter @@ -2727,8 +2728,8 @@ func EpochStateFixture(options ...func(*flow.RichProtocolStateEntry)) *flow.Rich allIdentities := currentEpochIdentities.Union( prevEpochIdentities.Map(mapfunc.WithEpochParticipationStatus(flow.EpochParticipationStatusLeaving))) - entry := &flow.RichProtocolStateEntry{ - ProtocolStateEntry: &flow.ProtocolStateEntry{ + entry := &flow.RichEpochProtocolStateEntry{ + EpochProtocolStateEntry: &flow.EpochProtocolStateEntry{ CurrentEpoch: flow.EpochStateContainer{ SetupID: currentEpochSetup.ID(), CommitID: currentEpochCommit.ID(), @@ -2764,8 +2765,8 @@ func EpochStateFixture(options ...func(*flow.RichProtocolStateEntry)) *flow.Rich // - We are currently in Epoch N. // - The previous epoch N-1 is known (specifically EpochSetup and EpochCommit events). // - The network has completed the epoch setup phase, i.e. published the EpochSetup and EpochCommit events for epoch N+1. -func WithNextEpochProtocolState() func(entry *flow.RichProtocolStateEntry) { - return func(entry *flow.RichProtocolStateEntry) { +func WithNextEpochProtocolState() func(entry *flow.RichEpochProtocolStateEntry) { + return func(entry *flow.RichEpochProtocolStateEntry) { nextEpochSetup := EpochSetupFixture(func(setup *flow.EpochSetup) { setup.Counter = entry.CurrentEpochSetup.Counter + 1 setup.FirstView = entry.CurrentEpochSetup.FinalView + 1 @@ -2811,8 +2812,8 @@ func WithNextEpochProtocolState() func(entry *flow.RichProtocolStateEntry) { } // WithValidDKG updated protocol state with correctly structured data for DKG. -func WithValidDKG() func(*flow.RichProtocolStateEntry) { - return func(entry *flow.RichProtocolStateEntry) { +func WithValidDKG() func(*flow.RichEpochProtocolStateEntry) { + return func(entry *flow.RichEpochProtocolStateEntry) { commit := entry.CurrentEpochCommit dkgParticipants := entry.CurrentEpochSetup.Participants.Filter(filter.IsValidDKGParticipant) lookup := DKGParticipantLookup(dkgParticipants)