forked from evmos/evmos
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* update cosmos sdk * add v9.1.0 upgrade handler and test * add changelog Co-authored-by: Ramiro Carlucho <[email protected]>
- Loading branch information
1 parent
db076d7
commit 80c38f6
Showing
8 changed files
with
2,476 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package v91 | ||
|
||
const ( | ||
// UpgradeName is the shared upgrade plan name for mainnet | ||
UpgradeName = "v9.1.0" | ||
// UpgradeInfo defines the binaries that will be used for the upgrade | ||
UpgradeInfo = `'{"binaries":{"darwin/arm64":"https://github.com/evmos/evmos/releases/download/v9.1.0/evmos_9.1.0_Darwin_arm64.tar.gz","darwin/amd64":"https://github.com/evmos/evmos/releases/download/v9.1.0/evmos_9.1.0_Darwin_amd64.tar.gz","linux/arm64":"https://github.com/evmos/evmos/releases/download/v9.1.0/evmos_9.1.0_Linux_arm64.tar.gz","linux/amd64":"https://github.com/evmos/evmos/releases/download/v9.1.0/evmos_9.1.0_Linux_amd64.tar.gz","windows/x86_64":"https://github.com/evmos/evmos/releases/download/v9.1.0/evmos_9.1.0_Windows_x86_64.zip"}}'` | ||
// MaxRecover is the maximum amount of coins to be redistributed in the upgrade | ||
MaxRecover = "93590289356801768542679" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package v91 | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/tendermint/tendermint/libs/log" | ||
|
||
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...") | ||
HandleMainnetUpgrade(ctx, dk, logger) | ||
} | ||
|
||
// Leave modules are as-is to avoid running InitGenesis. | ||
logger.Debug("running module migrations ...") | ||
return mm.RunMigrations(ctx, configurator, vm) | ||
} | ||
} | ||
|
||
// HandleMainnetUpgrade handles the logic for Mainnet upgrade, it only commits to the db if successful | ||
func HandleMainnetUpgrade(ctx sdk.Context, dk distrKeeper.Keeper, logger log.Logger) { | ||
// use a cache context as a rollback mechanism in case | ||
// the refund fails | ||
cacheCtx, writeFn := ctx.CacheContext() | ||
err := ReturnFundsFromCommunityPool(cacheCtx, dk) | ||
if err != nil { | ||
// log error instead of aborting the upgrade | ||
logger.Error("failed to recover from community funds", "error", err.Error()) | ||
} else { | ||
writeFn() | ||
} | ||
} | ||
|
||
// 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 { | ||
address := Accounts[i][0] | ||
amt := Accounts[i][1] | ||
|
||
refund, _ := sdk.NewIntFromString(amt) | ||
if availableCoins.LT(refund) { | ||
return fmt.Errorf( | ||
"refund to address %s exceeds the total available coins: %s > %s", | ||
address, amt, availableCoins, | ||
) | ||
} | ||
if err := ReturnFundsFromCommunityPoolToAccount(ctx, dk, address, 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
package v91_test | ||
|
||
import ( | ||
"fmt" | ||
"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_1" | ||
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) TestMigrateFaucetBalance() { | ||
|
||
firstAccountAmount := v9.Accounts[0][1] | ||
thousandAccountAmount := v9.Accounts[1000][1] | ||
|
||
testCases := []struct { | ||
name string | ||
chainID string | ||
malleate func() | ||
expectedSuccess bool | ||
}{ | ||
{ | ||
"Mainnet - sucess", | ||
evmostypes.MainnetChainID + "-4", | ||
func() { | ||
// 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)) | ||
}, | ||
true, | ||
}, | ||
{ | ||
"Mainnet - first account > MaxRecover", | ||
evmostypes.MainnetChainID + "-4", | ||
func() { | ||
// 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)) | ||
|
||
v9.Accounts[0][1] = v9.MaxRecover | ||
}, | ||
false, | ||
}, | ||
{ | ||
"Mainnet - middle account > MaxRecover", | ||
evmostypes.MainnetChainID + "-4", | ||
func() { | ||
// 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)) | ||
|
||
v9.Accounts[1000][1] = v9.MaxRecover | ||
}, | ||
false, | ||
}, | ||
{ | ||
"Mainnet - fail communityFund is empty", | ||
evmostypes.MainnetChainID + "-4", | ||
func() { | ||
}, | ||
false, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
suite.Run(fmt.Sprintf("Case %s", tc.name), func() { | ||
suite.SetupTest(tc.chainID) | ||
|
||
tc.malleate() | ||
|
||
logger := suite.ctx.Logger().With("upgrade", "Test v9 Upgrade") | ||
v9.HandleMainnetUpgrade(suite.ctx, suite.app.DistrKeeper, logger) | ||
|
||
// check balance of affected accounts | ||
if tc.expectedSuccess { | ||
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()) | ||
} else { | ||
for i := range v9.Accounts { | ||
addr := sdk.MustAccAddressFromBech32(v9.Accounts[i][0]) | ||
balance := suite.app.BankKeeper.GetBalance(suite.ctx, addr, "aevmos") | ||
suite.Require().Equal(balance.Amount, sdk.NewInt(0)) | ||
} | ||
} | ||
v9.Accounts[0][1] = firstAccountAmount | ||
v9.Accounts[1000][1] = thousandAccountAmount | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters