Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions blockstm/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ func (ms msWrapper) LatestVersion() int64 {
panic("implement me")
}

func (ms msWrapper) EarliestVersion() int64 {
// TODO implement me
panic("implement me")
}

func (ms msWrapper) getCacheWrapper(key storetypes.StoreKey) storetypes.CacheWrapper {
return ms.GetStore(key)
}
Expand Down
23 changes: 12 additions & 11 deletions client/grpc/node/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import (
gogogrpc "github.com/cosmos/gogoproto/grpc"
"github.com/grpc-ecosystem/grpc-gateway/runtime"

storetypes "cosmossdk.io/store/types"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// RegisterNodeService registers the node gRPC service on the provided gRPC router.
func RegisterNodeService(clientCtx client.Context, server gogogrpc.Server, cfg config.Config) {
RegisterServiceServer(server, NewQueryServer(clientCtx, cfg))
func RegisterNodeService(clientCtx client.Context, server gogogrpc.Server, cfg config.Config, cms storetypes.CommitMultiStore) {
RegisterServiceServer(server, NewQueryServer(clientCtx, cfg, cms))
}

// RegisterGRPCGatewayRoutes mounts the node gRPC service's GRPC-gateway routes
Expand All @@ -27,12 +29,14 @@ var _ ServiceServer = queryServer{}
type queryServer struct {
clientCtx client.Context
cfg config.Config
cms storetypes.CommitMultiStore
}

func NewQueryServer(clientCtx client.Context, cfg config.Config) ServiceServer {
func NewQueryServer(clientCtx client.Context, cfg config.Config, cms storetypes.CommitMultiStore) ServiceServer {
return queryServer{
clientCtx: clientCtx,
cfg: cfg,
cms: cms,
}
}

Expand All @@ -53,13 +57,10 @@ func (s queryServer) Status(ctx context.Context, _ *StatusRequest) (*StatusRespo
blockTime := sdkCtx.BlockTime()

return &StatusResponse{
// TODO: Get earliest version from store.
//
// Ref: ...
// EarliestStoreHeight: sdkCtx.MultiStore(),
Height: uint64(sdkCtx.BlockHeight()),
Timestamp: &blockTime,
AppHash: sdkCtx.BlockHeader().AppHash,
ValidatorHash: sdkCtx.BlockHeader().NextValidatorsHash,
EarliestStoreHeight: uint64(s.cms.EarliestVersion()),
Height: uint64(sdkCtx.BlockHeight()),
Timestamp: &blockTime,
AppHash: sdkCtx.BlockHeader().AppHash,
ValidatorHash: sdkCtx.BlockHeader().NextValidatorsHash,
}, nil
}
2 changes: 1 addition & 1 deletion client/grpc/node/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestServiceServer_Config(t *testing.T) {
defaultCfg.PruningKeepRecent = "2000"
defaultCfg.PruningInterval = "10"
defaultCfg.HaltHeight = 100
svr := NewQueryServer(client.Context{}, *defaultCfg)
svr := NewQueryServer(client.Context{}, *defaultCfg, nil)
ctx := sdk.Context{}.WithMinGasPrices(sdk.NewDecCoins(sdk.NewInt64DecCoin("stake", 15)))

resp, err := svr.Config(ctx, &ConfigRequest{})
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ require (

// Below are the long-lived replace of the Cosmos SDK
replace (
// Use local store module for EarliestVersion implementation
cosmossdk.io/store => ./store
// use cosmos fork of keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// dgrijalva/jwt-go is deprecated and doesn't receive security updates.
Expand Down
2 changes: 1 addition & 1 deletion runtime/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func (a *App) RegisterTendermintService(clientCtx client.Context) {

// RegisterNodeService registers the node gRPC service on the app gRPC router.
func (a *App) RegisterNodeService(clientCtx client.Context, cfg config.Config) {
nodeservice.RegisterNodeService(clientCtx, a.GRPCQueryRouter(), cfg)
nodeservice.RegisterNodeService(clientCtx, a.GRPCQueryRouter(), cfg, a.CommitMultiStore())
}

// Configurator returns the app's configurator.
Expand Down
4 changes: 4 additions & 0 deletions server/mock/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ func (ms multiStore) LatestVersion() int64 {
panic("not implemented")
}

func (ms multiStore) EarliestVersion() int64 {
panic("not implemented")
}

func (ms multiStore) WorkingHash() []byte {
panic("not implemented")
}
Expand Down
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ func (app *SimApp) RegisterTendermintService(clientCtx client.Context) {
}

func (app *SimApp) RegisterNodeService(clientCtx client.Context, cfg config.Config) {
nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg)
nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg, app.CommitMultiStore())
}

// GetMaccPerms returns a copy of the module account permissions
Expand Down
3 changes: 3 additions & 0 deletions simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
cosmossdk.io/collections v1.3.1 // indirect
cosmossdk.io/errors v1.0.2 // indirect
cosmossdk.io/schema v1.1.0 // indirect
cosmossdk.io/x/feegrant v0.2.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
Expand Down Expand Up @@ -264,6 +265,8 @@ require (

// Below are the long-lived replace of the SimApp
replace (
// Use local store module for EarliestVersion implementation
cosmossdk.io/store => ../store
// use cosmos fork of keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// Simapp always use the latest version of the cosmos-sdk
Expand Down
4 changes: 2 additions & 2 deletions simapp/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ cosmossdk.io/math v1.5.3 h1:WH6tu6Z3AUCeHbeOSHg2mt9rnoiUWVWaQ2t6Gkll96U=
cosmossdk.io/math v1.5.3/go.mod h1:uqcZv7vexnhMFJF+6zh9EWdm/+Ylyln34IvPnBauPCQ=
cosmossdk.io/schema v1.1.0 h1:mmpuz3dzouCoyjjcMcA/xHBEmMChN+EHh8EHxHRHhzE=
cosmossdk.io/schema v1.1.0/go.mod h1:Gb7pqO+tpR+jLW5qDcNOSv0KtppYs7881kfzakguhhI=
cosmossdk.io/store v1.3.0-beta.0 h1:jwJvAQkMsCY9xJHU/nz7yOo1WnNRvcI/9yLRSgZoFTk=
cosmossdk.io/store v1.3.0-beta.0/go.mod h1:CMz9JQGEA8eRcZv2pK07NgEbL4NEb9wVgzWK4tNQaPg=
cosmossdk.io/tools/confix v0.1.2 h1:2hoM1oFCNisd0ltSAAZw2i4ponARPmlhuNu3yy0VwI4=
cosmossdk.io/tools/confix v0.1.2/go.mod h1:7XfcbK9sC/KNgVGxgLM0BrFbVcR/+6Dg7MFfpx7duYo=
cosmossdk.io/x/feegrant v0.2.0 h1:oq3WVpoJdxko/XgWmpib63V1mYy9ZQN/1qxDajwGzJ8=
cosmossdk.io/x/feegrant v0.2.0/go.mod h1:9CutZbmhulk/Yo6tQSVD5LG8Lk40ZAQ1OX4d1CODWAE=
cosmossdk.io/x/tx v0.14.0 h1:hB3O25kIcyDW/7kMTLMaO8Ripj3yqs5imceVd6c/heA=
cosmossdk.io/x/tx v0.14.0/go.mod h1:Tn30rSRA1PRfdGB3Yz55W4Sn6EIutr9xtMKSHij+9PM=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
Expand Down
5 changes: 5 additions & 0 deletions store/cachemulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ func (cms Store) LatestVersion() int64 {
panic("cannot get latest version from branch cached multi-store")
}

// EarliestVersion returns the earliest version of the store
func (cms Store) EarliestVersion() int64 {
panic("cannot get earliest version from branch cached multi-store")
}

// GetStoreType returns the type of the store.
func (cms Store) GetStoreType() types.StoreType {
return types.StoreTypeMulti
Expand Down
54 changes: 52 additions & 2 deletions store/rootmulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ import (
)

const (
latestVersionKey = "s/latest"
commitInfoKeyFmt = "s/%d" // s/<version>
latestVersionKey = "s/latest"
earliestVersionKey = "s/earliest"
commitInfoKeyFmt = "s/%d" // s/<version>
)

const iavlDisablefastNodeDefault = false
Expand Down Expand Up @@ -455,6 +456,11 @@ func (rs *Store) LatestVersion() int64 {
return rs.LastCommitID().Version
}

// EarliestVersion returns the earliest version in the store
func (rs *Store) EarliestVersion() int64 {
return GetEarliestVersion(rs.db)
}

// LastCommitID implements Committer/CommitStore.
func (rs *Store) LastCommitID() types.CommitID {
info := rs.lastCommitInfo.Load()
Expand Down Expand Up @@ -752,6 +758,20 @@ func (rs *Store) PruneStores(pruningHeight int64) (err error) {

rs.logger.Error("failed to prune store", "key", key, "err", err)
}

// Update earliest version after successful pruning.
// The new earliest available version is pruningHeight + 1.
newEarliest := pruningHeight + 1
currentEarliest := GetEarliestVersion(rs.db)
if newEarliest > currentEarliest {
batch := rs.db.NewBatch()
defer batch.Close()
flushEarliestVersion(batch, newEarliest)
if err := batch.WriteSync(); err != nil {
rs.logger.Error("failed to persist earliest version", "err", err)
}
}

return nil
}

Expand Down Expand Up @@ -1222,6 +1242,36 @@ func GetLatestVersion(db dbm.DB) int64 {
return latestVersion
}

// GetEarliestVersion returns the earliest version stored in the database.
// Returns 1 if no earliest version has been explicitly set (unpruned chain).
func GetEarliestVersion(db dbm.DB) int64 {
bz, err := db.Get([]byte(earliestVersionKey))
if err != nil {
panic(err)
} else if bz == nil {
return 1 // default to 1 for unpruned chains
}

var earliestVersion int64

if err := gogotypes.StdInt64Unmarshal(&earliestVersion, bz); err != nil {
panic(err)
}

return earliestVersion
}

func flushEarliestVersion(batch dbm.Batch, version int64) {
bz, err := gogotypes.StdInt64Marshal(version)
if err != nil {
panic(err)
}

if err := batch.Set([]byte(earliestVersionKey), bz); err != nil {
panic(err)
}
}

// commitStores commits each store and returns a new commitInfo.
func commitStores(version int64, storeMap map[types.StoreKey]types.CommitStore, removalMap map[types.StoreKey]bool) *types.CommitInfo {
storeInfos := make([]types.StoreInfo, 0, len(storeMap))
Expand Down
74 changes: 74 additions & 0 deletions store/rootmulti/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1169,3 +1169,77 @@ func TestCommitStores(t *testing.T) {
})
}
}

func TestEarliestVersion(t *testing.T) {
db := dbm.NewMemDB()
ms := newMultiStoreWithMounts(db, pruningtypes.NewPruningOptions(pruningtypes.PruningNothing))
require.NoError(t, ms.LoadLatestVersion())

// Initially, earliest version should be 1 (default for unpruned chains)
require.Equal(t, int64(1), ms.EarliestVersion())

// Commit some versions
for i := 0; i < 5; i++ {
ms.Commit()
}

// Earliest version should still be 1
require.Equal(t, int64(1), ms.EarliestVersion())
require.Equal(t, int64(5), ms.LatestVersion())
}

func TestEarliestVersionWithPruning(t *testing.T) {
db := dbm.NewMemDB()
// keepRecent=2, interval=1 means prune aggressively
ms := newMultiStoreWithMounts(db, pruningtypes.NewCustomPruningOptions(2, 1))
require.NoError(t, ms.LoadLatestVersion())

// Initially, earliest version should be 1
require.Equal(t, int64(1), ms.EarliestVersion())

// Commit enough versions to trigger pruning
for i := 0; i < 10; i++ {
ms.Commit()
}

// Wait for async pruning to complete and check earliest version is updated
checkEarliest := func() bool {
return ms.EarliestVersion() > 1
}
require.Eventually(t, checkEarliest, 1*time.Second, 10*time.Millisecond,
"expected earliest version to be updated after pruning")

// Earliest version should now be greater than 1 (pruned heights + 1)
earliest := ms.EarliestVersion()
require.Greater(t, earliest, int64(1), "earliest version should be updated after pruning")

// Latest should still be 10
require.Equal(t, int64(10), ms.LatestVersion())
}

func TestEarliestVersionPersistence(t *testing.T) {
db := dbm.NewMemDB()
ms := newMultiStoreWithMounts(db, pruningtypes.NewCustomPruningOptions(2, 1))
require.NoError(t, ms.LoadLatestVersion())

// Commit and prune
for i := 0; i < 10; i++ {
ms.Commit()
}

// Wait for pruning
checkEarliest := func() bool {
return ms.EarliestVersion() > 1
}
require.Eventually(t, checkEarliest, 1*time.Second, 10*time.Millisecond)

earliestBeforeRestart := ms.EarliestVersion()

// "Restart" by creating new store with same db
ms2 := newMultiStoreWithMounts(db, pruningtypes.NewCustomPruningOptions(2, 1))
require.NoError(t, ms2.LoadLatestVersion())

// Earliest version should be persisted and restored
require.Equal(t, earliestBeforeRestart, ms2.EarliestVersion(),
"earliest version should persist across restarts")
}
3 changes: 3 additions & 0 deletions store/types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ type MultiStore interface {

// LatestVersion returns the latest version in the store
LatestVersion() int64

// EarliestVersion returns the earliest version in the store
EarliestVersion() int64
}

// CacheMultiStore extends MultiStore with a Write() method.
Expand Down
2 changes: 2 additions & 0 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ require (
replace (
// We always want to test against the latest version of the simapp.
cosmossdk.io/simapp => ../simapp
// Use local store module for EarliestVersion implementation
cosmossdk.io/store => ../store
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// We always want to test against the latest version of the SDK.
github.com/cosmos/cosmos-sdk => ../.
Expand Down
4 changes: 2 additions & 2 deletions tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ cosmossdk.io/math v1.5.3 h1:WH6tu6Z3AUCeHbeOSHg2mt9rnoiUWVWaQ2t6Gkll96U=
cosmossdk.io/math v1.5.3/go.mod h1:uqcZv7vexnhMFJF+6zh9EWdm/+Ylyln34IvPnBauPCQ=
cosmossdk.io/schema v1.1.0 h1:mmpuz3dzouCoyjjcMcA/xHBEmMChN+EHh8EHxHRHhzE=
cosmossdk.io/schema v1.1.0/go.mod h1:Gb7pqO+tpR+jLW5qDcNOSv0KtppYs7881kfzakguhhI=
cosmossdk.io/store v1.3.0-beta.0 h1:jwJvAQkMsCY9xJHU/nz7yOo1WnNRvcI/9yLRSgZoFTk=
cosmossdk.io/store v1.3.0-beta.0/go.mod h1:CMz9JQGEA8eRcZv2pK07NgEbL4NEb9wVgzWK4tNQaPg=
cosmossdk.io/x/feegrant v0.2.0 h1:oq3WVpoJdxko/XgWmpib63V1mYy9ZQN/1qxDajwGzJ8=
cosmossdk.io/x/feegrant v0.2.0/go.mod h1:9CutZbmhulk/Yo6tQSVD5LG8Lk40ZAQ1OX4d1CODWAE=
cosmossdk.io/x/tx v0.14.0 h1:hB3O25kIcyDW/7kMTLMaO8Ripj3yqs5imceVd6c/heA=
cosmossdk.io/x/tx v0.14.0/go.mod h1:Tn30rSRA1PRfdGB3Yz55W4Sn6EIutr9xtMKSHij+9PM=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
Expand Down
Loading