-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ADR 17 Implementation: Historical Module #5380
Changes from 11 commits
17fcad3
5f916a4
2b052b4
481ee0d
954c8b1
da266e8
813c634
5c634a3
33154f0
aa34dc7
fb2e660
d0e8d88
5997e15
82d386f
a45a5fa
ef43df5
093dcf5
c3fc30b
e4d2c34
74b3f19
73cbb7e
277054b
a61d28a
1fdc269
3f59d8c
9364ba4
c0f2ac7
4402252
c3ad793
b1a9ac5
ffc64bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package staking | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/staking/types" | ||
) | ||
|
||
// BeginBlocker will persist the current header and validator set as a historical entry | ||
// and prune the oldest entry based on the HistoricalEntries parameter | ||
func BeginBlocker(ctx sdk.Context, k Keeper) { | ||
entryNum := k.HistoricalEntries(ctx) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best to move this entire block into a method on the keeper.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Open to doing this here, but what i have follows the pattern of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They should be updated as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the EndBlocker? Since that isn't a method on the keeper either? |
||
// if there is no need to persist historicalInfo, return | ||
if entryNum == 0 { | ||
return | ||
} | ||
|
||
// Create HistoricalInfo struct | ||
lastVals := k.GetLastValidators(ctx) | ||
types.Validators(lastVals).Sort() | ||
historicalEntry := types.HistoricalInfo{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, the validation (+ panic on error) is missing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't know if validation here is necessary. Validation is true by construction, so long as Tendermint consensus is working correctly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is the validation used then? |
||
Header: ctx.BlockHeader(), | ||
ValSet: lastVals, | ||
} | ||
|
||
// Set latest HistoricalInfo at current height | ||
k.SetHistoricalInfo(ctx, ctx.BlockHeight(), historicalEntry) | ||
|
||
// prune store to ensure we only have parameter-defined historical entries | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// in most cases, this will invole removing a single historical entry | ||
cwgoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// In the rare scenario when the historical entries gets reduced to a lower value k' | ||
// from the original value k. k - k' entries must be deleted from the store | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Since the entries to be deleted are always in a continuous range, we can iterate | ||
// over the historical entries starting from the most recent version to be pruned | ||
// and then return at the first empty entry | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for i := ctx.BlockHeight() - int64(entryNum); i >= 0; i-- { | ||
alexanderbez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_, found := k.GetHistoricalInfo(ctx, i) | ||
if found { | ||
k.DeleteHistoricalInfo(ctx, i) | ||
} else { | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package staking | ||
|
||
import ( | ||
"sort" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/staking/keeper" | ||
"github.com/cosmos/cosmos-sdk/x/staking/types" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
) | ||
|
||
func TestBeginBlocker(t *testing.T) { | ||
ctx, _, k, _ := keeper.CreateTestInput(t, false, 10) | ||
|
||
// set historical entries in params to 5 | ||
params := types.DefaultParams() | ||
params.HistoricalEntries = 5 | ||
k.SetParams(ctx, params) | ||
|
||
// set historical info at 5, 4 which should be pruned | ||
// and check that it has been stored | ||
h4 := abci.Header{ | ||
ChainID: "HelloChain", | ||
Height: 4, | ||
} | ||
h5 := abci.Header{ | ||
ChainID: "HelloChain", | ||
Height: 5, | ||
} | ||
valSet := []Validator{ | ||
types.NewValidator(sdk.ValAddress(keeper.Addrs[0]), keeper.PKs[0], types.Description{}), | ||
types.NewValidator(sdk.ValAddress(keeper.Addrs[1]), keeper.PKs[1], types.Description{}), | ||
} | ||
hi4 := types.NewHistoricalInfo(h4, valSet) | ||
hi5 := types.NewHistoricalInfo(h5, valSet) | ||
k.SetHistoricalInfo(ctx, 4, hi4) | ||
k.SetHistoricalInfo(ctx, 5, hi5) | ||
recv, found := k.GetHistoricalInfo(ctx, 4) | ||
require.True(t, found) | ||
require.Equal(t, hi4, recv) | ||
recv, found = k.GetHistoricalInfo(ctx, 5) | ||
require.True(t, found) | ||
require.Equal(t, hi5, recv) | ||
|
||
// Set last validators in keeper | ||
val1 := types.NewValidator(sdk.ValAddress(keeper.Addrs[2]), keeper.PKs[2], types.Description{}) | ||
k.SetValidator(ctx, val1) | ||
k.SetLastValidatorPower(ctx, val1.OperatorAddress, 10) | ||
val2 := types.NewValidator(sdk.ValAddress(keeper.Addrs[3]), keeper.PKs[3], types.Description{}) | ||
vals := []types.Validator{val1, val2} | ||
sort.Sort(types.Validators(vals)) | ||
k.SetValidator(ctx, val2) | ||
k.SetLastValidatorPower(ctx, val2.OperatorAddress, 8) | ||
|
||
// Set Header for BeginBlock context | ||
header := abci.Header{ | ||
ChainID: "HelloChain", | ||
Height: 10, | ||
} | ||
ctx = ctx.WithBlockHeader(header) | ||
|
||
BeginBlocker(ctx, k) | ||
|
||
// Check HistoricalInfo at height 10 is persisted | ||
expected := types.HistoricalInfo{ | ||
Header: header, | ||
ValSet: vals, | ||
} | ||
recv, found = k.GetHistoricalInfo(ctx, 10) | ||
require.True(t, found, "GetHistoricalInfo failed after BeginBlock") | ||
require.Equal(t, expected, recv, "GetHistoricalInfo returned eunexpected result") | ||
|
||
// Check HistoricalInfo at height 5, 4 is pruned | ||
recv, found = k.GetHistoricalInfo(ctx, 4) | ||
require.False(t, found, "GetHistoricalInfo did not prune earlier height") | ||
require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune") | ||
recv, found = k.GetHistoricalInfo(ctx, 5) | ||
require.False(t, found, "GetHistoricalInfo did not prune first prune height") | ||
require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package keeper | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/staking/types" | ||
) | ||
|
||
// GetHistoricalInfo gets the historical info at a given height | ||
func (k Keeper) GetHistoricalInfo(ctx sdk.Context, height int64) (hi types.HistoricalInfo, found bool) { | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
store := ctx.KVStore(k.storeKey) | ||
key := types.GetHistoricalInfoKey(height) | ||
|
||
value := store.Get(key) | ||
|
||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if value == nil { | ||
return types.HistoricalInfo{}, false | ||
} | ||
|
||
hi = types.MustUnmarshalHistoricalInfo(k.cdc, value) | ||
return hi, true | ||
} | ||
|
||
// SetHistoricalInfo sets the historical info at a given height | ||
func (k Keeper) SetHistoricalInfo(ctx sdk.Context, height int64, hi types.HistoricalInfo) { | ||
store := ctx.KVStore(k.storeKey) | ||
key := types.GetHistoricalInfoKey(height) | ||
|
||
value := types.MustMarshalHistoricalInfo(k.cdc, hi) | ||
store.Set(key, value) | ||
} | ||
|
||
// DeleteHistoricalInfo deletes the historical info at a given height | ||
func (k Keeper) DeleteHistoricalInfo(ctx sdk.Context, height int64) { | ||
store := ctx.KVStore(k.storeKey) | ||
key := types.GetHistoricalInfoKey(height) | ||
|
||
store.Delete(key) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package keeper | ||
|
||
import ( | ||
"sort" | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/x/staking/types" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestHistoricalInfo(t *testing.T) { | ||
ctx, _, keeper, _ := CreateTestInput(t, false, 10) | ||
var validators []types.Validator | ||
AdityaSripal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for i, valAddr := range addrVals { | ||
validators = append(validators, types.NewValidator(valAddr, PKs[i], types.Description{})) | ||
} | ||
|
||
hi := types.NewHistoricalInfo(ctx.BlockHeader(), validators) | ||
|
||
keeper.SetHistoricalInfo(ctx, 2, hi) | ||
|
||
recv, found := keeper.GetHistoricalInfo(ctx, 2) | ||
require.True(t, found, "HistoricalInfo not found after set") | ||
require.Equal(t, hi, recv, "HistoricalInfo not equal") | ||
require.True(t, sort.IsSorted(types.Validators(recv.ValSet)), "HistoricalInfo validators is not sorted") | ||
|
||
keeper.DeleteHistoricalInfo(ctx, 2) | ||
|
||
recv, found = keeper.GetHistoricalInfo(ctx, 2) | ||
require.False(t, found, "HistoricalInfo found after delete") | ||
require.Equal(t, types.HistoricalInfo{}, recv, "HistoricalInfo is not empty") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single changelog entry should be made with bullet points if need be.