Skip to content

Commit

Permalink
v9.0.0 release (evmos#987)
Browse files Browse the repository at this point in the history
* fix ClawbackEmptyAccounts logic (evmos#951)

* fix ClawbackEmptyAccounts logic

* fix tests

* update changelog

* fix claim_test

* turn dust into a variable

* add module account test

* update test name

* update module account test

* update documentation

* format comment

* update changelog

* Upgrade handler v9 (evmos#952)

* Apply suggestions from code review

Co-authored-by: Federico Kunze Küllmer <[email protected]>

* use sdk.Int instead of sdkmath.Int

* apply suggestions from review

* fix: adjust clawback fix to sdk v0.45

* check if recover is positive

* Update app/upgrades/v9/upgrades.go

Co-authored-by: Daniel Burckhardt <[email protected]>

* Update app/upgrades/v9/upgrades_test.go

Co-authored-by: Daniel Burckhardt <[email protected]>

* Update app/upgrades/v9/upgrades_test.go

Co-authored-by: Daniel Burckhardt <[email protected]>

* add necessary comments

* remove upgrade height constant

* wrap ReturnFundsFromCommunityPoolToAccount error

* run gofmt

* unwrapping error

* add attestation accounts

* Update CHANGELOG.md

* add accounts comments

Co-authored-by: Ramiro Carlucho <[email protected]>
Co-authored-by: Federico Kunze Küllmer <[email protected]>
Co-authored-by: Daniel Burckhardt <[email protected]>
  • Loading branch information
4 people authored Oct 21, 2022
1 parent 1097824 commit db076d7
Show file tree
Hide file tree
Showing 12 changed files with 2,504 additions and 28 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## [v9.0.0] - 2022-10-21

### Bug Fixes

- (claims) [#951](https://github.com/evmos/evmos/pull/951) Fix ClawbackEmptyAccounts logic

## [v8.2.3] - 2022-10-15

### Improvements
Expand Down
12 changes: 12 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import (
v8 "github.com/evmos/evmos/v9/app/upgrades/v8"
v81 "github.com/evmos/evmos/v9/app/upgrades/v8_1"
v82 "github.com/evmos/evmos/v9/app/upgrades/v8_2"
v9 "github.com/evmos/evmos/v9/app/upgrades/v9"
"github.com/evmos/evmos/v9/x/claims"
claimskeeper "github.com/evmos/evmos/v9/x/claims/keeper"
claimstypes "github.com/evmos/evmos/v9/x/claims/types"
Expand Down Expand Up @@ -1110,6 +1111,15 @@ func (app *Evmos) setupUpgradeHandlers() {
app.mm, app.configurator),
)

// v9 upgrade handler
app.UpgradeKeeper.SetUpgradeHandler(
v9.UpgradeName,
v9.CreateUpgradeHandler(
app.mm, app.configurator,
app.DistrKeeper,
),
)

// When a planned update height is reached, the old binary will panic
// writing on disk the height and name of the update that triggered it
// This will read that value, and execute the preparations for the upgrade.
Expand Down Expand Up @@ -1150,6 +1160,8 @@ func (app *Evmos) setupUpgradeHandlers() {
Added: []string{revenuetypes.ModuleName},
Deleted: []string{"feesplit"},
}
case v9.UpgradeName:
// no store upgrade in v9
}

if storeUpgrades != nil {
Expand Down
2,162 changes: 2,162 additions & 0 deletions app/upgrades/v9/accounts.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions app/upgrades/v9/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package v9

const (
// UpgradeName is the shared upgrade plan name for mainnet
UpgradeName = "v9.0.0"
// UpgradeInfo defines the binaries that will be used for the upgrade
UpgradeInfo = `'{"binaries":{"darwin/arm64":"https://github.com/evmos/evmos/releases/download/v9.0.0/evmos_9.0.0_Darwin_arm64.tar.gz","darwin/amd64":"https://github.com/evmos/evmos/releases/download/v9.0.0/evmos_9.0.0_Darwin_amd64.tar.gz","linux/arm64":"https://github.com/evmos/evmos/releases/download/v9.0.0/evmos_9.0.0_Linux_arm64.tar.gz","linux/amd64":"https://github.com/evmos/evmos/releases/download/v9.0.0/evmos_9.0.0_Linux_amd64.tar.gz","windows/x86_64":"https://github.com/evmos/evmos/releases/download/v9.0.0/evmos_9.0.0_Windows_x86_64.zip"}}'`
// MaxRecover is the maximum amount of coins to be redistributed in the upgrade
MaxRecover = "93590289356801768542679"
)
67 changes: 67 additions & 0 deletions app/upgrades/v9/upgrades.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package v9

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
distrKeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/evmos/evmos/v9/types"
)

// CreateUpgradeHandler creates an SDK upgrade handler for v9
func CreateUpgradeHandler(
mm *module.Manager,
configurator module.Configurator,
dk distrKeeper.Keeper,
) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
logger := ctx.Logger().With("upgrade", UpgradeName)

if types.IsMainnet(ctx.ChainID()) {
logger.Debug("recovering lost funds from clawback...")
if err := ReturnFundsFromCommunityPool(ctx, dk); err != nil {
// log error instead of aborting the upgrade
logger.Error("FAILED TO RECOVER FROM COMMUNITY FUNDS", "error", err.Error())
}
}

// Leave modules are as-is to avoid running InitGenesis.
logger.Debug("running module migrations ...")
return mm.RunMigrations(ctx, configurator, vm)
}
}

// ReturnFundsFromCommunityPool handles the return of funds from the community pool to accounts affected during the claims clawback
func ReturnFundsFromCommunityPool(ctx sdk.Context, dk distrKeeper.Keeper) error {
availableCoins, ok := sdk.NewIntFromString(MaxRecover)
if !ok || availableCoins.IsNegative() {
return fmt.Errorf("failed to read maximum amount to recover from community funds")
}
for i := range Accounts {
refund, _ := sdk.NewIntFromString(Accounts[i][1])
if availableCoins.LT(refund) {
return fmt.Errorf("refund exceeds the total available coins: %s > %s", Accounts[i][1], availableCoins)
}
if err := ReturnFundsFromCommunityPoolToAccount(ctx, dk, Accounts[i][0], refund); err != nil {
return err
}
availableCoins = availableCoins.Sub(refund)
}
return nil
}

// ReturnFundsFromCommunityPoolToAccount sends specified amount from the community pool to the affected account
func ReturnFundsFromCommunityPoolToAccount(ctx sdk.Context, dk distrKeeper.Keeper, account string, amount sdk.Int) error {
to := sdk.MustAccAddressFromBech32(account)
balance := sdk.Coin{
Denom: "aevmos",
Amount: amount,
}

if err := dk.DistributeFromFeePool(ctx, sdk.Coins{balance}, to); err != nil {
return err
}
return nil
}
110 changes: 110 additions & 0 deletions app/upgrades/v9/upgrades_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package v9_test

import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/suite"

"github.com/tendermint/tendermint/crypto/tmhash"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
"github.com/tendermint/tendermint/version"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/evmos/ethermint/crypto/ethsecp256k1"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"

"github.com/evmos/evmos/v9/app"
v9 "github.com/evmos/evmos/v9/app/upgrades/v9"
evmostypes "github.com/evmos/evmos/v9/types"
"github.com/evmos/evmos/v9/x/erc20/types"
)

type UpgradeTestSuite struct {
suite.Suite

ctx sdk.Context
app *app.Evmos
consAddress sdk.ConsAddress
}

func (suite *UpgradeTestSuite) SetupTest(chainID string) {
checkTx := false

// consensus key
priv, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())

// NOTE: this is the new binary, not the old one.
suite.app = app.Setup(checkTx, feemarkettypes.DefaultGenesisState())
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
Height: 1,
ChainID: chainID,
Time: time.Date(2022, 5, 9, 8, 0, 0, 0, time.UTC),
ProposerAddress: suite.consAddress.Bytes(),

Version: tmversion.Consensus{
Block: version.BlockProtocol,
},
LastBlockId: tmproto.BlockID{
Hash: tmhash.Sum([]byte("block_id")),
PartSetHeader: tmproto.PartSetHeader{
Total: 11,
Hash: tmhash.Sum([]byte("partset_header")),
},
},
AppHash: tmhash.Sum([]byte("app")),
DataHash: tmhash.Sum([]byte("data")),
EvidenceHash: tmhash.Sum([]byte("evidence")),
ValidatorsHash: tmhash.Sum([]byte("validators")),
NextValidatorsHash: tmhash.Sum([]byte("next_validators")),
ConsensusHash: tmhash.Sum([]byte("consensus")),
LastResultsHash: tmhash.Sum([]byte("last_result")),
})

cp := suite.app.BaseApp.GetConsensusParams(suite.ctx)
suite.ctx = suite.ctx.WithConsensusParams(cp)
}

func TestUpgradeTestSuite(t *testing.T) {
s := new(UpgradeTestSuite)
suite.Run(t, s)
}

func (suite *UpgradeTestSuite) TestReturnFundsFromCommunityPool() {
suite.SetupTest(evmostypes.TestnetChainID + "-2")

// send funds to the community pool
priv, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
address := common.BytesToAddress(priv.PubKey().Address().Bytes())
sender := sdk.AccAddress(address.Bytes())
res, _ := sdk.NewIntFromString(v9.MaxRecover)
coins := sdk.NewCoins(sdk.NewCoin("aevmos", res))
suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
err = suite.app.DistrKeeper.FundCommunityPool(suite.ctx, coins, sender)
suite.Require().NoError(err)

balanceBefore := suite.app.DistrKeeper.GetFeePoolCommunityCoins(suite.ctx)
suite.Require().Equal(balanceBefore.AmountOf("aevmos"), sdk.NewDecFromInt(res))

// return funds to accounts affected
err = v9.ReturnFundsFromCommunityPool(suite.ctx, suite.app.DistrKeeper)
suite.Require().NoError(err)

// check balance of affected accounts
for i := range v9.Accounts {
addr := sdk.MustAccAddressFromBech32(v9.Accounts[i][0])
res, _ := sdk.NewIntFromString(v9.Accounts[i][1])
balance := suite.app.BankKeeper.GetBalance(suite.ctx, addr, "aevmos")
suite.Require().Equal(balance.Amount, res)
}

balanceAfter := suite.app.DistrKeeper.GetFeePoolCommunityCoins(suite.ctx)
suite.Require().True(balanceAfter.IsZero())
}
37 changes: 34 additions & 3 deletions x/claims/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
ethermint "github.com/evmos/ethermint/types"

"github.com/evmos/evmos/v9/x/claims/types"
)
Expand Down Expand Up @@ -112,6 +113,11 @@ func (k Keeper) ClawbackEmptyAccounts(ctx sdk.Context, claimsDenom string) {
return false
}

// ignore non ETH accounts
if _, isEthAccount := acc.(ethermint.EthAccountI); !isEthAccount {
return false
}

seq, err := k.accountKeeper.GetSequence(ctx, addr)
if err != nil {
logger.Debug(
Expand All @@ -125,15 +131,30 @@ func (k Keeper) ClawbackEmptyAccounts(ctx sdk.Context, claimsDenom string) {
return false
}

clawbackCoin := k.bankKeeper.GetBalance(ctx, addr, claimsDenom)
// get all balances to check if address has balances in other denoms
accountBalances := k.bankKeeper.GetAllBalances(ctx, addr)

// prune empty accounts from the airdrop
if clawbackCoin.IsZero() {
// only prune empty accounts from the airdrop
if accountBalances.IsZero() {
k.accountKeeper.RemoveAccount(ctx, acc)
accPruned++
return false
}

// dust amount sent on genesis
dustCoin := sdk.Coin{
Amount: sdk.NewInt(types.GenesisDust),
Denom: claimsDenom,
}

// check if acc has claims denom balance and only clawback if the balance is
// the same as the initial dust sent on genesis
found, clawbackCoin := findCoin(accountBalances, claimsDenom)
// found, clawbackCoin := accountBalances.Find(claimsDenom)
if !found || !clawbackCoin.Equal(dustCoin) {
return false
}

// Send all unclaimed airdropped coins back to the community pool
// and prune those inactive wallets from current state.
// "Unclaimed" tokens are defined as being in wallets which have a sequence
Expand Down Expand Up @@ -166,3 +187,13 @@ func (k Keeper) ClawbackEmptyAccounts(ctx sdk.Context, claimsDenom string) {
"pruned-accounts", strconv.FormatInt(accPruned, 10),
)
}

func findCoin(coins []sdk.Coin, denom string) (found bool, coin sdk.Coin) {
for i := range coins {
if coins[i].Denom == denom {
return true, coins[i]
}
}

return false, sdk.Coin{}
}
Loading

0 comments on commit db076d7

Please sign in to comment.