- package lite
-
- import (
- "fmt"
- "math/rand"
- "sync"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- 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 = rand.New(rand.NewSource(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)
- }
- }
|