Skip to content

Commit

Permalink
make downtime garbage collection use blocks instead of time
Browse files Browse the repository at this point in the history
  • Loading branch information
unknown unknown committed Jul 20, 2023
1 parent 1bf6c17 commit b376793
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 133 deletions.
8 changes: 4 additions & 4 deletions proto/downtime/v1/downtime.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ message Params {
// downtime_duration defines the minimum time elapsed between blocks
// that we consider the chain to be down.
google.protobuf.Duration downtime_duration = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
// garbage_collection_duration defines the time a downtime will be recorded
// garbage_collection_blocks defines the elapsed blocks a downtime will be recorded
// on the chain before it goes through garbage collection.
google.protobuf.Duration garbage_collection_duration = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
uint64 garbage_collection_blocks = 2;
}

// Downtime defines a single downtime record.
Expand All @@ -32,6 +32,6 @@ message Downtime {
message DowntimeGarbageCollection {
// block defines the block that references a Downtime.
uint64 block = 1;
// gc_time defines the time when the downtime will be garbage collected.
google.protobuf.Timestamp gc_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
// gc_time defines the block when garbage collection will be performed.
uint64 gc_block = 2;
}
23 changes: 12 additions & 11 deletions x/downtime/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (*v1.GenesisState, error) {
return false
})
// get garbage collection
k.IterateGarbageCollections(ctx, func(height uint64, gcTime time.Time) (stop bool) {
gs.DowntimesGarbageCollection = append(gs.DowntimesGarbageCollection, &v1.DowntimeGarbageCollection{Block: height, GcTime: gcTime})
k.IterateGarbageCollections(ctx, func(height uint64, gcBlock uint64) (stop bool) {
gs.DowntimesGarbageCollection = append(gs.DowntimesGarbageCollection, &v1.DowntimeGarbageCollection{Block: height, GcBlock: gcBlock})
return false
})

Expand All @@ -74,7 +74,7 @@ func (k Keeper) ImportGenesis(ctx sdk.Context, gs *v1.GenesisState) error {
}
// set garbage collection
for _, gc := range gs.DowntimesGarbageCollection {
k.SetDowntimeGarbageCollection(ctx, gc.Block, gc.GcTime)
k.SetDowntimeGarbageCollection(ctx, gc.Block, gc.GcBlock)
}
// set last block time, only if it's present in genesis
// otherwise it means we don't care about it. This can
Expand Down Expand Up @@ -181,23 +181,23 @@ func (k Keeper) IterateDowntimes(ctx sdk.Context, startHeight, endHeight uint64,
}
}

func (k Keeper) SetDowntimeGarbageCollection(ctx sdk.Context, height uint64, gcTime time.Time) {
func (k Keeper) SetDowntimeGarbageCollection(ctx sdk.Context, height uint64, gcBlock uint64) {
ctx.KVStore(k.storeKey).
Set(
types.GetDowntimeGarbageKey(gcTime),
types.GetDowntimeGarbageKey(gcBlock),
sdk.Uint64ToBigEndian(height),
)
}

func (k Keeper) IterateGarbageCollections(ctx sdk.Context, onResult func(height uint64, gcTime time.Time) (stop bool)) {
func (k Keeper) IterateGarbageCollections(ctx sdk.Context, onResult func(height uint64, gcBlock uint64) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iter := store.Iterator(types.DowntimeHeightGarbageKey, sdk.PrefixEndBytes(types.DowntimeHeightGarbageKey))

defer iter.Close()
for ; iter.Valid(); iter.Next() {
height := sdk.BigEndianToUint64(iter.Value())
gcTime := types.ParseDowntimeGarbageKey(iter.Key())
if onResult(height, gcTime) {
gcBlock := types.ParseDowntimeGarbageKey(iter.Key())
if onResult(height, gcBlock) {
break
}
}
Expand All @@ -208,15 +208,16 @@ func (k Keeper) IterateGarbageCollections(ctx sdk.Context, onResult func(height
// RecordDowntime will record a downtime for the current block
func (k Keeper) RecordDowntime(ctx sdk.Context, duration time.Duration) {
k.SetDowntime(ctx, uint64(ctx.BlockHeight()), duration)
k.SetDowntimeGarbageCollection(ctx, uint64(ctx.BlockHeight()), ctx.BlockTime().Add(k.GetParams(ctx).GarbageCollectionDuration))
k.SetDowntimeGarbageCollection(ctx, uint64(ctx.BlockHeight()), uint64(ctx.BlockHeight())+k.GetParams(ctx).GarbageCollectionBlocks)
}

// GarbageCollectDowntimes will garbage collect downtimes.
func (k Keeper) GarbageCollectDowntimes(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
iter := store.Iterator(
types.GetDowntimeGarbageKey(time.Unix(0, 0)),
types.GetDowntimeGarbageKey(ctx.BlockTime()))
types.GetDowntimeGarbageKey(0),
types.GetDowntimeGarbageKey(uint64(ctx.BlockHeight())+1), // NOTE: +1 because prefix end is exclusive.
)

defer iter.Close()
keysToDelete := make([][]byte, 0) // this guarantees that we don't delete keys while iterating.
Expand Down
21 changes: 10 additions & 11 deletions x/downtime/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,14 @@ func TestBeginBlock(t *testing.T) {
_, hadDowntimes = keeper.GetDowntime(ctx, uint64(ctx.BlockHeight()))
require.False(t, hadDowntimes)

// now check garbage collection
// we extend the downtime duration in order not to have another downtime
// since we're making time elapse by a lot in order to trigger garbage collection!
gcDuration := keeper.GetParams(ctx).GarbageCollectionDuration
keeper.SetParams(ctx, v1.Params{DowntimeDuration: gcDuration*2 + keeper.GetParams(ctx).DowntimeDuration, GarbageCollectionDuration: gcDuration})
ctx = nextBlock(ctx, gcDuration+1*time.Second)
// we check garbage collection by advancing blocks until the garbage collection blocks are reached
gcBlocks := keeper.GetParams(ctx).GarbageCollectionBlocks
for i := 0; i < int(gcBlocks); i++ {
ctx = nextBlock(ctx, 1*time.Second)
keeper.BeginBlock(ctx)
}
keeper.BeginBlock(ctx)
_, ok = keeper.GetDowntime(ctx, uint64(ctx.BlockHeight()-1)) // currHeight-1 because we're checking the downtime of the previous block
require.False(t, ok)

// now we check the garbage collection store prefix, which should be empty
sk := app.GetKey(types.ModuleName)
store := prefix.NewStore(ctx.KVStore(sk), types.DowntimeHeightGarbageKey)
Expand All @@ -155,8 +154,8 @@ func TestImportExportGenesis(t *testing.T) {
},
DowntimesGarbageCollection: []*v1.DowntimeGarbageCollection{
{
Block: 1,
GcTime: ctx.BlockTime().Add(2 * time.Hour),
Block: 1,
GcBlock: 100,
},
},
LastBlockTime: nil,
Expand All @@ -169,7 +168,7 @@ func TestImportExportGenesis(t *testing.T) {
// we check that if we export we have the same genesis state
gotGs, err := keeper.ExportGenesis(ctx)
require.NoError(t, err)
require.Equal(t, wantGs, gotGs)
require.Equal(t, wantGs, gotGs, wantGs.String(), "-", gotGs.String())
})

t.Run("import export – last block time", func(t *testing.T) {
Expand Down
14 changes: 4 additions & 10 deletions x/downtime/types/keys.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package types

import (
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -30,15 +28,11 @@ func ParseDowntimeKey(key []byte) uint64 {
}

// GetDowntimeGarbageKey returns the downtime garbage storage key given the height.
func GetDowntimeGarbageKey(time time.Time) []byte {
return append(DowntimeHeightGarbageKey, sdk.FormatTimeBytes(time)...)
func GetDowntimeGarbageKey(block uint64) []byte {
return append(DowntimeHeightGarbageKey, sdk.Uint64ToBigEndian(block)...)
}

// ParseDowntimeGarbageKey returns the downtime garbage time given the key.
func ParseDowntimeGarbageKey(key []byte) time.Time {
r, err := sdk.ParseTimeBytes(key[1:])
if err != nil {
panic(err)
}
return r
func ParseDowntimeGarbageKey(key []byte) uint64 {
return sdk.BigEndianToUint64(key[1:])
}
Loading

0 comments on commit b376793

Please sign in to comment.