Petite Fleece Cheetah
High
The iteration over a map will result in a chain halt.
The choice to iterate over a map will result in non-determinism and therefore a chain halt. According to the CosmosSDK documentation (https://docs.cosmos.network/v0.52/build/building-apps/security-part-1#go-map-internals):
When building on the Cosmos SDK, you should never iterate over a Go map. Doing so results in non-determinism. Instead, if map usage is inevitable, it is necessary to convert it to a slice and sort it. See an example here
The function findLastBlockHash()
iterates over a blockHashes
mapping causing a consequent chain halt.
No response
No response
No response
The chain will halt (which falls under high categorization according to the contest docs). There are some existing examples of the chain halt as a result of non-determinism. Here's an example of such issue (due to the map) in the thorchain
:
https://gitlab.com/thorchain/thornode/-/issues/1169
Let's take a look at the findLastBlockHash()
functionality:
// Find the block hash that has the maximum voting power committed to it
for blockHash, power := range blockHashes {
if power > maxPower {
resBlockHash = blockHash
maxPower = power
}
}
if len(resBlockHash) == 0 {
return nil, fmt.Errorf("could not find the block hash")
}
It can be seen here that the function iterates over a mapping of blockHashes
:
// Mapping between block hashes and voting power committed to them
blockHashes := make(map[string]int64, 0)
This will result in non-determinism as on different nodes the parameter resBlockHash
will be a different value depending on when iterations start that is an array with the same power will return different resBlockHash
.
Here's an example of the possible outcome:
blockHashes := map[string]int64{
"a3f1b2c4d5e6f7890123456789abcdef12345678": 500,
"b7c8d9e0f123456789abcdef0123456789abcdef": 400,
"c1d2e3f4a567890123456789abcdefabcdef1234": 200,
"d4e5f6a7b890123456789abcdef0123456789abc": 300,
"e5f6a7b8c90123456789abcdef0123456789abcd": 500,
}
Slice the mapping first. An example from the Allora protocol code:
// Generic function that sorts the keys of a map
// Used for deterministic ranging of maps
func GetSortedKeys[K cmp.Ordered, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
return keys
}