From 68a677f1eb6aaa1137d08b416fd18a5c78bb6240 Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 10 Feb 2025 15:59:21 +0200 Subject: [PATCH 1/8] fix --- x/epochstorage/keeper/keeper.go | 7 ++- x/epochstorage/keeper/provider_metadata.go | 19 ++++++++ x/epochstorage/types/provider_metdata.go | 43 +++++++++++++++++++ .../keeper/msg_server_stake_provider_test.go | 17 ++++++++ x/pairing/keeper/staking.go | 10 +++++ x/pairing/types/expected_keepers.go | 1 + 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 x/epochstorage/types/provider_metdata.go diff --git a/x/epochstorage/keeper/keeper.go b/x/epochstorage/keeper/keeper.go index 23389ad0de..d2bbeac8c7 100644 --- a/x/epochstorage/keeper/keeper.go +++ b/x/epochstorage/keeper/keeper.go @@ -34,7 +34,7 @@ type ( stakeEntries *collections.IndexedMap[collections.Triple[uint64, string, collections.Pair[uint64, string]], types.StakeEntry, types.EpochChainIdProviderIndexes] stakeEntriesCurrent *collections.IndexedMap[collections.Pair[string, string], types.StakeEntry, types.ChainIdVaultIndexes] epochHashes collections.Map[uint64, []byte] - providersMetaData collections.Map[string, types.ProviderMetadata] + providersMetaData *collections.IndexedMap[string, types.ProviderMetadata, types.MetadataVaultIndexes] } ) @@ -79,7 +79,10 @@ func NewKeeper( epochHashes: collections.NewMap(sb, types.EpochHashesPrefix, "epoch_hashes", collections.Uint64Key, collections.BytesValue), - providersMetaData: collections.NewMap(sb, types.ProviderMetaDataPrefix, "provider_metadata", collections.StringKey, collcompat.ProtoValue[types.ProviderMetadata](cdc)), + providersMetaData: collections.NewIndexedMap(sb, types.ProviderMetaDataPrefix, "provider_metadata", + collections.StringKey, + collcompat.ProtoValue[types.ProviderMetadata](cdc), + types.NewMetadataVaultIndexes(sb)), } keeper.AddFixationRegistry(string(types.KeyEpochBlocks), func(ctx sdk.Context) any { return keeper.EpochBlocksRaw(ctx) }) diff --git a/x/epochstorage/keeper/provider_metadata.go b/x/epochstorage/keeper/provider_metadata.go index 5ee91bd5e7..a7c0596cbd 100644 --- a/x/epochstorage/keeper/provider_metadata.go +++ b/x/epochstorage/keeper/provider_metadata.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/v5/utils" "github.com/lavanet/lava/v5/x/epochstorage/types" ) @@ -33,3 +34,21 @@ func (k Keeper) GetAllMetadata(ctx sdk.Context) ([]types.ProviderMetadata, error } return iter.Values() } + +// GetProviderMetadataByVault gets the provider metadata for a specific vault address +func (k Keeper) GetProviderMetadataByVault(ctx sdk.Context, vault string) (val types.ProviderMetadata, found bool) { + pk, err := k.providersMetaData.Indexes.Index.MatchExact(ctx, vault) + if err != nil { + return types.ProviderMetadata{}, false + } + + entry, err := k.providersMetaData.Get(ctx, pk) + if err != nil { + utils.LavaFormatError("GetProviderMetadataByVault: Get with primary key failed", err, + utils.LogAttr("vault", vault), + ) + return types.ProviderMetadata{}, false + } + + return entry, true +} diff --git a/x/epochstorage/types/provider_metdata.go b/x/epochstorage/types/provider_metdata.go new file mode 100644 index 0000000000..fee4202d20 --- /dev/null +++ b/x/epochstorage/types/provider_metdata.go @@ -0,0 +1,43 @@ +package types + +import ( + fmt "fmt" + + "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" + "github.com/lavanet/lava/v5/utils" +) + +var ( + MetadataVaultIndexesPrefix = collections.NewPrefix([]byte("MetadataVaultIndexes/")) +) + +// ChainIdVaultIndexes defines a secondary unique index for the keeper's stakeEntriesCurrent indexed map +// Normally, a current stake entry can be accessed with the primary key: [chainID, address] +// The new set of indexes, ChainIdVaultIndexes, allows accessing the stake entries with [chainID, vault] +type MetadataVaultIndexes struct { + Index *indexes.Unique[string, string, ProviderMetadata] +} + +func (c MetadataVaultIndexes) IndexesList() []collections.Index[string, ProviderMetadata] { + return []collections.Index[string, ProviderMetadata]{c.Index} +} + +func NewMetadataVaultIndexes(sb *collections.SchemaBuilder) MetadataVaultIndexes { + return MetadataVaultIndexes{ + Index: indexes.NewUnique(sb, MetadataVaultIndexesPrefix, "provider_metadata_by_vault", + collections.StringKey, + collections.StringKey, + func(pk string, entry ProviderMetadata) (string, error) { + if entry.Vault == "" { + return "", + utils.LavaFormatError("NewMetadataVaultIndexes: cannot create new MetadataVault index", + fmt.Errorf("empty vault address"), + utils.LogAttr("provider", entry.Provider), + ) + } + return entry.Vault, nil + }, + ), + } +} diff --git a/x/pairing/keeper/msg_server_stake_provider_test.go b/x/pairing/keeper/msg_server_stake_provider_test.go index 32b741cc90..fad7b5b4d2 100644 --- a/x/pairing/keeper/msg_server_stake_provider_test.go +++ b/x/pairing/keeper/msg_server_stake_provider_test.go @@ -1245,3 +1245,20 @@ func TestDelegatorAfterProviderUnstakeAndStake(t *testing.T) { }) } } + +// TestStakeProviderWithSameVault tests that a vault address cannot be used to stake for multiple providers. +// It verifies that: +// 1. A vault can stake for itself as a provider +// 2. The same vault cannot stake for a different provider address +func TestStakeProviderWithSameVault(t *testing.T) { + ts := newTester(t) + SetupForSingleProviderTests(ts, 0, 3, 0) + + provider, _ := ts.AddAccount(common.PROVIDER, 1, 100*testBalance) + + err := ts.StakeProvider(provider.GetVaultAddr(), provider.GetVaultAddr(), ts.Spec(SpecName(0)), 100) + require.NoError(t, err) + + err = ts.StakeProvider(provider.GetVaultAddr(), provider.Addr.String(), ts.Spec(SpecName(0)), 100) + require.Error(t, err) +} diff --git a/x/pairing/keeper/staking.go b/x/pairing/keeper/staking.go index cf60f32643..d26273cce8 100644 --- a/x/pairing/keeper/staking.go +++ b/x/pairing/keeper/staking.go @@ -37,6 +37,16 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin metadata.Chains = lavaslices.AddUnique(metadata.Chains, chainID) } + vaultMetadata, found := k.epochStorageKeeper.GetProviderMetadataByVault(ctx, creator) + if found { + if vaultMetadata.Provider != provider || vaultMetadata.Vault != creator { + return utils.LavaFormatWarning("provider metadata mismatch", fmt.Errorf("provider metadata mismatch"), + utils.LogAttr("provider", provider), + utils.LogAttr("vault", creator), + ) + } + } + spec, err := k.specKeeper.GetExpandedSpec(ctx, specChainID) if err != nil || !spec.Enabled { return utils.LavaFormatWarning("spec not found or not active", err, diff --git a/x/pairing/types/expected_keepers.go b/x/pairing/types/expected_keepers.go index c7007e2c16..18692aafc7 100644 --- a/x/pairing/types/expected_keepers.go +++ b/x/pairing/types/expected_keepers.go @@ -55,6 +55,7 @@ type EpochstorageKeeper interface { GetEpochHash(ctx sdk.Context, epoch uint64) []byte GetMetadata(ctx sdk.Context, provider string) (epochstoragetypes.ProviderMetadata, error) SetMetadata(ctx sdk.Context, metadata epochstoragetypes.ProviderMetadata) + GetProviderMetadataByVault(ctx sdk.Context, vault string) (epochstoragetypes.ProviderMetadata, bool) } type AccountKeeper interface { From 18875b2a3c3383a90e0ddea99a23769cbe0bbbbe Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 10 Feb 2025 16:27:45 +0200 Subject: [PATCH 2/8] fix lint --- x/epochstorage/types/provider_metdata.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x/epochstorage/types/provider_metdata.go b/x/epochstorage/types/provider_metdata.go index fee4202d20..045f39d86a 100644 --- a/x/epochstorage/types/provider_metdata.go +++ b/x/epochstorage/types/provider_metdata.go @@ -8,9 +8,7 @@ import ( "github.com/lavanet/lava/v5/utils" ) -var ( - MetadataVaultIndexesPrefix = collections.NewPrefix([]byte("MetadataVaultIndexes/")) -) +var MetadataVaultIndexesPrefix = collections.NewPrefix([]byte("MetadataVaultIndexes/")) // ChainIdVaultIndexes defines a secondary unique index for the keeper's stakeEntriesCurrent indexed map // Normally, a current stake entry can be accessed with the primary key: [chainID, address] From 15f849a2e962f22c74fdae3ce0220c52000fdf4b Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 10 Feb 2025 17:16:46 +0200 Subject: [PATCH 3/8] migrator --- x/epochstorage/keeper/migrations.go | 22 ++++++++++++++++++++++ x/epochstorage/module.go | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/x/epochstorage/keeper/migrations.go b/x/epochstorage/keeper/migrations.go index 531b0ce743..ec1b11ef47 100644 --- a/x/epochstorage/keeper/migrations.go +++ b/x/epochstorage/keeper/migrations.go @@ -124,3 +124,25 @@ func (m Migrator) SetEpochHashForMigrator(ctx sdk.Context, epoch uint64, hash [] panic(err) } } + +// fix provider metadata to be consistent with the stake entries +func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { + allMetadata, err := m.keeper.GetAllMetadata(ctx) + if err != nil { + return err + } + + for _, metadata := range allMetadata { + chains := []string{} + for _, chainID := range metadata.Chains { + stakeEntry, found := m.keeper.GetStakeEntryCurrent(ctx, chainID, metadata.Provider) + if found { + chains = append(chains, stakeEntry.Chain) + } + } + metadata.Chains = chains + m.keeper.SetMetadata(ctx, metadata) + } + + return nil +} diff --git a/x/epochstorage/module.go b/x/epochstorage/module.go index f3e6011a60..69c8094b70 100644 --- a/x/epochstorage/module.go +++ b/x/epochstorage/module.go @@ -135,6 +135,11 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { // panic:ok: at start up, migration cannot proceed anyhow panic(fmt.Errorf("%s: failed to register migration to v8: %w", types.ModuleName, err)) } + + // register v8 -> v9 migration + if err := cfg.RegisterMigration(types.ModuleName, 8, migrator.MigrateVersion8To9); err != nil { + panic(fmt.Errorf("%s: failed to register migration to v9: %w", types.ModuleName, err)) + } } // RegisterInvariants registers the capability module's invariants. From 3fd90ade49bfddbcf71cad57734fbdbfccbeea34 Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 10 Feb 2025 17:22:35 +0200 Subject: [PATCH 4/8] add debug print --- x/epochstorage/keeper/migrations.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/epochstorage/keeper/migrations.go b/x/epochstorage/keeper/migrations.go index ec1b11ef47..663bbe345a 100644 --- a/x/epochstorage/keeper/migrations.go +++ b/x/epochstorage/keeper/migrations.go @@ -138,6 +138,8 @@ func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { stakeEntry, found := m.keeper.GetStakeEntryCurrent(ctx, chainID, metadata.Provider) if found { chains = append(chains, stakeEntry.Chain) + } else { + fmt.Println("stake entry not found for provider: ", metadata.Provider, " and chain: ", chainID) } } metadata.Chains = chains From 4262e5f02e888032b6656931adc5cac938fb2e95 Mon Sep 17 00:00:00 2001 From: Yaroms Date: Tue, 11 Feb 2025 14:37:39 +0200 Subject: [PATCH 5/8] safely migrate --- x/epochstorage/keeper/migrations.go | 9 +++++++++ x/epochstorage/module.go | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/x/epochstorage/keeper/migrations.go b/x/epochstorage/keeper/migrations.go index 663bbe345a..4bc9e61f9a 100644 --- a/x/epochstorage/keeper/migrations.go +++ b/x/epochstorage/keeper/migrations.go @@ -133,6 +133,14 @@ func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { } for _, metadata := range allMetadata { + if metadata.Provider != metadata.Vault { + _, err := m.keeper.GetMetadata(ctx, metadata.Vault) + if err == nil { + fmt.Println("exists for vault with different provider: ", metadata.Vault, " and provider: ", metadata.Provider) + continue + } + } + chains := []string{} for _, chainID := range metadata.Chains { stakeEntry, found := m.keeper.GetStakeEntryCurrent(ctx, chainID, metadata.Provider) @@ -144,6 +152,7 @@ func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { } metadata.Chains = chains m.keeper.SetMetadata(ctx, metadata) + } return nil diff --git a/x/epochstorage/module.go b/x/epochstorage/module.go index 69c8094b70..99064e004c 100644 --- a/x/epochstorage/module.go +++ b/x/epochstorage/module.go @@ -164,7 +164,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 8 } +func (AppModule) ConsensusVersion() uint64 { return 9 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { From 17c48a337a0ee43af5385a48e44642b1afbe8592 Mon Sep 17 00:00:00 2001 From: Yaroms Date: Tue, 11 Feb 2025 14:38:10 +0200 Subject: [PATCH 6/8] add handler --- app/app.go | 1 + app/upgrades/empty_upgrades.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/app.go b/app/app.go index 7ac9f15ada..179d59ceda 100644 --- a/app/app.go +++ b/app/app.go @@ -171,6 +171,7 @@ var Upgrades = []upgrades.Upgrade{ upgrades.Upgrade_4_2_0, upgrades.Upgrade_5_0_0, upgrades.Upgrade_5_1_0, + upgrades.Upgrade_5_2_0, } // this line is used by starport scaffolding # stargate/wasm/app/enabledProposals diff --git a/app/upgrades/empty_upgrades.go b/app/upgrades/empty_upgrades.go index 25eea7460e..4a3fd9adfa 100644 --- a/app/upgrades/empty_upgrades.go +++ b/app/upgrades/empty_upgrades.go @@ -75,3 +75,9 @@ var Upgrade_5_1_0 = Upgrade{ CreateUpgradeHandler: defaultUpgradeHandler, StoreUpgrades: store.StoreUpgrades{}, } + +var Upgrade_5_2_0 = Upgrade{ + UpgradeName: "v5.2.0", + CreateUpgradeHandler: defaultUpgradeHandler, + StoreUpgrades: store.StoreUpgrades{}, +} From d2d9467f46f9b5c3e2f924413dafc167d4ca8c68 Mon Sep 17 00:00:00 2001 From: Oren Date: Tue, 11 Feb 2025 15:06:55 +0200 Subject: [PATCH 7/8] fix lint --- app/app.go | 2 +- x/epochstorage/keeper/migrations.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index 179d59ceda..6c379564f8 100644 --- a/app/app.go +++ b/app/app.go @@ -1087,7 +1087,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(stakingtypes.ModuleName) paramsKeeper.Subspace(distrtypes.ModuleName) paramsKeeper.Subspace(slashingtypes.ModuleName) - paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(v1.ParamKeyTable()) //nolint:staticcheck + paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(v1.ParamKeyTable()) //nolint paramsKeeper.Subspace(crisistypes.ModuleName) paramsKeeper.Subspace(ibctransfertypes.ModuleName) paramsKeeper.Subspace(ibcexported.ModuleName) diff --git a/x/epochstorage/keeper/migrations.go b/x/epochstorage/keeper/migrations.go index 4bc9e61f9a..3369261919 100644 --- a/x/epochstorage/keeper/migrations.go +++ b/x/epochstorage/keeper/migrations.go @@ -152,7 +152,6 @@ func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { } metadata.Chains = chains m.keeper.SetMetadata(ctx, metadata) - } return nil From 8992166e6ea491825d675afe7faf776ece05e47b Mon Sep 17 00:00:00 2001 From: Oren Date: Tue, 11 Feb 2025 15:33:03 +0200 Subject: [PATCH 8/8] remove vault metadata --- x/epochstorage/keeper/migrations.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x/epochstorage/keeper/migrations.go b/x/epochstorage/keeper/migrations.go index 3369261919..a1a6116b93 100644 --- a/x/epochstorage/keeper/migrations.go +++ b/x/epochstorage/keeper/migrations.go @@ -136,7 +136,11 @@ func (m Migrator) MigrateVersion8To9(ctx sdk.Context) error { if metadata.Provider != metadata.Vault { _, err := m.keeper.GetMetadata(ctx, metadata.Vault) if err == nil { - fmt.Println("exists for vault with different provider: ", metadata.Vault, " and provider: ", metadata.Provider) + fmt.Println("exists for vault with different provider: ", metadata.Vault, " and provider: ", metadata.Provider, "removing metadata") + err := m.keeper.RemoveMetadata(ctx, metadata.Vault) + if err != nil { + return err + } continue } }