feat: add unresolved claim cleanup (#460)

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

View File

@ -10,9 +10,11 @@ import (
"strconv" "strconv"
"time" "time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/cli" 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/lib"
"github.com/planetmint/planetmint-go/monitor" "github.com/planetmint/planetmint-go/monitor"
"github.com/planetmint/planetmint-go/testutil" "github.com/planetmint/planetmint-go/testutil"
@ -85,6 +87,17 @@ func (s *SelectionE2ETestSuite) SetupSuite() {
daoGenState.Params.ClaimAddress = valAddr.String() daoGenState.Params.ClaimAddress = valAddr.String()
s.cfg.GenesisState[daotypes.ModuleName] = s.cfg.Codec.MustMarshalJSON(&daoGenState) 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) s.network = network.Load(s.T(), s.cfg)
} }
@ -198,7 +211,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
}) })
s.Require().NoError(err) s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token) 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{ out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
machines[0].address, machines[0].address,
@ -206,7 +219,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
}) })
s.Require().NoError(err) s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token) 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{ out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
machines[1].address, machines[1].address,
@ -214,7 +227,7 @@ func (s *SelectionE2ETestSuite) VerifyTokens(token string) {
}) })
s.Require().NoError(err) s.Require().NoError(err)
assert.Contains(s.T(), out.String(), token) 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{ out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
val.Address.String(), val.Address.String(),
@ -320,11 +333,11 @@ func (s *SelectionE2ETestSuite) TestTokenRedeemClaim() {
// QueryRedeemClaim // QueryRedeemClaim
qOut, err := clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdShowRedeemClaim(), []string{"liquidAddress", "0"}) qOut, err := clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdShowRedeemClaim(), []string{"liquidAddress", "0"})
s.Require().NoError(err) 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"}) qOut, err = clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdRedeemClaimByLiquidTxHash(), []string{"0000000000000000000000000000000000000000000000000000000000000000"})
s.Require().NoError(err) 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 // Make sure "Publish" has been called with PoPInit cmnd
calls := mocks.GetCallLog() calls := mocks.GetCallLog()

View File

@ -2,6 +2,7 @@ package keeper
import ( import (
"context" "context"
"sort"
"strconv" "strconv"
errorsmod "cosmossdk.io/errors" errorsmod "cosmossdk.io/errors"
@ -26,6 +27,10 @@ func (k msgServer) DistributionResult(goCtx context.Context, msg *types.MsgDistr
distribution.EarlyInvAddr = msg.EarlyInvestorTxID distribution.EarlyInvAddr = msg.EarlyInvestorTxID
distribution.StrategicTxID = msg.StrategicTxID 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) err := k.resolveStagedClaims(ctx, distribution.FirstPop, distribution.LastPop)
if err != nil { if err != nil {
util.GetAppLogger().Error(ctx, "%s for provided PoP heights: %d %d", types.ErrResolvingStagedClaims.Error(), distribution.FirstPop, distribution.LastPop) 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 return &types.MsgDistributionResultResponse{}, nil
} }
func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64) (err error) { // clearUnresolvedClaims checks for all Challenge participants starting from a given height.
// lookup all challenges since the last distribution // An accounts stagedDenom amount should always be 0 except for claims that have not yet been reissued.
challenges, err := k.GetChallengeRange(ctx, start, end) // 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 { if err != nil {
return err 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 { for _, challenge := range challenges {
// if challenge not finished nobody has claims // if challenge not finished nobody has claims
@ -52,9 +91,9 @@ func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64)
continue continue
} }
_, challengerAmt, challengeeAmt := util.GetPopReward(challenge.Height, k.GetParams(ctx).PopEpochs) _, challengerAmt, challengeeAmt := util.GetPopReward(challenge.Height, k.GetParams(ctx).PopEpochs)
popParticipants[challenge.Challenger] += challengerAmt claims[challenge.Challenger] += challengerAmt
if challenge.GetSuccess() { if challenge.GetSuccess() {
popParticipants[challenge.Challengee] += challengeeAmt claims[challenge.Challengee] += challengeeAmt
} }
initiatorAddr, err := sdk.AccAddressFromBech32(challenge.Initiator) initiatorAddr, err := sdk.AccAddressFromBech32(challenge.Initiator)
if err != nil { if err != nil {
@ -64,16 +103,22 @@ func (k msgServer) resolveStagedClaims(ctx sdk.Context, start int64, end int64)
if !found { if !found {
util.GetAppLogger().Error(ctx, "No PoP initiator reward found for height %v", challenge.GetHeight()) 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 // second data structure because map iteration order is not guaranteed in GO
keys := make([]string, 0) keys := make([]string, 0)
for p := range popParticipants { for accountAddress := range claims {
keys = append(keys, p) 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 { if err != nil {
return err return err
} }