Skip to content

Commit

Permalink
TimerStore: library for timers based on block height/timestamp
Browse files Browse the repository at this point in the history
TimerStore manages timers to efficiently support future timeouts. Timeouts
can be based on either block-height or block-timestamp. When a timeout occurs,
a designated callback function is called with the details (ctx and data).
  • Loading branch information
orenl-lava committed Apr 8, 2023
1 parent 95d0e7c commit c8956b6
Show file tree
Hide file tree
Showing 6 changed files with 474 additions and 55 deletions.
38 changes: 38 additions & 0 deletions common/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package common_test

import (
"testing"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmdb "github.com/tendermint/tm-db"
)

var (
mockStoreKey *sdk.KVStoreKey = sdk.NewKVStoreKey("storeKey")
mockMemStoreKey *sdk.MemoryStoreKey = storetypes.NewMemoryStoreKey("storeMemKey")
)

// Helper function to init a mock keeper and context
func initCtx(t *testing.T) (sdk.Context, *codec.ProtoCodec) {
db := tmdb.NewMemDB()
stateStore := store.NewCommitMultiStore(db)

registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)

stateStore.MountStoreWithDB(mockStoreKey, sdk.StoreTypeIAVL, db)
stateStore.MountStoreWithDB(mockMemStoreKey, sdk.StoreTypeMemory, nil)

require.NoError(t, stateStore.LoadLatestVersion())

ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.TestingLogger())

return ctx, cdc
}
2 changes: 1 addition & 1 deletion common/fixation_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
// FixationStore manages lists of entries with versions in the store.
// (See also documentation in common/fixation_entry_index.go)
//
// Its primary use it to implemented "fixated entries": entries that may change over
// Its primary use it to implement "fixated entries": entries that may change over
// time, and whose versions must be retained on-chain as long as they are referenced.
// For examples, an older version of a package is needed as long as the subscription
// that uses it lives.
Expand Down
87 changes: 33 additions & 54 deletions common/fixation_entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,30 @@ import (
"strconv"
"testing"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/lavanet/lava/common"
"github.com/lavanet/lava/common/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmdb "github.com/tendermint/tm-db"
)

// Helper function to init a mock keeper and context
func initCtxAndFixationStores(t *testing.T, count int) ([]*common.FixationStore, sdk.Context) {
db := tmdb.NewMemDB()
stateStore := store.NewCommitMultiStore(db)

registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)

mockStoreKey := sdk.NewKVStoreKey("storeKey")
mockMemStoreKey := storetypes.NewMemoryStoreKey("storeMemKey")
stateStore.MountStoreWithDB(mockStoreKey, sdk.StoreTypeIAVL, db)
stateStore.MountStoreWithDB(mockMemStoreKey, sdk.StoreTypeMemory, nil)

require.NoError(t, stateStore.LoadLatestVersion())
func initCtxAndFixationStores(t *testing.T, count int) (sdk.Context, []*common.FixationStore) {
ctx, cdc := initCtx(t)

fs := make([]*common.FixationStore, count)
for i := 0; i < count; i++ {
fixationKey := "mock_fix_" + strconv.Itoa(i)
fs[i] = common.NewFixationStore(mockStoreKey, cdc, fixationKey)
}

ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.TestingLogger())

return fs, ctx
return ctx, fs
}

func initCtxAndFixationStore(t *testing.T) (*common.FixationStore, sdk.Context) {
fs, ctx := initCtxAndFixationStores(t, 1)
return fs[0], ctx
func initCtxAndFixationStore(t *testing.T) (sdk.Context, *common.FixationStore) {
ctx, fs := initCtxAndFixationStores(t, 1)
return ctx, fs[0]
}

type template struct {
type fixationTemplate struct {
op string
name string
store int
Expand All @@ -59,8 +38,8 @@ type template struct {
}

// helper to automate testing operations
func testWithTemplate(t *testing.T, playbook []template, countObj int, countVS int) {
vs, ctx := initCtxAndFixationStores(t, countVS)
func testWithFixationTemplate(t *testing.T, playbook []fixationTemplate, countObj int, countVS int) {
ctx, fs := initCtxAndFixationStores(t, countVS)

var coins []sdk.Coin
var dummy sdk.Coin
Expand All @@ -86,41 +65,41 @@ func testWithTemplate(t *testing.T, playbook []template, countObj int, countVS i
if block > uint64(ctx.BlockHeight()) {
ctx = ctx.WithBlockHeight(int64(block))
}
err := vs[play.store].AppendEntry(ctx, play.index, block, &coins[play.coin])
err := fs[play.store].AppendEntry(ctx, play.index, block, &coins[play.coin])
if !play.fail {
require.Nil(t, err, what)
} else {
require.NotNil(t, err, what)
}
case "modify":
err := vs[play.store].ModifyEntry(ctx, play.index, block, &coins[play.coin])
err := fs[play.store].ModifyEntry(ctx, play.index, block, &coins[play.coin])
if !play.fail {
require.Nil(t, err, what)
} else {
require.NotNil(t, err, what)
}
case "find":
found := vs[play.store].FindEntry(ctx, play.index, block, &dummy)
found := fs[play.store].FindEntry(ctx, play.index, block, &dummy)
if !play.fail {
require.True(t, found, what)
require.Equal(t, dummy, coins[play.coin], what)
} else {
require.False(t, found, what)
}
case "get":
found := vs[play.store].GetEntry(ctx, play.index, &dummy)
found := fs[play.store].GetEntry(ctx, play.index, &dummy)
if !play.fail {
require.True(t, found, what)
require.Equal(t, dummy, coins[play.coin], what)
} else {
require.False(t, found, what)
}
case "put":
vs[play.store].PutEntry(ctx, play.index, block)
fs[play.store].PutEntry(ctx, play.index, block)
case "block":
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + play.count)
case "getall":
indexList := vs[play.store].GetAllEntryIndices(ctx)
indexList := fs[play.store].GetAllEntryIndices(ctx)
require.Equal(t, int(play.count), len(indexList), what)
}
}
Expand All @@ -130,22 +109,22 @@ func testWithTemplate(t *testing.T, playbook []template, countObj int, countVS i
func TestEntryInvalidIndex(t *testing.T) {
invalid := "index" + string('\001')

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "with invalid index (fail)", index: invalid, fail: true },
{ op: "modify", name: "with invalid index (fail)", index: invalid, fail: true },
{ op: "find", name: "with invalid index (fail)", index: invalid, fail: true },
{ op: "get", name: "with invalid index (fail)", index: invalid, fail: true },
}

testWithTemplate(t, playbook, 3, 1)
testWithFixationTemplate(t, playbook, 3, 1)
}

// Test addition and auto-removal of a fixation entry
func TestFixationEntryAdditionAndRemoval(t *testing.T) {
block0 := int64(10)
block1 := block0 + types.STALE_ENTRY_TIME + 1

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "find", name: "entry #1", count: block0, coin: 0 },
{ op: "getall", name: "to check exactly one index", count: 1 },
Expand All @@ -159,36 +138,36 @@ func TestFixationEntryAdditionAndRemoval(t *testing.T) {
{ op: "getall", name: "to check again exactly one index", count: 1 },
}

testWithTemplate(t, playbook, 2, 1)
testWithFixationTemplate(t, playbook, 2, 1)
}

// Test addition of same entry twice within the same block
func TestAdditionOfTwoEntriesWithSameIndexInSameBlock(t *testing.T) {
block0 := int64(10)

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "append", name: "entry #2", count: block0, coin: 1 },
{ op: "getall", name: "to check exactly one index", count: 1 },
{ op: "find", name: "entry #2", count: block0, coin: 1 },
}

testWithTemplate(t, playbook, 2, 1)
testWithFixationTemplate(t, playbook, 2, 1)
}

// Test adding entry versions and getting an older version
func TestEntryVersions(t *testing.T) {
block0 := int64(10)
block1 := block0 + int64(10)

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "append", name: "entry #2", count: block1, coin: 1 },
{ op: "find", name: "entry #1", count: block0, coin: 0 },
{ op: "getall", name: "to check exactly one index", count: 1 },
}

testWithTemplate(t, playbook, 2, 1)
testWithFixationTemplate(t, playbook, 2, 1)
}

// Test non-visibility of a stale entry
Expand All @@ -197,7 +176,7 @@ func TestEntryStale(t *testing.T) {
block1 := block0 + int64(10)
block2 := block1 + int64(10) + types.STALE_ENTRY_TIME+1

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "get", name: "refcount entry #1", count: block0, coin: 0 },
{ op: "append", name: "entry #2", count: block1, coin: 1 },
Expand All @@ -214,7 +193,7 @@ func TestEntryStale(t *testing.T) {
{ op: "find", name: "entry #3", count: block2+1, coin: 2 },
}

testWithTemplate(t, playbook, 3, 1)
testWithFixationTemplate(t, playbook, 3, 1)
}

// Test adding entry versions with different fixation keys
Expand All @@ -223,7 +202,7 @@ func TestDifferentFixationKeys(t *testing.T) {
block1 := block0 + int64(10)
block2 := block1 + types.STALE_ENTRY_TIME+1

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1 (store #1)", store: 0, count: block0, coin: 0 },
{ op: "append", name: "entry #1 (store #2)", store: 1, count: block1, coin: 1 },
{ op: "getall", name: "for exactly one index (store #1)", store: 0, count: 1 },
Expand All @@ -240,15 +219,15 @@ func TestDifferentFixationKeys(t *testing.T) {
{ op: "find", name: "entry #2 (store #2)", store: 1, count: block1, coin: 1 },
}

testWithTemplate(t, playbook, 3, 2)
testWithFixationTemplate(t, playbook, 3, 2)
}

func TestGetAndPutEntry(t *testing.T) {
block0 := int64(10)
block1 := block0 + types.STALE_ENTRY_TIME+1
block2 := block1 + types.STALE_ENTRY_TIME+1

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "get", name: "refcount entry #1", coin: 0 },
{ op: "append", name: "entry #2", count: block1, coin: 1 },
Expand All @@ -262,7 +241,7 @@ func TestGetAndPutEntry(t *testing.T) {
{ op: "find", name: "entry #1", count: block0, fail: true },
}

testWithTemplate(t, playbook, 3, 1)
testWithFixationTemplate(t, playbook, 3, 1)
}

func TestDeleteTwoEntries(t *testing.T) {
Expand All @@ -271,7 +250,7 @@ func TestDeleteTwoEntries(t *testing.T) {
block2 := block1 + int64(10)
block3 := block2 + types.STALE_ENTRY_TIME+1

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "append", name: "entry #2", count: block1, coin: 1 },
{ op: "append", name: "entry #3", count: block2, coin: 2 },
Expand All @@ -281,7 +260,7 @@ func TestDeleteTwoEntries(t *testing.T) {
{ op: "find", name: "entry #2", count: block1, fail: true },
}

testWithTemplate(t, playbook, 4, 1)
testWithFixationTemplate(t, playbook, 4, 1)
}

// Test that the appended entries are sorted (first element is oldest)
Expand All @@ -290,7 +269,7 @@ func TestEntriesSort(t *testing.T) {
block1 := block0 + int64(10)
block2 := block1 + int64(10)

playbook := []template{
playbook := []fixationTemplate{
{ op: "append", name: "entry #1", count: block0, coin: 0 },
{ op: "append", name: "entry #2", count: block1, coin: 1 },
{ op: "append", name: "entry #3", count: block2, coin: 2 },
Expand All @@ -300,5 +279,5 @@ func TestEntriesSort(t *testing.T) {
{ op: "find", name: "no entry", count: block0-int64(5), fail: true },
}

testWithTemplate(t, playbook, 3, 1)
testWithFixationTemplate(t, playbook, 3, 1)
}
Loading

0 comments on commit c8956b6

Please sign in to comment.