cosmos-sdk/codec/address/bech32_codec_test.go
2024-06-13 09:28:12 +00:00

210 lines
4.7 KiB
Go

package address
import (
"crypto/rand"
"sync"
"sync/atomic"
"testing"
"github.com/hashicorp/golang-lru/simplelru"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/internal/conv"
)
var (
lru, _ = simplelru.NewLRU(500, nil)
cacheOptions = CachedCodecOptions{
Mu: &sync.Mutex{},
Lru: lru,
}
)
func generateAddresses(totalAddresses int) ([][]byte, error) {
keys := make([][]byte, totalAddresses)
addr := make([]byte, 32)
for i := 0; i < totalAddresses; i++ {
_, err := rand.Read(addr)
if err != nil {
return nil, err
}
keys[i] = addr
}
return keys, nil
}
func TestNewBech32Codec(t *testing.T) {
tests := []struct {
name string
mu *sync.Mutex
lru func(t *testing.T) *simplelru.LRU
error bool
}{
{
name: "lru and mutex provided",
mu: &sync.Mutex{},
lru: func(t *testing.T) *simplelru.LRU {
t.Helper()
newLru, err := simplelru.NewLRU(500, nil)
require.NoError(t, err)
return newLru
},
},
{
name: "both empty",
mu: nil,
lru: func(t *testing.T) *simplelru.LRU {
t.Helper()
return nil
},
},
{
name: "only lru provided",
mu: nil,
lru: func(t *testing.T) *simplelru.LRU {
t.Helper()
newLru, err := simplelru.NewLRU(500, nil)
require.NoError(t, err)
return newLru
},
error: true,
},
{
name: "only mutex provided",
mu: &sync.Mutex{},
lru: func(t *testing.T) *simplelru.LRU {
t.Helper()
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
newLru := tt.lru(t)
got, err := NewCachedBech32Codec("cosmos", CachedCodecOptions{
Mu: tt.mu,
Lru: newLru,
})
if tt.error {
require.Error(t, err)
} else {
require.NoError(t, err)
require.NotNil(t, got)
}
})
}
}
func TestBech32Codec(t *testing.T) {
tests := []struct {
name string
prefix string
lru *simplelru.LRU
address string
}{
{
name: "create accounts cached bech32 codec",
prefix: "cosmos",
lru: lru,
address: "cosmos1p8s0p6gqc6c9gt77lgr2qqujz49huhu6a80smx",
},
{
name: "create validator cached bech32 codec",
prefix: "cosmosvaloper",
lru: lru,
address: "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0",
},
{
name: "create consensus cached bech32 codec",
prefix: "cosmosvalcons",
lru: lru,
address: "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ac, err := NewCachedBech32Codec(tt.prefix, cacheOptions)
require.NoError(t, err)
cached, ok := ac.(cachedBech32Codec)
require.True(t, ok)
require.Equal(t, cached.cache, tt.lru)
addr, err := ac.StringToBytes(tt.address)
require.NoError(t, err)
cachedAddr, ok := tt.lru.Get(tt.address)
require.True(t, ok)
require.Equal(t, addr, cachedAddr)
accAddr, err := ac.BytesToString(addr)
require.NoError(t, err)
cachedStrAddr, ok := tt.lru.Get(conv.UnsafeBytesToStr(addr))
require.True(t, ok)
cachedStrAddrMap, ok := cachedStrAddr.(map[string]string)
require.True(t, ok)
require.Equal(t, accAddr, cachedStrAddrMap[tt.prefix])
})
}
}
func TestMultipleBech32Codec(t *testing.T) {
cAc, err := NewCachedBech32Codec("cosmos", cacheOptions)
require.NoError(t, err)
cosmosAc, ok := cAc.(cachedBech32Codec)
require.True(t, ok)
sAc, err := NewCachedBech32Codec("stake", cacheOptions)
require.NoError(t, err)
stakeAc, ok := sAc.(cachedBech32Codec)
require.True(t, ok)
require.Equal(t, cosmosAc.cache, stakeAc.cache)
addr := make([]byte, 32)
_, err = rand.Read(addr)
require.NoError(t, err)
cosmosAddr, err := cosmosAc.BytesToString(addr)
require.NoError(t, err)
stakeAddr, err := stakeAc.BytesToString(addr)
require.NoError(t, err)
require.True(t, cosmosAddr != stakeAddr)
cachedCosmosAddr, err := cosmosAc.BytesToString(addr)
require.NoError(t, err)
require.Equal(t, cosmosAddr, cachedCosmosAddr)
cachedStakeAddr, err := stakeAc.BytesToString(addr)
require.NoError(t, err)
require.Equal(t, stakeAddr, cachedStakeAddr)
}
func TestBech32CodecRace(t *testing.T) {
ac, err := NewCachedBech32Codec("cosmos", cacheOptions)
require.NoError(t, err)
myAddrBz := []byte{0x1, 0x2, 0x3, 0x4, 0x5}
var (
wgStart, wgDone sync.WaitGroup
errCount atomic.Uint32
)
const n = 3
wgStart.Add(n)
wgDone.Add(n)
for i := 0; i < n; i++ {
go func() {
wgStart.Done()
wgStart.Wait() // wait for all routines started
got, err := ac.BytesToString(myAddrBz)
if err != nil || got != "cosmos1qypqxpq9dc9msf" {
errCount.Add(1)
}
wgDone.Done()
}()
}
wgDone.Wait() // wait for all routines completed
require.Equal(t, errCount.Load(), uint32(0))
}