Skip to content
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

fix: vault double stake bug #1954

Merged
merged 9 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 5 additions & 2 deletions x/epochstorage/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
)

Expand Down Expand Up @@ -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) })
Expand Down
19 changes: 19 additions & 0 deletions x/epochstorage/keeper/provider_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}
41 changes: 41 additions & 0 deletions x/epochstorage/types/provider_metdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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
},
),
}
}
17 changes: 17 additions & 0 deletions x/pairing/keeper/msg_server_stake_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
10 changes: 10 additions & 0 deletions x/pairing/keeper/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions x/pairing/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading