Skip to content

fix(lib/grandpa): capped number of tracked commit messages #2490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions lib/grandpa/commits_tracker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2022 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package grandpa

import (
"container/list"

"github.com/ChainSafe/gossamer/lib/common"
)

// commitsTracker tracks vote messages that could
// not be processed, and removes the oldest ones once
// its maximum capacity is reached.
// It is NOT THREAD SAFE to use.
type commitsTracker struct {
// map of commit block hash to linked list commit message.
mapping map[common.Hash]*list.Element
// double linked list of commit messages
// to track the order commit messages were added in.
linkedList *list.List
capacity int
}

// newCommitsTracker creates a new commit messages tracker
// with the capacity specified.
func newCommitsTracker(capacity int) commitsTracker {
return commitsTracker{
mapping: make(map[common.Hash]*list.Element, capacity),
linkedList: list.New(),
capacity: capacity,
}
}

// add adds a commit message to the commit message tracker.
// If the commit message tracker capacity is reached,
// the oldest commit message is removed.
func (ct *commitsTracker) add(commitMessage *CommitMessage) {
blockHash := commitMessage.Vote.Hash

listElement, has := ct.mapping[blockHash]
if has {
// commit already exists so override the commit message in the linked list;
// do not move the list element in the linked list to avoid
// someone re-sending the same commit message and going at the
// front of the list, hence erasing other possible valid commit messages
// in the tracker.
listElement.Value = commitMessage
return
}

// add new block hash in tracker
ct.cleanup()
listElement = ct.linkedList.PushFront(commitMessage)
ct.mapping[blockHash] = listElement
}

// cleanup removes the oldest commit message from the tracker
// if the number of commit messages is at the tracker capacity.
// This method is designed to be called automatically from the
// add method and should not be called elsewhere.
func (ct *commitsTracker) cleanup() {
if ct.linkedList.Len() < ct.capacity {
return
}

oldestElement := ct.linkedList.Back()
ct.linkedList.Remove(oldestElement)

oldestCommitMessage := oldestElement.Value.(*CommitMessage)
oldestBlockHash := oldestCommitMessage.Vote.Hash
delete(ct.mapping, oldestBlockHash)
}

// delete deletes all the vote messages for a particular
// block hash from the vote messages tracker.
func (ct *commitsTracker) delete(blockHash common.Hash) {
listElement, has := ct.mapping[blockHash]
if !has {
return
}

ct.linkedList.Remove(listElement)
delete(ct.mapping, blockHash)
}

// message returns a pointer to the
// commit message for a particular block hash from
// the tracker. It returns nil if the block hash
// does not exist in the tracker
func (ct *commitsTracker) message(blockHash common.Hash) (
message *CommitMessage) {
listElement, ok := ct.mapping[blockHash]
if !ok {
return nil
}

return listElement.Value.(*CommitMessage)
}

// forEach runs the function `f` on each
// commit message stored in the tracker.
func (ct *commitsTracker) forEach(f func(message *CommitMessage)) {
for _, data := range ct.mapping {
message := data.Value.(*CommitMessage)
f(message)
}
}
Loading