From c53b1cd425f691071316a6cf92ad1644af5e258b Mon Sep 17 00:00:00 2001 From: Diego Romero Date: Wed, 3 May 2023 21:31:09 -0300 Subject: [PATCH] feat(telemetry): Add scheduled and force changes telemetry metrics (#3226) --- dot/state/grandpa.go | 19 ++++-- dot/state/grandpa_test.go | 60 ++++++++++++++----- dot/state/initialize.go | 2 +- dot/state/service.go | 3 +- ...fg_applying_forced_authority_set_change.go | 40 +++++++++++++ ...applying_scheduled_authority_set_change.go | 40 +++++++++++++ dot/telemetry/telemetry.go | 12 ++-- lib/grandpa/helpers_integration_test.go | 2 +- 8 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 dot/telemetry/afg_applying_forced_authority_set_change.go create mode 100644 dot/telemetry/afg_applying_scheduled_authority_set_change.go diff --git a/dot/state/grandpa.go b/dot/state/grandpa.go index a4ae7bce33..68c5ee9d8f 100644 --- a/dot/state/grandpa.go +++ b/dot/state/grandpa.go @@ -7,8 +7,10 @@ import ( "encoding/binary" "errors" "fmt" + "strconv" "github.com/ChainSafe/chaindb" + "github.com/ChainSafe/gossamer/dot/telemetry" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/pkg/scale" @@ -40,17 +42,19 @@ type GrandpaState struct { forcedChanges *orderedPendingChanges scheduledChangeRoots *changeTree + telemetry Telemetry } // NewGrandpaStateFromGenesis returns a new GrandpaState given the grandpa genesis authorities func NewGrandpaStateFromGenesis(db *chaindb.BadgerDB, bs *BlockState, - genesisAuthorities []types.GrandpaVoter) (*GrandpaState, error) { + genesisAuthorities []types.GrandpaVoter, telemetry Telemetry) (*GrandpaState, error) { grandpaDB := chaindb.NewTable(db, grandpaPrefix) s := &GrandpaState{ db: grandpaDB, blockState: bs, scheduledChangeRoots: new(changeTree), forcedChanges: new(orderedPendingChanges), + telemetry: telemetry, } if err := s.setCurrentSetID(genesisSetID); err != nil { @@ -73,12 +77,13 @@ func NewGrandpaStateFromGenesis(db *chaindb.BadgerDB, bs *BlockState, } // NewGrandpaState returns a new GrandpaState -func NewGrandpaState(db *chaindb.BadgerDB, bs *BlockState) *GrandpaState { +func NewGrandpaState(db *chaindb.BadgerDB, bs *BlockState, telemetry Telemetry) *GrandpaState { return &GrandpaState{ db: chaindb.NewTable(db, grandpaPrefix), blockState: bs, scheduledChangeRoots: new(changeTree), forcedChanges: new(orderedPendingChanges), + telemetry: telemetry, } } @@ -194,7 +199,10 @@ func (s *GrandpaState) ApplyScheduledChanges(finalizedHeader *types.Header) erro logger.Debugf("Applying authority set change scheduled at block #%d", changeToApply.change.announcingHeader.Number) - // TODO(#3218): add afg.applying_scheduled_authority_set_change telemetry info here + canonHeightString := strconv.FormatUint(uint64(changeToApply.change.announcingHeader.Number), 10) + s.telemetry.SendMessage(telemetry.NewAfgApplyingScheduledAuthoritySetChange( + canonHeightString, + )) return nil } @@ -229,7 +237,10 @@ func (s *GrandpaState) ApplyForcedChanges(importedBlockHeader *types.Header) err logger.Debugf("Applying authority set forced change: %s", forcedChange) - // TODO(#3218) afg.applying_forced_authority_set_change + canonHeightString := strconv.FormatUint(uint64(forcedChange.announcingHeader.Number), 10) + s.telemetry.SendMessage(telemetry.NewAfgApplyingForcedAuthoritySetChange( + canonHeightString, + )) currentSetID, err := s.GetCurrentSetID() if err != nil { diff --git a/dot/state/grandpa_test.go b/dot/state/grandpa_test.go index f13f7900f4..1b8531df29 100644 --- a/dot/state/grandpa_test.go +++ b/dot/state/grandpa_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/ChainSafe/chaindb" + "github.com/ChainSafe/gossamer/dot/telemetry" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/crypto" @@ -30,7 +31,7 @@ var ( func TestNewGrandpaStateFromGenesis(t *testing.T) { db := NewInMemoryDB(t) - gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths) + gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths, nil) require.NoError(t, err) currSetID, err := gs.GetCurrentSetID() @@ -48,7 +49,7 @@ func TestNewGrandpaStateFromGenesis(t *testing.T) { func TestGrandpaState_SetNextChange(t *testing.T) { db := NewInMemoryDB(t) - gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths) + gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths, nil) require.NoError(t, err) err = gs.SetNextChange(testAuths, 1) @@ -65,7 +66,7 @@ func TestGrandpaState_SetNextChange(t *testing.T) { func TestGrandpaState_IncrementSetID(t *testing.T) { db := NewInMemoryDB(t) - gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths) + gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths, nil) require.NoError(t, err) setID, err := gs.IncrementSetID() @@ -75,7 +76,7 @@ func TestGrandpaState_IncrementSetID(t *testing.T) { func TestGrandpaState_GetSetIDByBlockNumber(t *testing.T) { db := NewInMemoryDB(t) - gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths) + gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths, nil) require.NoError(t, err) err = gs.SetNextChange(testAuths, 100) @@ -108,7 +109,7 @@ func TestGrandpaState_GetSetIDByBlockNumber(t *testing.T) { func TestGrandpaState_LatestRound(t *testing.T) { db := NewInMemoryDB(t) - gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths) + gs, err := NewGrandpaStateFromGenesis(db, nil, testAuths, nil) require.NoError(t, err) r, err := gs.GetLatestRound() @@ -126,7 +127,7 @@ func TestGrandpaState_LatestRound(t *testing.T) { func testBlockState(t *testing.T, db *chaindb.BadgerDB) *BlockState { ctrl := gomock.NewController(t) telemetryMock := NewMockTelemetry(ctrl) - telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes() + telemetryMock.EXPECT().SendMessage(gomock.AssignableToTypeOf(&telemetry.NotifyFinalized{})).Times(1) header := testGenesisHeader bs, err := NewBlockStateFromGenesis(db, newTriesEmpty(), header, telemetryMock) @@ -151,7 +152,7 @@ func TestAddScheduledChangesKeepTheRightForkTree(t *testing.T) { //nolint:tparal db := NewInMemoryDB(t) blockState := testBlockState(t, db) - gs, err := NewGrandpaStateFromGenesis(db, blockState, nil) + gs, err := NewGrandpaStateFromGenesis(db, blockState, nil, nil) require.NoError(t, err) /* @@ -287,7 +288,7 @@ func TestForcedScheduledChangesOrder(t *testing.T) { db := NewInMemoryDB(t) blockState := testBlockState(t, db) - gs, err := NewGrandpaStateFromGenesis(db, blockState, nil) + gs, err := NewGrandpaStateFromGenesis(db, blockState, nil, nil) require.NoError(t, err) aliceHeaders := issueBlocksWithBABEPrimary(t, keyring.KeyAlice, gs.blockState, @@ -353,7 +354,7 @@ func TestShouldNotAddMoreThanOneForcedChangeInTheSameFork(t *testing.T) { db := NewInMemoryDB(t) blockState := testBlockState(t, db) - gs, err := NewGrandpaStateFromGenesis(db, blockState, nil) + gs, err := NewGrandpaStateFromGenesis(db, blockState, nil, nil) require.NoError(t, err) aliceHeaders := issueBlocksWithBABEPrimary(t, keyring.KeyAlice, gs.blockState, @@ -531,7 +532,7 @@ func TestNextGrandpaAuthorityChange(t *testing.T) { db := NewInMemoryDB(t) blockState := testBlockState(t, db) - gs, err := NewGrandpaStateFromGenesis(db, blockState, nil) + gs, err := NewGrandpaStateFromGenesis(db, blockState, nil, nil) require.NoError(t, err) const sizeOfChain = 10 @@ -608,6 +609,7 @@ func TestApplyForcedChanges(t *testing.T) { generateForks func(t *testing.T, blockState *BlockState) [][]*types.Header changes func(*GrandpaState, [][]*types.Header) + telemetryMock *MockTelemetry }{ "no_forced_changes": { generateForks: genericForks, @@ -615,6 +617,7 @@ func TestApplyForcedChanges(t *testing.T) { expectedSetID: 0, expectedGRANDPAAuthoritySet: genesisGrandpaVoters, expectedPruning: false, + telemetryMock: nil, }, "apply_forced_change_without_pending_scheduled_changes": { generateForks: genericForks, @@ -649,6 +652,14 @@ func TestApplyForcedChanges(t *testing.T) { {Key: keyring.KeyBob.Public().(*sr25519.PublicKey).AsBytes()}, {Key: keyring.KeyDave.Public().(*sr25519.PublicKey).AsBytes()}, }, + telemetryMock: func() *MockTelemetry { + ctrl := gomock.NewController(t) + + telemetryMock := NewMockTelemetry(ctrl) + telemetryMock.EXPECT().SendMessage(gomock.Eq(&telemetry.AfgApplyingForcedAuthoritySetChange{Block: "8"})).Times(1) + + return telemetryMock + }(), }, "import_block_before_forced_change_should_do_nothing": { generateForks: genericForks, @@ -668,6 +679,7 @@ func TestApplyForcedChanges(t *testing.T) { expectedSetID: 0, expectedPruning: false, expectedGRANDPAAuthoritySet: genesisGrandpaVoters, + telemetryMock: nil, }, "import_block_from_another_fork_should_do_nothing": { generateForks: genericForks, @@ -687,6 +699,7 @@ func TestApplyForcedChanges(t *testing.T) { expectedSetID: 0, expectedPruning: false, expectedGRANDPAAuthoritySet: genesisGrandpaVoters, + telemetryMock: nil, }, "apply_forced_change_with_pending_scheduled_changes_should_fail": { generateForks: genericForks, @@ -726,8 +739,8 @@ func TestApplyForcedChanges(t *testing.T) { expectedGRANDPAAuthoritySet: genesisGrandpaVoters, expectedSetID: 0, expectedPruning: false, + telemetryMock: nil, }, - "apply_forced_change_should_prune_scheduled_changes": { generateForks: genericForks, changes: func(gs *GrandpaState, headers [][]*types.Header) { @@ -763,6 +776,7 @@ func TestApplyForcedChanges(t *testing.T) { expectedGRANDPAAuthoritySet: genesisGrandpaVoters, expectedSetID: 0, expectedPruning: false, + telemetryMock: nil, }, } @@ -776,7 +790,7 @@ func TestApplyForcedChanges(t *testing.T) { blockState := testBlockState(t, db) voters := types.NewGrandpaVotersFromAuthorities(genesisAuths) - gs, err := NewGrandpaStateFromGenesis(db, blockState, voters) + gs, err := NewGrandpaStateFromGenesis(db, blockState, voters, tt.telemetryMock) require.NoError(t, err) forks := tt.generateForks(t, blockState) @@ -898,7 +912,7 @@ func TestApplyScheduledChangesKeepDescendantForcedChanges(t *testing.T) { }, }) }, - finalizedHeader: [2]int{0, 3}, //finalize header number 4 from chain A + finalizedHeader: [2]int{0, 3}, // finalize header number 4 from chain A }, } @@ -911,7 +925,7 @@ func TestApplyScheduledChangesKeepDescendantForcedChanges(t *testing.T) { blockState := testBlockState(t, db) voters := types.NewGrandpaVotersFromAuthorities(genesisAuths) - gs, err := NewGrandpaStateFromGenesis(db, blockState, voters) + gs, err := NewGrandpaStateFromGenesis(db, blockState, voters, nil) require.NoError(t, err) forks := tt.generateForks(t, gs.blockState) @@ -1140,7 +1154,7 @@ func TestApplyScheduledChangeGetApplicableChange(t *testing.T) { blockState := testBlockState(t, db) voters := types.NewGrandpaVotersFromAuthorities(genesisAuths) - gs, err := NewGrandpaStateFromGenesis(db, blockState, voters) + gs, err := NewGrandpaStateFromGenesis(db, blockState, voters, nil) require.NoError(t, err) forks := tt.generateForks(t, gs.blockState) @@ -1222,6 +1236,7 @@ func TestApplyScheduledChange(t *testing.T) { expectedSetID uint64 expectedAuthoritySet []types.GrandpaVoter changeSetIDAt uint + telemetryMock *MockTelemetry }{ "empty_scheduled_changes_only_update_the_forced_changes": { generateForks: genericForks, @@ -1254,6 +1269,7 @@ func TestApplyScheduledChange(t *testing.T) { auths, _ := types.GrandpaAuthoritiesRawToAuthorities(genesisGrandpaVoters) return types.NewGrandpaVotersFromAuthorities(auths) }(), + telemetryMock: nil, }, "pending_scheduled_changes_should_return_error": { generateForks: genericForks, @@ -1288,6 +1304,7 @@ func TestApplyScheduledChange(t *testing.T) { auths, _ := types.GrandpaAuthoritiesRawToAuthorities(genesisGrandpaVoters) return types.NewGrandpaVotersFromAuthorities(auths) }(), + telemetryMock: nil, }, "no_changes_to_apply_should_only_update_the_scheduled_roots": { generateForks: genericForks, @@ -1318,6 +1335,7 @@ func TestApplyScheduledChange(t *testing.T) { auths, _ := types.GrandpaAuthoritiesRawToAuthorities(genesisGrandpaVoters) return types.NewGrandpaVotersFromAuthorities(auths) }(), + telemetryMock: nil, }, "apply_scheduled_change_should_change_voters_and_set_id": { generateForks: genericForks, @@ -1355,6 +1373,16 @@ func TestApplyScheduledChange(t *testing.T) { }) return types.NewGrandpaVotersFromAuthorities(auths) }(), + telemetryMock: func() *MockTelemetry { + ctrl := gomock.NewController(t) + + telemetryMock := NewMockTelemetry(ctrl) + telemetryMock.EXPECT().SendMessage( + gomock.Eq(&telemetry.AfgApplyingScheduledAuthoritySetChange{Block: "6"}), + ).Times(1) + + return telemetryMock + }(), }, } @@ -1370,7 +1398,7 @@ func TestApplyScheduledChange(t *testing.T) { require.NoError(t, err) voters := types.NewGrandpaVotersFromAuthorities(genesisAuths) - gs, err := NewGrandpaStateFromGenesis(db, blockState, voters) + gs, err := NewGrandpaStateFromGenesis(db, blockState, voters, tt.telemetryMock) require.NoError(t, err) forks := tt.generateForks(t, gs.blockState) diff --git a/dot/state/initialize.go b/dot/state/initialize.go index 3cdb932bb8..0aafdeb32d 100644 --- a/dot/state/initialize.go +++ b/dot/state/initialize.go @@ -87,7 +87,7 @@ func (s *Service) Initialise(gen *genesis.Genesis, header *types.Header, t *trie return fmt.Errorf("failed to load grandpa authorities: %w", err) } - grandpaState, err := NewGrandpaStateFromGenesis(db, blockState, grandpaAuths) + grandpaState, err := NewGrandpaStateFromGenesis(db, blockState, grandpaAuths, s.Telemetry) if err != nil { return fmt.Errorf("failed to create grandpa state: %s", err) } diff --git a/dot/state/service.go b/dot/state/service.go index e75a5daa6d..97859764fc 100644 --- a/dot/state/service.go +++ b/dot/state/service.go @@ -151,7 +151,7 @@ func (s *Service) Start() (err error) { return fmt.Errorf("failed to create epoch state: %w", err) } - s.Grandpa = NewGrandpaState(s.db, s.Block) + s.Grandpa = NewGrandpaState(s.db, s.Block, s.Telemetry) num, _ := s.Block.BestBlockNumber() logger.Infof( "created state service with head %s, highest number %d and genesis hash %s", @@ -233,7 +233,6 @@ func (s *Service) Rewind(toBlock uint) error { } } - //return s.Base.StoreBestBlockHash(newHead) return nil } diff --git a/dot/telemetry/afg_applying_forced_authority_set_change.go b/dot/telemetry/afg_applying_forced_authority_set_change.go new file mode 100644 index 0000000000..3b2fcd346c --- /dev/null +++ b/dot/telemetry/afg_applying_forced_authority_set_change.go @@ -0,0 +1,40 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package telemetry + +import ( + "encoding/json" + "time" +) + +type afgApplyingForcedAuthoritySetChange AfgApplyingForcedAuthoritySetChange + +var _ json.Marshaler = (*AfgApplyingForcedAuthoritySetChange)(nil) + +// AfgApplyingForcedAuthoritySetChange is a telemetry message of type `afg.applying_forced_authority_set_change` +// which is meant to be sent when a forced change is applied +type AfgApplyingForcedAuthoritySetChange struct { + Block string `json:"block"` +} + +// NewAfgApplyingForcedAuthoritySetChange creates a new AfgAuthoritySetTM struct. +func NewAfgApplyingForcedAuthoritySetChange(block string) *AfgApplyingForcedAuthoritySetChange { + return &AfgApplyingForcedAuthoritySetChange{ + Block: block, + } +} + +func (afg AfgApplyingForcedAuthoritySetChange) MarshalJSON() ([]byte, error) { + telemetryData := struct { + afgApplyingForcedAuthoritySetChange + MessageType string `json:"msg"` + Timestamp time.Time `json:"ts"` + }{ + afgApplyingForcedAuthoritySetChange: afgApplyingForcedAuthoritySetChange(afg), + MessageType: afgApplyingForcedAuthoritySetChangeMsg, + Timestamp: time.Now(), + } + + return json.Marshal(telemetryData) +} diff --git a/dot/telemetry/afg_applying_scheduled_authority_set_change.go b/dot/telemetry/afg_applying_scheduled_authority_set_change.go new file mode 100644 index 0000000000..e257a19427 --- /dev/null +++ b/dot/telemetry/afg_applying_scheduled_authority_set_change.go @@ -0,0 +1,40 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package telemetry + +import ( + "encoding/json" + "time" +) + +type afgApplyingScheduledAuthoritySetChange AfgApplyingScheduledAuthoritySetChange + +var _ json.Marshaler = (*AfgApplyingScheduledAuthoritySetChange)(nil) + +// AfgApplyingScheduledAuthoritySetChange is a telemetry message of type `afg.applying_scheduled_authority_set_change` +// which is meant to be sent when a scheduled change is applied +type AfgApplyingScheduledAuthoritySetChange struct { + Block string `json:"block"` +} + +// NewAfgApplyingScheduledAuthoritySetChange creates a new AfgAuthoritySetTM struct. +func NewAfgApplyingScheduledAuthoritySetChange(block string) *AfgApplyingScheduledAuthoritySetChange { + return &AfgApplyingScheduledAuthoritySetChange{ + Block: block, + } +} + +func (afg AfgApplyingScheduledAuthoritySetChange) MarshalJSON() ([]byte, error) { + telemetryData := struct { + afgApplyingScheduledAuthoritySetChange + MessageType string `json:"msg"` + Timestamp time.Time `json:"ts"` + }{ + afgApplyingScheduledAuthoritySetChange: afgApplyingScheduledAuthoritySetChange(afg), + MessageType: afgApplyingScheduledAuthoritySetChangeMsg, + Timestamp: time.Now(), + } + + return json.Marshal(telemetryData) +} diff --git a/dot/telemetry/telemetry.go b/dot/telemetry/telemetry.go index ac777b244c..7b813aa6b3 100644 --- a/dot/telemetry/telemetry.go +++ b/dot/telemetry/telemetry.go @@ -7,11 +7,13 @@ import "encoding/json" // telemetry message types const ( - afgAuthoritySetMsg = "afg.authority_set" - afgFinalizedBlocksUpToMsg = "afg.finalized_blocks_up_to" - afgReceivedCommitMsg = "afg.received_commit" - afgReceivedPrecommitMsg = "afg.received_precommit" - afgReceivedPrevoteMsg = "afg.received_prevote" + afgAuthoritySetMsg = "afg.authority_set" + afgFinalizedBlocksUpToMsg = "afg.finalized_blocks_up_to" + afgReceivedCommitMsg = "afg.received_commit" + afgReceivedPrecommitMsg = "afg.received_precommit" + afgReceivedPrevoteMsg = "afg.received_prevote" + afgApplyingScheduledAuthoritySetChangeMsg = "afg.applying_scheduled_authority_set_change" + afgApplyingForcedAuthoritySetChangeMsg = "afg.applying_forced_authority_set_change" blockImportMsg = "block.import" diff --git a/lib/grandpa/helpers_integration_test.go b/lib/grandpa/helpers_integration_test.go index 98cd9d1923..9acf420bc9 100644 --- a/lib/grandpa/helpers_integration_test.go +++ b/lib/grandpa/helpers_integration_test.go @@ -151,7 +151,7 @@ func newTestState(t *testing.T) *state.Service { require.NoError(t, err) block.StoreRuntime(block.BestBlockHash(), rt) - grandpa, err := state.NewGrandpaStateFromGenesis(db, nil, newTestVoters(t)) + grandpa, err := state.NewGrandpaStateFromGenesis(db, nil, newTestVoters(t), telemetryMock) require.NoError(t, err) return &state.Service{