feat: add unresolved claim cleanup

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2024-11-06 13:00:00 +01:00
parent 764d1158dc
commit 01100ec219
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
2 changed files with 74 additions and 16 deletions

View File

@ -10,9 +10,11 @@ import (
"strconv"
"time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/planetmint/planetmint-go/lib"
"github.com/planetmint/planetmint-go/monitor"
"github.com/planetmint/planetmint-go/testutil"
@ -85,6 +87,17 @@ func (s *SelectionE2ETestSuite) SetupSuite() {
daoGenState.Params.ClaimAddress = valAddr.String()
s.cfg.GenesisState[daotypes.ModuleName] = s.cfg.Codec.MustMarshalJSON(&daoGenState)
// setting up stagedClaims that are not part of PoP issuance (i.e.: past unresolved claims)
machineBalances := []banktypes.Balance{
{Address: machines[0].address, Coins: sdk.NewCoins(sdk.NewCoin(daoGenState.Params.StagedDenom, sdkmath.NewInt(10000)))},
{Address: machines[1].address, Coins: sdk.NewCoins(sdk.NewCoin(daoGenState.Params.StagedDenom, sdkmath.NewInt(10000)))},
}
var bankGenState banktypes.GenesisState
s.cfg.Codec.MustUnmarshalJSON(s.cfg.GenesisState[banktypes.ModuleName], &bankGenState)
bankGenState.Balances = append(bankGenState.Balances, machineBalances...)
s.cfg.GenesisState[banktypes.ModuleName] = s.cfg.Codec.MustMarshalJSON(&bankGenState)
s.network = network.Load(s.T(), s.cfg)
}
@ -198,7 +211,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
})
s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token)
assert.Equal(s.T(), "amount: \"18279452050\"\ndenom: "+token+"\n", out.String()) // Total supply 2 * 7990867578 (total supply) + 1 * 1997716894 (challenger) + 3 * 100000000 (validator) = 17979452050
assert.Equal(s.T(), "amount: \"18279472050\"\ndenom: "+token+"\n", out.String()) // Total supply 2 * 7990867578 (total supply) + 1 * 1997716894 (challenger) + 3 * 100000000 (validator) + 2 * 10000 (past unresolved claims) = 17979472050
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
machines[0].address,
@ -206,7 +219,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
})
s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token)
assert.Equal(s.T(), "amount: \"5993150682\"\ndenom: "+token+"\n", out.String()) // 3 * 1997716894 = 5993150682
assert.Equal(s.T(), "amount: \"5993160682\"\ndenom: "+token+"\n", out.String()) // 3 * 1997716894 + 1 * 10000= 5993160682
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
machines[1].address,
@ -214,7 +227,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
})
s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token)
assert.Equal(s.T(), "amount: \"11986301368\"\ndenom: "+token+"\n", out.String()) // 2 * 5993150684 = 11986301368
assert.Equal(s.T(), "amount: \"11986311368\"\ndenom: "+token+"\n", out.String()) // 2 * 5993150684 + 1 * 10000 = 11986311368
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
val.Address.String(),
@ -320,11 +333,11 @@ func (s *SelectionE2ETestSuite) TestTokenRedeemClaim() {
// QueryRedeemClaim
qOut, err := clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdShowRedeemClaim(), []string{"liquidAddress", "0"})
s.Require().NoError(err)
assert.Equal(s.T(), "redeemClaim:\n amount: \"5993150682\"\n beneficiary: liquidAddress\n confirmed: true\n creator: plmnt1kp93kns6hs2066d8qw0uz84fw3vlthewt2ck6p\n id: \"0\"\n liquidTxHash: \"0000000000000000000000000000000000000000000000000000000000000000\"\n", qOut.String())
assert.Equal(s.T(), "redeemClaim:\n amount: \"5993160682\"\n beneficiary: liquidAddress\n confirmed: true\n creator: plmnt1kp93kns6hs2066d8qw0uz84fw3vlthewt2ck6p\n id: \"0\"\n liquidTxHash: \"0000000000000000000000000000000000000000000000000000000000000000\"\n", qOut.String())
qOut, err = clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdRedeemClaimByLiquidTxHash(), []string{"0000000000000000000000000000000000000000000000000000000000000000"})
s.Require().NoError(err)
assert.Equal(s.T(), "redeemClaim:\n amount: \"5993150682\"\n beneficiary: liquidAddress\n confirmed: true\n creator: plmnt1kp93kns6hs2066d8qw0uz84fw3vlthewt2ck6p\n id: \"0\"\n liquidTxHash: \"0000000000000000000000000000000000000000000000000000000000000000\"\n", qOut.String())
assert.Equal(s.T(), "redeemClaim:\n amount: \"5993160682\"\n beneficiary: liquidAddress\n confirmed: true\n creator: plmnt1kp93kns6hs2066d8qw0uz84fw3vlthewt2ck6p\n id: \"0\"\n liquidTxHash: \"0000000000000000000000000000000000000000000000000000000000000000\"\n", qOut.String())
// Make sure "Publish" has been called with PoPInit cmnd
calls := mocks.GetCallLog()

View File

@ -2,6 +2,7 @@ package keeper
import (
"context"
"sort"
"strconv"
errorsmod "cosmossdk.io/errors"
@ -26,6 +27,10 @@ func (k msgServer) DistributionResult(goCtx context.Context, msg *types.MsgDistr
distribution.EarlyInvAddr = msg.EarlyInvestorTxID
distribution.StrategicTxID = msg.StrategicTxID
if err := k.clearUnresolvedClaims(ctx, distribution.FirstPop); err != nil {
util.GetAppLogger().Error(ctx, "error while clearing unresolved claims for heights %d-%d: %v", distribution.FirstPop, distribution.LastPop, err)
}
err := k.resolveStagedClaims(ctx, distribution.FirstPop, distribution.LastPop)
if err != nil {
util.GetAppLogger().Error(ctx, "%s for provided PoP heights: %d %d", types.ErrResolvingStagedClaims.Error(), distribution.FirstPop, distribution.LastPop)
@ -37,14 +42,48 @@ func (k msgServer) DistributionResult(goCtx context.Context, msg *types.MsgDistr
return &types.MsgDistributionResultResponse{}, nil
}
func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64) (err error) {
// lookup all challenges since the last distribution
challenges, err := k.GetChallengeRange(ctx, start, end)
// clearUnresolvedClaims checks for all Challenge participants starting from a given height.
// An accounts stagedDenom amount should always be 0 except for claims that have not yet been reissued.
// Calculate the difference for a set of participants and clear out all past unresolved staged claims.
func (k msgServer) clearUnresolvedClaims(ctx sdk.Context, start int64) (err error) {
// calculate total amounts for current and future claims
currentAmounts, err := k.getClaims(ctx, start, ctx.BlockHeight())
if err != nil {
return err
}
popParticipants := make(map[string]uint64)
totalAmounts := make(map[string]uint64)
for participantAddress := range currentAmounts {
stagedBalance := k.bankKeeper.GetBalance(ctx, sdk.MustAccAddressFromBech32(participantAddress), k.GetParams(ctx).StagedDenom)
totalAmounts[participantAddress] = stagedBalance.Amount.Uint64()
}
// calculate difference to account balance
for participantAddress := range totalAmounts {
totalAmounts[participantAddress] -= currentAmounts[participantAddress]
}
return k.convertOrderedClaim(ctx, totalAmounts)
}
// resolveStagedClaims converts staged claims to claims in an ordered fashion for a given range
func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64) (err error) {
popParticipantAmounts, err := k.getClaims(ctx, start, end)
if err != nil {
return err
}
return k.convertOrderedClaim(ctx, popParticipantAmounts)
}
func (k msgServer) getClaims(ctx sdk.Context, start int64, end int64) (claims map[string]uint64, err error) {
// lookup all challenges for a given range
challenges, err := k.GetChallengeRange(ctx, start, end)
if err != nil {
return
}
claims = make(map[string]uint64)
for _, challenge := range challenges {
// if challenge not finished nobody has claims
@ -52,9 +91,9 @@ func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64)
continue
}
_, challengerAmt, challengeeAmt := util.GetPopReward(challenge.Height, k.GetParams(ctx).PopEpochs)
popParticipants[challenge.Challenger] += challengerAmt
claims[challenge.Challenger] += challengerAmt
if challenge.GetSuccess() {
popParticipants[challenge.Challengee] += challengeeAmt
claims[challenge.Challengee] += challengeeAmt
}
initiatorAddr, err := sdk.AccAddressFromBech32(challenge.Initiator)
if err != nil {
@ -64,16 +103,22 @@ func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64)
if !found {
util.GetAppLogger().Error(ctx, "No PoP initiator reward found for height %v", challenge.GetHeight())
}
popParticipants[initiatorAddr.String()] += validatorPopReward
claims[initiatorAddr.String()] += validatorPopReward
}
return
}
func (k msgServer) convertOrderedClaim(ctx sdk.Context, claims map[string]uint64) (err error) {
// second data structure because map iteration order is not guaranteed in GO
keys := make([]string, 0)
for p := range popParticipants {
keys = append(keys, p)
for accountAddress := range claims {
keys = append(keys, accountAddress)
}
for _, p := range keys {
err = k.convertAccountClaim(ctx, p, popParticipants[p])
sort.Strings(keys)
for _, accountAddress := range keys {
err = k.convertAccountClaim(ctx, accountAddress, claims[accountAddress])
if err != nil {
return err
}