From cc0a6a39e3bf9250e6269d72da7cfe592afa76c3 Mon Sep 17 00:00:00 2001 From: Oren Date: Sun, 15 Dec 2024 16:03:02 +0200 Subject: [PATCH 1/3] freeze after stake unit test --- x/pairing/keeper/helpers_test.go | 16 ++++++++++++ x/pairing/keeper/pairing_test.go | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/x/pairing/keeper/helpers_test.go b/x/pairing/keeper/helpers_test.go index eea0eb175b..fef021192d 100644 --- a/x/pairing/keeper/helpers_test.go +++ b/x/pairing/keeper/helpers_test.go @@ -319,3 +319,19 @@ func (ts *tester) newRelaySession( } return relaySession } + +func (ts *tester) isProviderFrozen(provider string, chain string) bool { + res, err := ts.QueryPairingProvider(provider, chain) + require.NoError(ts.T, err) + foundChain := false + for _, stakeEntry := range res.StakeEntries { + if stakeEntry.Address == provider && stakeEntry.Chain == chain { + foundChain = true + return stakeEntry.IsFrozen() + } + } + if !foundChain { + require.Fail(ts.T, "provider not staked in chain", "provider: %s, chain: %s", provider, chain) + } + return false +} diff --git a/x/pairing/keeper/pairing_test.go b/x/pairing/keeper/pairing_test.go index c312411f1e..2972491723 100644 --- a/x/pairing/keeper/pairing_test.go +++ b/x/pairing/keeper/pairing_test.go @@ -2509,3 +2509,47 @@ func TestMaxEndpointPerGeolocationLimit(t *testing.T) { ) require.Error(t, err) } + +// TestStakeNotAffectingFreeze checks the following scenario: +// 1. a provider is staked and frozen +// 2. the provider stakes to a new chain +// 3. the provider's freeze status in the original chain is not affected +func TestStakeNotAffectingFreeze(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(0, 0, 1) // 0 sub, 0 adm, 1 dev + + var balance int64 = 10000 + stake := balance / 10 + + // Create provider account and stake them to first spec + acc, addr := ts.AddAccount(common.PROVIDER, 1, balance) + err := ts.StakeProvider(acc.GetVaultAddr(), addr, ts.spec, stake) + require.NoError(t, err) + + ts.AdvanceEpoch() + + // Freeze the provider + _, err = ts.TxPairingFreezeProvider(addr, ts.spec.Index) + require.NoError(t, err) + + // Verify provider is frozen + frozen := ts.isProviderFrozen(addr, ts.spec.Index) + require.True(t, frozen) + + // Create a second spec and stake provider to it + spec1 := ts.spec + spec1Name := "spec1" + spec1.Index, spec1.Name = spec1Name, spec1Name + ts.AddSpec(spec1Name, spec1) + err = ts.StakeProvider(acc.GetVaultAddr(), addr, spec1, stake) + require.NoError(t, err) + ts.AdvanceEpoch() + + // Verify provider is still frozen in original spec + frozen = ts.isProviderFrozen(addr, ts.spec.Index) + require.True(t, frozen) + + // Verify provider is not frozen in new spec + frozen = ts.isProviderFrozen(addr, spec1.Index) + require.False(t, frozen) +} From 8d83f8bc48933fccc38c3b758fa2c626d2bbb930 Mon Sep 17 00:00:00 2001 From: Oren Date: Sun, 15 Dec 2024 16:21:34 +0200 Subject: [PATCH 2/3] do automatic unfreeze only for going over min stake --- x/dualstaking/keeper/delegate.go | 3 +-- x/pairing/keeper/staking.go | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/x/dualstaking/keeper/delegate.go b/x/dualstaking/keeper/delegate.go index 3bf28f4343..c4a398c8aa 100644 --- a/x/dualstaking/keeper/delegate.go +++ b/x/dualstaking/keeper/delegate.go @@ -165,9 +165,8 @@ func (k Keeper) AfterDelegationModified(ctx sdk.Context, delegator, provider str details["stake"] = entry.TotalStake().String() utils.LogLavaEvent(ctx, k.Logger(ctx), types.FreezeFromUnbond, details, "freezing provider due to stake below min spec stake") entry.Freeze() - } else if delegator == entry.Vault && entry.IsFrozen() && !entry.IsJailed(ctx.BlockTime().UTC().Unix()) { - entry.UnFreeze(k.epochstorageKeeper.GetCurrentNextEpoch(ctx) + 1) } + k.epochstorageKeeper.SetStakeEntryCurrent(ctx, *entry) } diff --git a/x/pairing/keeper/staking.go b/x/pairing/keeper/staking.go index 4e4d8ae221..f9c04cf7fc 100644 --- a/x/pairing/keeper/staking.go +++ b/x/pairing/keeper/staking.go @@ -84,6 +84,14 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin // new staking takes effect from the next block stakeAppliedBlock := uint64(ctx.BlockHeight()) + 1 + nextEpoch, err := k.epochStorageKeeper.GetNextEpoch(ctx, uint64(ctx.BlockHeight())) + if err != nil { + return utils.LavaFormatWarning("cannot get next epoch to count past delegations", err, + utils.LogAttr("provider", senderAddr.String()), + utils.LogAttr("block", nextEpoch), + ) + } + existingEntry, entryExists := k.epochStorageKeeper.GetStakeEntryCurrent(ctx, chainID, creator) if entryExists { // modify the entry (check who's modifying - vault/provider) @@ -175,6 +183,13 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin details..., ) } + + // automatically unfreeze the provider if it was frozen due to stake below min spec stake + minSpecStake := k.specKeeper.GetMinStake(ctx, chainID) + if beforeAmount.IsLT(minSpecStake) && existingEntry.IsFrozen() && !existingEntry.IsJailed(ctx.BlockTime().UTC().Unix()) && amount.IsGTE(minSpecStake) { + existingEntry.UnFreeze(nextEpoch) + k.epochStorageKeeper.SetStakeEntryCurrent(ctx, existingEntry) + } } else if decrease { // unbond the difference diffAmount := beforeAmount.Sub(amount) @@ -228,15 +243,8 @@ func (k Keeper) StakeNewEntry(ctx sdk.Context, validator, creator, chainID strin // if there are registered delegations to the provider, count them in the delegateTotal delegateTotal := sdk.ZeroInt() - nextEpoch, err := k.epochStorageKeeper.GetNextEpoch(ctx, uint64(ctx.BlockHeight())) - if err != nil { - return utils.LavaFormatWarning("cannot get next epoch to count past delegations", err, - utils.LogAttr("provider", senderAddr.String()), - utils.LogAttr("block", nextEpoch), - ) - } - stakeAmount := amount + // creating a new provider, fetch old delegation if len(metadata.Chains) == 1 { delegations, err := k.dualstakingKeeper.GetProviderDelegators(ctx, provider) From 7903158564d5a7b352a147e685198563d723e249 Mon Sep 17 00:00:00 2001 From: Oren Date: Sun, 15 Dec 2024 16:58:34 +0200 Subject: [PATCH 3/3] small fix --- x/pairing/keeper/helpers_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x/pairing/keeper/helpers_test.go b/x/pairing/keeper/helpers_test.go index fef021192d..f45473d574 100644 --- a/x/pairing/keeper/helpers_test.go +++ b/x/pairing/keeper/helpers_test.go @@ -324,14 +324,16 @@ func (ts *tester) isProviderFrozen(provider string, chain string) bool { res, err := ts.QueryPairingProvider(provider, chain) require.NoError(ts.T, err) foundChain := false + var isFrozen bool for _, stakeEntry := range res.StakeEntries { if stakeEntry.Address == provider && stakeEntry.Chain == chain { foundChain = true - return stakeEntry.IsFrozen() + isFrozen = stakeEntry.IsFrozen() + break } } if !foundChain { require.Fail(ts.T, "provider not staked in chain", "provider: %s, chain: %s", provider, chain) } - return false + return isFrozen }