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

Fix extractCommitteeMember #1511

Closed
Tracked by #5
fed-franz opened this issue Mar 21, 2023 · 5 comments · Fixed by #1524
Closed
Tracked by #5

Fix extractCommitteeMember #1511

fed-franz opened this issue Mar 21, 2023 · 5 comments · Fixed by #1524
Assignees

Comments

@fed-franz
Copy link
Contributor

fed-franz commented Mar 21, 2023

QUESTIONS on extractCommitteeMember
Originally posted by @fed-franz in #1491 (comment)

Original code:

	for i := 0; ; i++ {
		// If a provisioner is missing, we use the provisioner at position 0
		if m, e = p.MemberAt(i); e != nil {
			m, e = p.MemberAt(0)

			// If provisioner 0 is also missing, panic
			if e != nil {
				// FIXME: shall this panic?
				log.Panic(e)
			}

			i = 0
		}

		stake, err := p.GetStake(m.PublicKeyBLS)
		if err != nil {
			// If we get an error from GetStake, it means we either got a public key of a
			// provisioner who is no longer in the set, or we got a malformed public key.
			// We can't repair our committee on the fly, so we have to panic.
			log.Panic(fmt.Errorf("pk: %s err: %v", util.StringifyBytes(m.PublicKeyBLS), err))
		}

		// If the current stake is higher than the score, return the current provisioner's BLS key
		if stake >= score {
			return m.PublicKeyBLS
		}

		score -= stake
	}
}

This line:
if m, e = p.MemberAt(i); e != nil {
handles the case in which a Member is missing.
Q: When does/can this occur?

This line:
m, e = p.MemberAt(0)
handles a missing Member by setting the current member 'm' to Member 0.
Q: What's the logic behind this?

This line:
if err != nil {
seems similar to the case in which a member is missing.
Q: When does this occur? What's the difference between the two cases?

@fed-franz
Copy link
Contributor Author

@herr-seppia @autholykos

We discussed this in Amsterdam.
If I recall correctly, the first check is actually not necessary.
Not sure about the last one.

Can you confirm that?

@herr-seppia
Copy link
Member

The first check shall not be necessary.
Indeed I cannot see any reason why a provisioner should not be found.
It smells to me like a workaround for some tests when the rusk integration was not fully done.

So, back to your question

  1. No reason why a provisioner should not be found
  2. Then, no need to fallback to the first member
  3. No way to panic if the provisioners are empty.

About the last point, maybe it worth to add a check into the stake contract in order to not perform unstake for the last provisioner

@fed-franz fed-franz self-assigned this Mar 22, 2023
@fed-franz
Copy link
Contributor Author

@herr-seppia
What do you think about this?:

stake, err := p.GetStake(m.PublicKeyBLS)
	if err != nil {
		// If we get an error from GetStake, it means we either got a public key of a
		// provisioner who is no longer in the set, or we got a malformed public key.
		// We can't repair our committee on the fly, so we have to panic.
		log.Panic(fmt.Errorf("pk: %s err: %v", util.StringifyBytes(m.PublicKeyBLS), err))
	}

Is this necessary?
I think the provisioner set should not change while extracting a member. Isn't that the case?

@fed-franz
Copy link
Contributor Author

@herr-seppia

I think we misinterpreted the code.
The missing provisioner check is actually meant to loop over the provisioner set when the last index is reached.
Since the loop only increases i with no break condition, when i equals p.size() + 1, it sets the provisioner to provisioner 0 and the index i to 0.

So the first check is actually necessary for the loop to work.

The second check (if e != nil) I still believe is not necessary.

@fed-franz fed-franz linked a pull request Apr 20, 2023 that will close this issue
@herr-seppia
Copy link
Member

I think we misinterpreted the code.

Yes, you're right.

In that case, I suggest to change the comments with

- // extractCommitteeMember walks through the provisioners set, while deducting each stake
+ // extractCommitteeMember loops the provisioners set, while deducting each stake

and

- // If a provisioner is missing, we use the provisioner at position 0
+ // If a provisioner is missing it means that we've reached the end of the set. 
+ // Then we use the provisioner at position 0 and continue to loop

fed-franz added a commit that referenced this issue May 4, 2023
…ember

Fix extractCommitteeMember loop

Resolves #1511
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 a pull request may close this issue.

2 participants