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: unfreeze after new stake bug #1837

Merged
merged 6 commits into from
Dec 16, 2024
Merged
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
3 changes: 1 addition & 2 deletions x/dualstaking/keeper/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
18 changes: 18 additions & 0 deletions x/pairing/keeper/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,21 @@ 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
var isFrozen bool
for _, stakeEntry := range res.StakeEntries {
if stakeEntry.Address == provider && stakeEntry.Chain == chain {
foundChain = true
isFrozen = stakeEntry.IsFrozen()
break
}
}
if !foundChain {
require.Fail(ts.T, "provider not staked in chain", "provider: %s, chain: %s", provider, chain)
}
return isFrozen
}
44 changes: 44 additions & 0 deletions x/pairing/keeper/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
24 changes: 16 additions & 8 deletions x/pairing/keeper/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading