Skip to content
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

chore(lib/grandpa): update grandpa message tracker to store messages with map of maps, also store commit messages #1769

Merged
merged 34 commits into from
Aug 30, 2021

Conversation

noot
Copy link
Contributor

@noot noot commented Aug 25, 2021

Changes

  • update grandpa message tracker to store vote messages with map of maps to prevent duplicates
  • also update message tracker to store commit messages (that we haven't synced the block for yet), so when we sync it, we can handle the commit message
  • minor sync fixes

Tests

go test ./lib/grandpa -short

Issues

Primary Reviewer

@noot noot requested a review from arijitAD as a code owner August 25, 2021 18:57
dot/network/sync.go Outdated Show resolved Hide resolved
dot/network/sync.go Outdated Show resolved Hide resolved
@noot noot requested a review from EclesioMeloJunior August 25, 2021 23:40
}

func TestMessageTracker_SendMessage(t *testing.T) {
kr, err := keystore.NewEd25519Keyring()
require.NoError(t, err)

gs, in, _, _ := setupGrandpa(t, kr.Bob().(*ed25519.Keypair))
fmt.Println(len(in))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if goal-int64(start) < int64(blockRequestSize) {
start := best.Int64() + 1
req := createBlockRequest(start, 0)
req := createBlockRequest(start, uint32(goal)-uint32(start))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint32(goal - start)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we merge this and the below code and decide on the request size and make the request code common?

	var requestSize int32
	if goal-int64(start) < int64(blockRequestSize) {
		start := best.Int64() + 1
		requestSize = int32(goal - start)
	} else {
		m := start % uint64(blockRequestSize)
		start = start - m + 1
		requestSize = ...
	}

	for i := 0; i < numRequests; i++ {
		if start > uint64(goal) {
			return
		}
		
		if requestSize / blockRequestSize == 0 {
			currentRequestSize = requestSize
		} else {
			currentRequestSize = blockRequestSize
		}
		
		... // logic
		start += uint64(currentRequestSize)
	}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

goal and start have different types so they need to both be casted to uint32 before they can be subtracted

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both of them are int64. Let me recheck.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arijitAD they aren't :(

../../dot/network/sync.go:457:25: invalid operation: goal - start (mismatched types int64 and uint64)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2021-08-26 at 8 59 48 PM

My bad I was checking it inside the if condition where the variable `goal` was getting shadowed.

@@ -54,13 +56,18 @@ func (h *MessageHandler) handleMessage(from peer.ID, m GrandpaMessage) (network.
switch m.Type() {
case voteType:
vm, ok := m.(*VoteMessage)
if h.grandpa != nil && ok {
// send vote message to grandpa service
if h.grandpa == nil || !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a switch case,

func (h *MessageHandler) handleMessage(from peer.ID, m GrandpaMessage) (network.NotificationsMessage, error) {
	logger.Trace("handling grandpa message", "msg", m)

	switch msg := m.(type) {
	case *VoteMessage:
		if h.grandpa == nil {
			return nil, nil
		}

		// send vote message to grandpa service
		go func() {
			h.grandpa.in <- &networkVoteMessage{
				from: from,
				msg:  msg,
			}
		}()

		return nil, nil
	case *CommitMessage:
		return nil, h.handleCommitMessage(msg)
	case *NeighbourMessage:
		return nil, h.handleNeighbourMessage(from, msg)
	case *catchUpRequest:
		return h.handleCatchUpRequest(msg)
	case *catchUpResponse:
		return nil, h.handleCatchUpResponse(msg)
	}
	
	return nil, ErrInvalidMessageType
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

voteMessages map[common.Hash]map[ed25519.PublicKeyBytes]*networkVoteMessage // map of vote block hash -> array of VoteMessages for that hash
commitMessages map[common.Hash]*CommitMessage // map of commit block hash to commit message
mapLock sync.Mutex
in chan *types.Block // receive imported block from BlockState
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This channel is used to receive data
in <-chan *types.Block
We should close the channel at the sender's end.

func (bs *BlockState) UnregisterImportedChannel(id byte) {
	bs.importedLock.Lock()
	defer bs.importedLock.Unlock()

	if ch, ok := bs.imported[id]; !ok {
		close(ch)
	}
	delete(bs.imported, id)
	err := bs.importedBytePool.Put(id)
	if err != nil {
		logger.Error("failed to unregister imported channel", "error", err)
	}
}

instead of

func (t *tracker) stop() {
	close(t.stopped)
	t.blockState.UnregisterImportedChannel(t.chanID)
	close(t.in)
}

Reference:

One general principle of using Go channels is don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders. In other words, we should only close a channel in a sender goroutine if the sender is the only sender of the channel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think this relies on Ed's refactor in #1736 so I'd rather not change it here, I agree with you though that the whole notifier code needs to be refactored. I can change it here if you'd like but it overlaps with Ed's PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented on this on Ed's PR #1736 (comment)

t.mapLock.Unlock()
defer t.mapLock.Unlock()

msgs, has := t.voteMessages[v.msg.Message.Hash]
Copy link
Contributor

@arijitAD arijitAD Aug 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to

	voteMsg, has := t.voteMessages[v.msg.Message.Hash]
	if !has {
		voteMsg = make(map[ed25519.PublicKeyBytes]*networkVoteMessage)
		t.voteMessages[v.msg.Message.Hash] = voteMsg
	}

	voteMsg[v.msg.Message.AuthorityID] = v

Code to verify

func TestMessageTracker_MapInsideMap(t *testing.T) {
	mm := make(map[common.Hash]map[ed25519.PublicKeyBytes]*networkVoteMessage)
	hash := common.NewHash([]byte{0})

	voteMsg, ok := mm[hash]
	require.False(t, ok)

	voteMsg = make(map[ed25519.PublicKeyBytes]*networkVoteMessage)
	mm[hash] = voteMsg

	AuthorityID := ed25519.PublicKeyBytes(common.MustHexToHash("0x34602b88f60513f1c805d87ef52896934baf6a662bc37414dbdbf69356b1a691"))
	voteMsg[AuthorityID] = &networkVoteMessage{}

	voteMsg, ok = mm[hash]
	require.True(t, ok)

	_, ok = voteMsg[AuthorityID]
	require.True(t, ok)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a test for the maps in addVote

@@ -82,16 +97,31 @@ func (t *tracker) handleBlocks() {
continue
}

t.mapLock.Lock()
func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better indentation

func (t *tracker) handleBlocks() {
	handleBlocks := func(b *types.Block) {
		t.mapLock.Lock()
		defer t.mapLock.Unlock()

		h := b.Header.Hash()
		if vms, has := t.voteMessages[h]; has {
			for _, v := range vms {
				_, err := t.handler.handleMessage(v.from, v.msg)
				if err != nil {
					logger.Warn("failed to handle vote message", "message", v, "error", err)
				}
			}

			delete(t.voteMessages, h)
		}

		if cm, has := t.commitMessages[h]; has {
			_, err := t.handler.handleMessage("", cm)
			if err != nil {
				logger.Warn("failed to handle commit message", "message", cm, "error", err)
			}

			delete(t.commitMessages, h)
		}
	}
	
	for {
		select {
		case b := <-t.in:
			if b == nil {
				continue
			}
			handleBlocks(b)
		case <-t.stopped:
			return
		}
	}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or create a function func (t *tracker) handle(b *types.Block) and call it inside the case:

for { 
  select { 
  case b := <-t.in: 
     if b == nil { continue }
     t.handle(b)
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to have a separate handleBlock function

@@ -125,6 +132,11 @@ func (h *MessageHandler) handleCommitMessage(msg *CommitMessage) error {

// check justification here
if err := h.verifyCommitMessageJustification(msg); err != nil {
if errors.Is(err, blocktree.ErrStartNodeNotFound) {
// TODO: make this synchronous
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created to this TODO -> #1771

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, I'm still unsure about this todo, after the sync refactor it hopefully won't be necessary, but we'll see

start = start - m + 1
reqSize := blockRequestSize
if goal-int64(start) < int64(blockRequestSize) {
start = uint64(best.Int64() + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
start = uint64(best.Int64() + 1)
start = best.Uint64() + 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 79 to 80
t.voteMessages[v.msg.Message.Hash] = make(map[ed25519.PublicKeyBytes]*networkVoteMessage)
msgs = t.voteMessages[v.msg.Message.Hash]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
t.voteMessages[v.msg.Message.Hash] = make(map[ed25519.PublicKeyBytes]*networkVoteMessage)
msgs = t.voteMessages[v.msg.Message.Hash]
msgs = make(map[ed25519.PublicKeyBytes]*networkVoteMessage)
t.voteMessages[v.msg.Message.Hash] = msgs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@codecov
Copy link

codecov bot commented Aug 30, 2021

Codecov Report

Merging #1769 (362ef19) into development (66b1410) will increase coverage by 0.00%.
The diff coverage is 77.27%.

Impacted file tree graph

@@             Coverage Diff              @@
##           development    #1769   +/-   ##
============================================
  Coverage        59.09%   59.09%           
============================================
  Files              184      184           
  Lines            19383    19390    +7     
============================================
+ Hits             11454    11459    +5     
+ Misses            5951     5944    -7     
- Partials          1978     1987    +9     
Flag Coverage Δ
unit-tests 59.09% <77.27%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
lib/grandpa/message_tracker.go 72.00% <69.69%> (-15.88%) ⬇️
lib/grandpa/message_handler.go 56.89% <82.35%> (-1.09%) ⬇️
dot/network/sync.go 63.70% <84.61%> (+1.47%) ⬆️
lib/grandpa/grandpa.go 59.87% <100.00%> (-0.32%) ⬇️
lib/grandpa/vote_message.go 80.50% <100.00%> (ø)
lib/grandpa/network.go 52.89% <0.00%> (-1.66%) ⬇️
lib/grandpa/message.go 59.31% <0.00%> (-1.38%) ⬇️
lib/blocktree/blocktree.go 55.61% <0.00%> (-1.13%) ⬇️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 66b1410...362ef19. Read the comment docs.

@noot noot merged commit 071fe44 into development Aug 30, 2021
@noot noot deleted the noot/grandpa-msg-tracker branch August 30, 2021 16:57
@github-actions
Copy link

github-actions bot commented Dec 3, 2021

🎉 This PR is included in version 0.6.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

timwu20 pushed a commit to timwu20/gossamer that referenced this pull request Dec 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[GRANDPA] Message tracker use map of maps to avoid duplicates
4 participants