fix(x/bank): disallow duplicated addresses when sanitizing the genesis bank balances (#18542)
Co-authored-by: GrosQuildu <e2.8a.95@gmail.com>
This commit is contained in:
parent
b418daed4d
commit
139f851337
@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"cosmossdk.io/x/bank/exported"
|
||||
@ -51,7 +52,7 @@ func (b balanceByAddress) Swap(i, j int) {
|
||||
b.balances[i], b.balances[j] = b.balances[j], b.balances[i]
|
||||
}
|
||||
|
||||
// SanitizeGenesisBalances sorts addresses and coin sets.
|
||||
// SanitizeGenesisBalances checks for duplicates and sorts addresses and coin sets.
|
||||
func SanitizeGenesisBalances(balances []Balance) []Balance {
|
||||
// Given that this function sorts balances, using the standard library's
|
||||
// Quicksort based algorithms, we have algorithmic complexities of:
|
||||
@ -64,12 +65,18 @@ func SanitizeGenesisBalances(balances []Balance) []Balance {
|
||||
|
||||
// 1. Retrieve the address equivalents for each Balance's address.
|
||||
addresses := make([]sdk.AccAddress, len(balances))
|
||||
// 2. Track any duplicate addresses to avoid false positives on invariant checks.
|
||||
seen := make(map[string]struct{})
|
||||
for i := range balances {
|
||||
addr, _ := sdk.AccAddressFromBech32(balances[i].Address)
|
||||
addresses[i] = addr
|
||||
if _, exists := seen[string(addr)]; exists {
|
||||
panic(fmt.Sprintf("genesis state has a duplicate account: %q aka %x", balances[i].Address, addr))
|
||||
}
|
||||
seen[string(addr)] = struct{}{}
|
||||
}
|
||||
|
||||
// 2. Sort balances.
|
||||
// 3. Sort balances.
|
||||
sort.Sort(balanceByAddress{addresses: addresses, balances: balances})
|
||||
|
||||
return balances
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -171,6 +172,44 @@ func TestSanitizeBalances(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeBalancesDuplicates(t *testing.T) {
|
||||
// 1. Generate balances
|
||||
tokens := sdk.TokensFromConsensusPower(81, sdk.DefaultPowerReduction)
|
||||
coin := sdk.NewCoin("benchcoin", tokens)
|
||||
coins := sdk.Coins{coin}
|
||||
addrs, _ := makeRandomAddressesAndPublicKeys(13)
|
||||
|
||||
var balances []bank.Balance
|
||||
for _, addr := range addrs {
|
||||
balances = append(balances, bank.Balance{
|
||||
Address: addr.String(),
|
||||
Coins: coins,
|
||||
})
|
||||
}
|
||||
|
||||
// 2. Add duplicate
|
||||
dupIdx := 3
|
||||
balances = append(balances, balances[dupIdx])
|
||||
addr, _ := sdk.AccAddressFromBech32(balances[dupIdx].Address)
|
||||
expectedError := fmt.Sprintf("genesis state has a duplicate account: %q aka %x", balances[dupIdx].Address, addr)
|
||||
|
||||
// 3. Add more balances
|
||||
coin2 := sdk.NewCoin("coinbench", tokens)
|
||||
coins2 := sdk.Coins{coin2, coin}
|
||||
addrs2, _ := makeRandomAddressesAndPublicKeys(31)
|
||||
for _, addr := range addrs2 {
|
||||
balances = append(balances, bank.Balance{
|
||||
Address: addr.String(),
|
||||
Coins: coins2,
|
||||
})
|
||||
}
|
||||
|
||||
// 4. Execute SanitizeGenesisBalances and expect an error
|
||||
require.PanicsWithValue(t, expectedError, func() {
|
||||
bank.SanitizeGenesisBalances(balances)
|
||||
}, "SanitizeGenesisBalances should panic with duplicate accounts")
|
||||
}
|
||||
|
||||
func makeRandomAddressesAndPublicKeys(n int) (accL []sdk.AccAddress, pkL []*ed25519.PubKey) {
|
||||
for i := 0; i < n; i++ {
|
||||
pk := ed25519.GenPrivKey().PubKey().(*ed25519.PubKey)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user