mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
This commit switches all usage of math/rand to cmn's rand. The only exceptions are within the random file itself, the tools package, and the crypto package. In tools you don't want it to lock between the go-routines. The crypto package doesn't use it so the crypto package have no other dependencies within tendermint/tendermint for easier portability. Crypto/rand usage is unadjusted. Closes #1343
369 lines
11 KiB
Go
369 lines
11 KiB
Go
package lite
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
liteErr "github.com/tendermint/tendermint/lite/errors"
|
|
)
|
|
|
|
func TestMemStoreProvidergetByHeightBinaryAndLinearSameResult(t *testing.T) {
|
|
p := NewMemStoreProvider().(*memStoreProvider)
|
|
|
|
// Store a bunch of commits at specific heights
|
|
// and then ensure that:
|
|
// * getByHeightLinearSearch
|
|
// * getByHeightBinarySearch
|
|
// both return the exact same result
|
|
|
|
// 1. Non-existent height commits
|
|
nonExistent := []int64{-1000, -1, 0, 1, 10, 11, 17, 31, 67, 1000, 1e9}
|
|
ensureNonExistentCommitsAtHeight(t, "getByHeightLinearSearch", p.getByHeightLinearSearch, nonExistent)
|
|
ensureNonExistentCommitsAtHeight(t, "getByHeightBinarySearch", p.getByHeightBinarySearch, nonExistent)
|
|
|
|
// 2. Save some known height commits
|
|
knownHeights := []int64{0, 1, 7, 9, 12, 13, 18, 44, 23, 16, 1024, 100, 199, 1e9}
|
|
createAndStoreCommits(t, p, knownHeights)
|
|
|
|
// 3. Now check if those heights are retrieved
|
|
ensureExistentCommitsAtHeight(t, "getByHeightLinearSearch", p.getByHeightLinearSearch, knownHeights)
|
|
ensureExistentCommitsAtHeight(t, "getByHeightBinarySearch", p.getByHeightBinarySearch, knownHeights)
|
|
|
|
// 4. And now for the height probing to ensure that any height
|
|
// requested returns a fullCommit of height <= requestedHeight.
|
|
comparegetByHeightAlgorithms(t, p, 0, 0)
|
|
comparegetByHeightAlgorithms(t, p, 1, 1)
|
|
comparegetByHeightAlgorithms(t, p, 2, 1)
|
|
comparegetByHeightAlgorithms(t, p, 5, 1)
|
|
comparegetByHeightAlgorithms(t, p, 7, 7)
|
|
comparegetByHeightAlgorithms(t, p, 10, 9)
|
|
comparegetByHeightAlgorithms(t, p, 12, 12)
|
|
comparegetByHeightAlgorithms(t, p, 14, 13)
|
|
comparegetByHeightAlgorithms(t, p, 19, 18)
|
|
comparegetByHeightAlgorithms(t, p, 43, 23)
|
|
comparegetByHeightAlgorithms(t, p, 45, 44)
|
|
comparegetByHeightAlgorithms(t, p, 1025, 1024)
|
|
comparegetByHeightAlgorithms(t, p, 101, 100)
|
|
comparegetByHeightAlgorithms(t, p, 1e3, 199)
|
|
comparegetByHeightAlgorithms(t, p, 1e4, 1024)
|
|
comparegetByHeightAlgorithms(t, p, 1e9, 1e9)
|
|
comparegetByHeightAlgorithms(t, p, 1e9+1, 1e9)
|
|
}
|
|
|
|
func createAndStoreCommits(t *testing.T, p Provider, heights []int64) {
|
|
chainID := "cache-best-height-binary-and-linear"
|
|
appHash := []byte("0xdeadbeef")
|
|
keys := GenValKeys(len(heights) / 2)
|
|
|
|
for _, h := range heights {
|
|
vals := keys.ToValidators(10, int64(len(heights)/2))
|
|
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
|
err := p.StoreCommit(fc)
|
|
require.NoError(t, err, "StoreCommit height=%d", h)
|
|
}
|
|
}
|
|
|
|
func comparegetByHeightAlgorithms(t *testing.T, p *memStoreProvider, ask, expect int64) {
|
|
algos := map[string]func(int64) (FullCommit, error){
|
|
"getHeightByLinearSearch": p.getByHeightLinearSearch,
|
|
"getHeightByBinarySearch": p.getByHeightBinarySearch,
|
|
}
|
|
|
|
for algo, fn := range algos {
|
|
fc, err := fn(ask)
|
|
// t.Logf("%s got=%v want=%d", algo, expect, fc.Height())
|
|
require.Nil(t, err, "%s: %+v", algo, err)
|
|
if assert.Equal(t, expect, fc.Height()) {
|
|
err = p.StoreCommit(fc)
|
|
require.Nil(t, err, "%s: %+v", algo, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
var blankFullCommit FullCommit
|
|
|
|
func ensureNonExistentCommitsAtHeight(t *testing.T, prefix string, fn func(int64) (FullCommit, error), data []int64) {
|
|
for i, qh := range data {
|
|
fc, err := fn(qh)
|
|
assert.NotNil(t, err, "#%d: %s: height=%d should return non-nil error", i, prefix, qh)
|
|
assert.Equal(t, fc, blankFullCommit, "#%d: %s: height=%d\ngot =%+v\nwant=%+v", i, prefix, qh, fc, blankFullCommit)
|
|
}
|
|
}
|
|
|
|
func ensureExistentCommitsAtHeight(t *testing.T, prefix string, fn func(int64) (FullCommit, error), data []int64) {
|
|
for i, qh := range data {
|
|
fc, err := fn(qh)
|
|
assert.Nil(t, err, "#%d: %s: height=%d should not return an error: %v", i, prefix, qh, err)
|
|
assert.NotEqual(t, fc, blankFullCommit, "#%d: %s: height=%d got a blankCommit", i, prefix, qh)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenCommit20(b *testing.B) {
|
|
keys := GenValKeys(20)
|
|
benchmarkGenCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkGenCommit100(b *testing.B) {
|
|
keys := GenValKeys(100)
|
|
benchmarkGenCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkGenCommitSec20(b *testing.B) {
|
|
keys := GenSecpValKeys(20)
|
|
benchmarkGenCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkGenCommitSec100(b *testing.B) {
|
|
keys := GenSecpValKeys(100)
|
|
benchmarkGenCommit(b, keys)
|
|
}
|
|
|
|
func benchmarkGenCommit(b *testing.B, keys ValKeys) {
|
|
chainID := fmt.Sprintf("bench-%d", len(keys))
|
|
vals := keys.ToValidators(20, 10)
|
|
for i := 0; i < b.N; i++ {
|
|
h := int64(1 + i)
|
|
appHash := []byte(fmt.Sprintf("h=%d", h))
|
|
resHash := []byte(fmt.Sprintf("res=%d", h))
|
|
keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), resHash, 0, len(keys))
|
|
}
|
|
}
|
|
|
|
// this benchmarks generating one key
|
|
func BenchmarkGenValKeys(b *testing.B) {
|
|
keys := GenValKeys(20)
|
|
for i := 0; i < b.N; i++ {
|
|
keys = keys.Extend(1)
|
|
}
|
|
}
|
|
|
|
// this benchmarks generating one key
|
|
func BenchmarkGenSecpValKeys(b *testing.B) {
|
|
keys := GenSecpValKeys(20)
|
|
for i := 0; i < b.N; i++ {
|
|
keys = keys.Extend(1)
|
|
}
|
|
}
|
|
|
|
func BenchmarkToValidators20(b *testing.B) {
|
|
benchmarkToValidators(b, 20)
|
|
}
|
|
|
|
func BenchmarkToValidators100(b *testing.B) {
|
|
benchmarkToValidators(b, 100)
|
|
}
|
|
|
|
// this benchmarks constructing the validator set (.PubKey() * nodes)
|
|
func benchmarkToValidators(b *testing.B, nodes int) {
|
|
keys := GenValKeys(nodes)
|
|
for i := 1; i <= b.N; i++ {
|
|
keys.ToValidators(int64(2*i), int64(i))
|
|
}
|
|
}
|
|
|
|
func BenchmarkToValidatorsSec100(b *testing.B) {
|
|
benchmarkToValidatorsSec(b, 100)
|
|
}
|
|
|
|
// this benchmarks constructing the validator set (.PubKey() * nodes)
|
|
func benchmarkToValidatorsSec(b *testing.B, nodes int) {
|
|
keys := GenSecpValKeys(nodes)
|
|
for i := 1; i <= b.N; i++ {
|
|
keys.ToValidators(int64(2*i), int64(i))
|
|
}
|
|
}
|
|
|
|
func BenchmarkCertifyCommit20(b *testing.B) {
|
|
keys := GenValKeys(20)
|
|
benchmarkCertifyCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkCertifyCommit100(b *testing.B) {
|
|
keys := GenValKeys(100)
|
|
benchmarkCertifyCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkCertifyCommitSec20(b *testing.B) {
|
|
keys := GenSecpValKeys(20)
|
|
benchmarkCertifyCommit(b, keys)
|
|
}
|
|
|
|
func BenchmarkCertifyCommitSec100(b *testing.B) {
|
|
keys := GenSecpValKeys(100)
|
|
benchmarkCertifyCommit(b, keys)
|
|
}
|
|
|
|
func benchmarkCertifyCommit(b *testing.B, keys ValKeys) {
|
|
chainID := "bench-certify"
|
|
vals := keys.ToValidators(20, 10)
|
|
cert := NewStaticCertifier(chainID, vals)
|
|
check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), []byte("params"), []byte("res"), 0, len(keys))
|
|
for i := 0; i < b.N; i++ {
|
|
err := cert.Certify(check)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
type algo bool
|
|
|
|
const (
|
|
linearSearch = true
|
|
binarySearch = false
|
|
)
|
|
|
|
// Lazy load the commits
|
|
var fcs5, fcs50, fcs100, fcs500, fcs1000 []FullCommit
|
|
var h5, h50, h100, h500, h1000 []int64
|
|
var commitsOnce sync.Once
|
|
|
|
func lazyGenerateFullCommits(b *testing.B) {
|
|
b.Logf("Generating FullCommits")
|
|
commitsOnce.Do(func() {
|
|
fcs5, h5 = genFullCommits(nil, nil, 5)
|
|
b.Logf("Generated 5 FullCommits")
|
|
fcs50, h50 = genFullCommits(fcs5, h5, 50)
|
|
b.Logf("Generated 50 FullCommits")
|
|
fcs100, h100 = genFullCommits(fcs50, h50, 100)
|
|
b.Logf("Generated 100 FullCommits")
|
|
fcs500, h500 = genFullCommits(fcs100, h100, 500)
|
|
b.Logf("Generated 500 FullCommits")
|
|
fcs1000, h1000 = genFullCommits(fcs500, h500, 1000)
|
|
b.Logf("Generated 1000 FullCommits")
|
|
})
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightLinearSearch5(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs5, h5, linearSearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightLinearSearch50(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs50, h50, linearSearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightLinearSearch100(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs100, h100, linearSearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightLinearSearch500(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs500, h500, linearSearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightLinearSearch1000(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs1000, h1000, linearSearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightBinarySearch5(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs5, h5, binarySearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightBinarySearch50(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs50, h50, binarySearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightBinarySearch100(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs100, h100, binarySearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightBinarySearch500(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs500, h500, binarySearch)
|
|
}
|
|
|
|
func BenchmarkMemStoreProviderGetByHeightBinarySearch1000(b *testing.B) {
|
|
benchmarkMemStoreProvidergetByHeight(b, fcs1000, h1000, binarySearch)
|
|
}
|
|
|
|
var rng = cmn.NewRand()
|
|
|
|
func init() {
|
|
rng.Seed(10)
|
|
}
|
|
|
|
func benchmarkMemStoreProvidergetByHeight(b *testing.B, fcs []FullCommit, fHeights []int64, algo algo) {
|
|
lazyGenerateFullCommits(b)
|
|
|
|
b.StopTimer()
|
|
mp := NewMemStoreProvider()
|
|
for i, fc := range fcs {
|
|
if err := mp.StoreCommit(fc); err != nil {
|
|
b.Fatalf("FullCommit #%d: err: %v", i, err)
|
|
}
|
|
}
|
|
qHeights := make([]int64, len(fHeights))
|
|
copy(qHeights, fHeights)
|
|
// Append some non-existent heights to trigger the worst cases.
|
|
qHeights = append(qHeights, 19, -100, -10000, 1e7, -17, 31, -1e9)
|
|
|
|
memP := mp.(*memStoreProvider)
|
|
searchFn := memP.getByHeightLinearSearch
|
|
if algo == binarySearch { // nolint
|
|
searchFn = memP.getByHeightBinarySearch
|
|
}
|
|
|
|
hPerm := rng.Perm(len(qHeights))
|
|
b.StartTimer()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, j := range hPerm {
|
|
h := qHeights[j]
|
|
if _, err := searchFn(h); err != nil {
|
|
}
|
|
}
|
|
}
|
|
b.ReportAllocs()
|
|
}
|
|
|
|
func genFullCommits(prevFC []FullCommit, prevH []int64, want int) ([]FullCommit, []int64) {
|
|
fcs := make([]FullCommit, len(prevFC))
|
|
copy(fcs, prevFC)
|
|
heights := make([]int64, len(prevH))
|
|
copy(heights, prevH)
|
|
|
|
appHash := []byte("benchmarks")
|
|
chainID := "benchmarks-gen-full-commits"
|
|
n := want
|
|
keys := GenValKeys(2 + (n / 3))
|
|
for i := 0; i < n; i++ {
|
|
vals := keys.ToValidators(10, int64(n/2))
|
|
h := int64(20 + 10*i)
|
|
fcs = append(fcs, keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5))
|
|
heights = append(heights, h)
|
|
}
|
|
return fcs, heights
|
|
}
|
|
|
|
func TestMemStoreProviderLatestCommitAlwaysUsesSorted(t *testing.T) {
|
|
p := NewMemStoreProvider().(*memStoreProvider)
|
|
// 1. With no commits yet stored, it should return ErrCommitNotFound
|
|
got, err := p.LatestCommit()
|
|
require.Equal(t, err.Error(), liteErr.ErrCommitNotFound().Error(), "should return ErrCommitNotFound()")
|
|
require.Equal(t, got, blankFullCommit, "With no fullcommits, it should return a blank FullCommit")
|
|
|
|
// 2. Generate some full commits now and we'll add them unsorted.
|
|
genAndStoreCommitsOfHeight(t, p, 27, 100, 1, 12, 1000, 17, 91)
|
|
fc, err := p.LatestCommit()
|
|
require.Nil(t, err, "with commits saved no error expected")
|
|
require.NotEqual(t, fc, blankFullCommit, "with commits saved no blank FullCommit")
|
|
require.Equal(t, fc.Height(), int64(1000), "the latest commit i.e. the largest expected")
|
|
}
|
|
|
|
func genAndStoreCommitsOfHeight(t *testing.T, p Provider, heights ...int64) {
|
|
n := len(heights)
|
|
appHash := []byte("tests")
|
|
chainID := "tests-gen-full-commits"
|
|
keys := GenValKeys(2 + (n / 3))
|
|
for i := 0; i < n; i++ {
|
|
h := heights[i]
|
|
vals := keys.ToValidators(10, int64(n/2))
|
|
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
|
err := p.StoreCommit(fc)
|
|
require.NoError(t, err, "StoreCommit height=%d", h)
|
|
}
|
|
}
|