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

Map iteration non determinism in consumer AccumulateChanges #505

Closed
2 tasks done
danwt opened this issue Nov 21, 2022 · 6 comments · Fixed by #584
Closed
2 tasks done

Map iteration non determinism in consumer AccumulateChanges #505

danwt opened this issue Nov 21, 2022 · 6 comments · Fixed by #584
Assignees
Labels
type: bug Issues that need priority attention -- something isn't working

Comments

@danwt
Copy link
Contributor

danwt commented Nov 21, 2022

Problem

AccumulateChanges used to aggregate tendermint updates on the consumer uses non deterministic map iteration to produce a list, and then writes the list to store in one go. I think this could cause a consensus error.

Closing criteria

Fix the nondeterminism.

Problem details

for _, update := range m {
out = append(out, update)
}

err := k.SetPendingChanges(ctx, ccv.ValidatorSetChangePacketData{
ValidatorUpdates: pendingChanges,

TODOs

  • Labels have been added for issue
  • Issue has been added to the ICS project
@danwt danwt added the type: bug Issues that need priority attention -- something isn't working label Nov 21, 2022
@danwt danwt moved this to Todo in Replicated Security Nov 21, 2022
@danwt
Copy link
Contributor Author

danwt commented Nov 23, 2022

I'm not sure that it's safe to use .String() to get the map key

for i := 0; i < len(currentChanges); i++ {
m[currentChanges[i].PubKey.String()] = currentChanges[i]
}
for i := 0; i < len(newChanges); i++ {
m[newChanges[i].PubKey.String()] = newChanges[i]
}

// DeterministicStringify returns a deterministic string representation of the
// key. This is useful to identify like keys with different representations.
func DeterministicStringify(k tmprotocrypto.PublicKey) string {
	sdkPubKey, err := cryptocodec.FromTmProtoPublicKey(k)
	if err != nil {
		panic("could not get sdk public key from tm proto public key")
	}
	return string(sdkPubKey.Bytes())
}

Maybe it's better to use consensus address here^^

// The list of tendermint updates should hash the same across all consensus nodes
	// that means it is necessary to sort for determinism.
	sort.Slice(consumerUpdatesAsList, func(i, j int) bool {
		if consumerUpdatesAsList[i].Power > consumerUpdatesAsList[j].Power {
			return true
		}
		return consumerUpdatesAsList[i].PubKey.String() > consumerUpdatesAsList[j].PubKey.String()
	})

@mpoke
Copy link
Contributor

mpoke commented Nov 29, 2022

AccumulateChanges used to aggregate tendermint updates on the consumer uses non deterministic map iteration to produce a list, and then writes the list to store in one go. I think this could cause a consensus error.

Does ABCI require the lists of validator updates return by different nodes in EndBlock to be in the same order?

Maybe it's better to use consensus address here

Would that mean hashing the public key for every update? That sounds like a lot of hashing just for this purpose.

@mpoke
Copy link
Contributor

mpoke commented Nov 29, 2022

It seems that the only requirement is that any two nodes return the same list of updates, but not necessarily in the same order. Confirmed by @sergio-mena

@danwt
Copy link
Contributor Author

danwt commented Nov 29, 2022

Ahh yeah I forgot that these are never committed to store, because the pending updates are always deleted in endblock. I think it's fine then.

@danwt danwt closed this as completed Nov 29, 2022
Repository owner moved this from Todo to Done in Replicated Security Nov 29, 2022
@jtremback jtremback reopened this Nov 30, 2022
Repository owner moved this from Done to In Progress in Replicated Security Nov 30, 2022
@jtremback
Copy link
Contributor

I still think we should fix this. The fix is very easy.

@jtremback jtremback moved this from In Progress to Next in Replicated Security Dec 5, 2022
@danwt
Copy link
Contributor Author

danwt commented Dec 5, 2022

Snippet like

// The list of tendermint updates should hash the same across all consensus nodes
	// that means it is necessary to sort for determinism.
	sort.Slice(consumerUpdatesAsList, func(i, j int) bool {
		if consumerUpdatesAsList[i].Power > consumerUpdatesAsList[j].Power {
			return true
		}
		return consumerUpdatesAsList[i].PubKey.String() > consumerUpdatesAsList[j].PubKey.String()
	})

EDIT: not correct :/ lol

@jtremback jtremback moved this from Next to Waiting for review in Replicated Security Dec 12, 2022
Repository owner moved this from Waiting for review to Done in Replicated Security Dec 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Issues that need priority attention -- something isn't working
Projects
No open projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants
@jtremback @mpoke @danwt and others