From c3a428308f71d39f5db7c0078b9d795b0916c5ce Mon Sep 17 00:00:00 2001 From: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 1 Dec 2021 18:55:42 +0900 Subject: [PATCH] Feat: Re-enabling gravity bridge conditionally with flag (#210) --- CHANGELOG.md | 6 +- app/app.go | 297 +++++++++++++----- app/test_helpers.go | 32 +- cmd/cronosd/cmd/root.go | 5 + cmd/cronosd/experimental/util.go | 23 ++ docs/architecture/adr-001.md | 14 +- go.mod | 5 + go.sum | 2 + gomod2nix.toml | 10 + .../configs/disable_auto_deployment.yaml | 2 +- .../configs/genesis_token_mapping.yaml | 2 +- integration_tests/network.py | 9 + integration_tests/test_gravity.py | 9 +- scripts/cronos-experimental-devnet.yaml | 52 +++ scripts/devnet.yaml | 2 +- x/cronos/handler_test.go | 2 +- x/cronos/keeper/evm_hooks_test.go | 15 +- x/cronos/keeper/evm_log_handlers.go | 45 ++- x/cronos/keeper/evm_log_handlers_test.go | 65 +++- x/cronos/keeper/gravity_hooks.go | 94 ++++++ x/cronos/keeper/ibc_hooks_test.go | 1 + x/cronos/keeper/ibc_test.go | 1 + x/cronos/keeper/keeper.go | 4 + x/cronos/keeper/keeper_test.go | 2 +- x/cronos/keeper/params_test.go | 1 + x/cronos/module.go | 9 + x/cronos/types/interfaces.go | 7 + 27 files changed, 607 insertions(+), 109 deletions(-) create mode 100644 cmd/cronosd/experimental/util.go create mode 100644 scripts/cronos-experimental-devnet.yaml create mode 100644 x/cronos/keeper/gravity_hooks.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 85002de527..5683d5beb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Improvements +- [cronos#210](https://github.com/crypto-org-chain/cronos/pull/210) re-enabling gravity bridge conditionally + + *November 30, 2021* ## v0.6.4 @@ -24,6 +28,7 @@ - [tharsis#786](https://github.com/tharsis/ethermint/pull/786) Improve error message of `SendTransaction`/`SendRawTransaction` JSON-RPC APIs. - [cronos#222](https://github.com/crypto-org-chain/cronos/pull/222) change solc 0.6.11 to 0.6.8 (from dapp cachix) and update hermes to 0.8. + *November 19, 2021* ## v0.6.2 @@ -35,7 +40,6 @@ - [tharsis#741](https://github.com/tharsis/ethermint/pull/741) filter non eth txs in block rpc response - [crypto-org-chain/ethermint#12](https://github.com/crypto-org-chain/ethermint/pull/12) reject tx with too large gas limit - *October 26, 2021* ## v0.6.1 diff --git a/app/app.go b/app/app.go index 2938db9628..89d69d9648 100644 --- a/app/app.go +++ b/app/app.go @@ -102,8 +102,12 @@ import ( evmkeeper "github.com/tharsis/ethermint/x/evm/keeper" evmtypes "github.com/tharsis/ethermint/x/evm/types" + "github.com/peggyjv/gravity-bridge/module/x/gravity" + gravitykeeper "github.com/peggyjv/gravity-bridge/module/x/gravity/keeper" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" + // this line is used by starport scaffolding # stargate/app/moduleImport - cronos "github.com/crypto-org-chain/cronos/x/cronos" + "github.com/crypto-org-chain/cronos/x/cronos" cronosclient "github.com/crypto-org-chain/cronos/x/cronos/client" cronoskeeper "github.com/crypto-org-chain/cronos/x/cronos/keeper" cronostypes "github.com/crypto-org-chain/cronos/x/cronos/types" @@ -167,6 +171,7 @@ var ( transfer.AppModuleBasic{}, vesting.AppModuleBasic{}, evm.AppModuleBasic{}, + gravity.AppModuleBasic{}, // this line is used by starport scaffolding # stargate/app/moduleBasic cronos.AppModuleBasic{}, ) @@ -181,6 +186,7 @@ var ( govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, // used for secure addition and subtraction of balance using module account + gravitytypes.ModuleName: {authtypes.Minter, authtypes.Burner}, cronostypes.ModuleName: {authtypes.Minter, authtypes.Burner}, } // Module configurator @@ -243,6 +249,9 @@ type App struct { // Ethermint keepers EvmKeeper *evmkeeper.Keeper + // Gravity module + GravityKeeper gravitykeeper.Keeper + // this line is used by starport scaffolding # stargate/app/keeperDeclaration CronosKeeper cronoskeeper.Keeper @@ -270,24 +279,44 @@ func New( cdc := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry + experimental := cast.ToBool(appOpts.Get(cronos.ExperimentalFlag)) + bApp := baseapp.NewBaseApp(Name, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(interfaceRegistry) - keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, - evidencetypes.StoreKey, capabilitytypes.StoreKey, - feegrant.StoreKey, authzkeeper.StoreKey, - // ibc keys - ibchost.StoreKey, ibctransfertypes.StoreKey, - // ethermint keys - evmtypes.StoreKey, - // this line is used by starport scaffolding # stargate/app/storeKey - cronostypes.StoreKey, - ) + var keys map[string]*sdk.KVStoreKey + if experimental { + keys = sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, + feegrant.StoreKey, authzkeeper.StoreKey, + // ibc keys + ibchost.StoreKey, ibctransfertypes.StoreKey, + // ethermint keys + evmtypes.StoreKey, + gravitytypes.StoreKey, + // this line is used by starport scaffolding # stargate/app/storeKey + cronostypes.StoreKey, + ) + } else { + keys = sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, + feegrant.StoreKey, authzkeeper.StoreKey, + // ibc keys + ibchost.StoreKey, ibctransfertypes.StoreKey, + // ethermint keys + evmtypes.StoreKey, + // this line is used by starport scaffolding # stargate/app/storeKey + cronostypes.StoreKey, + ) + } // Add the EVM transient store key tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey) @@ -304,7 +333,7 @@ func New( memKeys: memKeys, } - app.ParamsKeeper = initParamsKeeper(appCodec, cdc, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + app.ParamsKeeper = initParamsKeeper(appCodec, cdc, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey], experimental) // set the BaseApp's parameter store bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable())) @@ -378,6 +407,20 @@ func New( tracer, ) + var gravityKeeper gravitykeeper.Keeper + if experimental { + gravityKeeper = gravitykeeper.NewKeeper( + appCodec, + keys[gravitytypes.StoreKey], + app.GetSubspace(gravitytypes.ModuleName), + app.AccountKeeper, + stakingKeeper, + app.BankKeeper, + app.SlashingKeeper, + sdk.DefaultPowerReduction, + ) + } + // this line is used by starport scaffolding # stargate/app/keeperDefinition app.CronosKeeper = *cronoskeeper.NewKeeper( @@ -387,6 +430,7 @@ func New( app.GetSubspace(cronostypes.ModuleName), app.BankKeeper, app.TransferKeeper, + gravityKeeper, app.EvmKeeper, ) cronosModule := cronos.NewAppModule(appCodec, app.CronosKeeper) @@ -409,21 +453,37 @@ func New( &stakingKeeper, govRouter, ) + var gravitySrv gravitytypes.MsgServer + if experimental { + app.GravityKeeper = *gravityKeeper.SetHooks(app.CronosKeeper) + gravitySrv = gravitykeeper.NewMsgServerImpl(app.GravityKeeper) + } + app.EvmKeeper.SetHooks(cronoskeeper.NewLogProcessEvmHook( cronoskeeper.NewSendToAccountHandler(app.BankKeeper, app.CronosKeeper), - cronoskeeper.NewSendToEthereumHandler(app.CronosKeeper), + cronoskeeper.NewSendToEthereumHandler(gravitySrv, app.CronosKeeper), cronoskeeper.NewSendToIbcHandler(app.BankKeeper, app.CronosKeeper), cronoskeeper.NewSendCroToIbcHandler(app.BankKeeper, app.CronosKeeper), )) // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.StakingKeeper = *stakingKeeper.SetHooks( - stakingtypes.NewMultiStakingHooks( - app.DistrKeeper.Hooks(), - app.SlashingKeeper.Hooks(), - ), - ) + if experimental { + app.StakingKeeper = *stakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks( + app.DistrKeeper.Hooks(), + app.SlashingKeeper.Hooks(), + app.GravityKeeper.Hooks(), + ), + ) + } else { + app.StakingKeeper = *stakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks( + app.DistrKeeper.Hooks(), + app.SlashingKeeper.Hooks(), + ), + ) + } // Create static IBC router, add transfer route, then set and seal it ibcRouter := porttypes.NewRouter() @@ -440,76 +500,148 @@ func New( // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. - app.mm = module.NewManager( - genutil.NewAppModule( - app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, - encodingConfig.TxConfig, - ), - auth.NewAppModule(appCodec, app.AccountKeeper, nil), - vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), - bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), - capability.NewAppModule(appCodec, *app.CapabilityKeeper), - crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), - gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), - slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), - upgrade.NewAppModule(app.UpgradeKeeper), - evidence.NewAppModule(app.EvidenceKeeper), - ibc.NewAppModule(app.IBCKeeper), - params.NewAppModule(app.ParamsKeeper), - feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - - transferModule, - evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), - // this line is used by starport scaffolding # stargate/app/appModule - cronosModule, - ) + if experimental { + app.mm = module.NewManager( + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, + encodingConfig.TxConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, nil), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + upgrade.NewAppModule(app.UpgradeKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + params.NewAppModule(app.ParamsKeeper), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + + transferModule, + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), + gravity.NewAppModule(app.GravityKeeper, app.BankKeeper), + // this line is used by starport scaffolding # stargate/app/appModule + cronosModule, + ) + } else { + app.mm = module.NewManager( + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, + encodingConfig.TxConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, nil), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + upgrade.NewAppModule(app.UpgradeKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + params.NewAppModule(app.ParamsKeeper), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + + transferModule, + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), + // this line is used by starport scaffolding # stargate/app/appModule + cronosModule, + ) + } // During begin block slashing happens after distr.BeginBlocker so that // there is nothing left over in the validator fee pool, so as to keep the // CanWithdrawInvariant invariant. // NOTE: staking module is required if HistoricalEntries param > 0 - app.mm.SetOrderBeginBlockers( - upgradetypes.ModuleName, - capabilitytypes.ModuleName, - evmtypes.ModuleName, - minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, - evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, - ) + if experimental { + app.mm.SetOrderBeginBlockers( + upgradetypes.ModuleName, + capabilitytypes.ModuleName, + evmtypes.ModuleName, + minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, + gravitytypes.ModuleName, + ) - app.mm.SetOrderEndBlockers( - crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, - evmtypes.ModuleName, - ) + app.mm.SetOrderEndBlockers( + crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, + evmtypes.ModuleName, + gravitytypes.ModuleName, + ) + } else { + app.mm.SetOrderBeginBlockers( + upgradetypes.ModuleName, + capabilitytypes.ModuleName, + evmtypes.ModuleName, + minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, + ) + + app.mm.SetOrderEndBlockers( + crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, + evmtypes.ModuleName, + ) + } // NOTE: The genutils module must occur after staking so that pools are // properly initialized with tokens from genesis accounts. // NOTE: Capability module must occur first so that it can initialize any capabilities // so that other modules that want to create or claim capabilities afterwards in InitChain // can do so safely. - app.mm.SetOrderInitGenesis( - capabilitytypes.ModuleName, - authtypes.ModuleName, - banktypes.ModuleName, - distrtypes.ModuleName, - stakingtypes.ModuleName, - slashingtypes.ModuleName, - govtypes.ModuleName, - minttypes.ModuleName, - crisistypes.ModuleName, - ibchost.ModuleName, - genutiltypes.ModuleName, - evidencetypes.ModuleName, - ibctransfertypes.ModuleName, - authz.ModuleName, - feegrant.ModuleName, - evmtypes.ModuleName, - // this line is used by starport scaffolding # stargate/app/initGenesis - cronostypes.ModuleName, - ) + if experimental { + app.mm.SetOrderInitGenesis( + capabilitytypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + slashingtypes.ModuleName, + govtypes.ModuleName, + minttypes.ModuleName, + crisistypes.ModuleName, + ibchost.ModuleName, + genutiltypes.ModuleName, + evidencetypes.ModuleName, + ibctransfertypes.ModuleName, + authz.ModuleName, + feegrant.ModuleName, + evmtypes.ModuleName, + gravitytypes.ModuleName, + // this line is used by starport scaffolding # stargate/app/initGenesis + cronostypes.ModuleName, + ) + } else { + app.mm.SetOrderInitGenesis( + capabilitytypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + slashingtypes.ModuleName, + govtypes.ModuleName, + minttypes.ModuleName, + crisistypes.ModuleName, + ibchost.ModuleName, + genutiltypes.ModuleName, + evidencetypes.ModuleName, + ibctransfertypes.ModuleName, + authz.ModuleName, + feegrant.ModuleName, + evmtypes.ModuleName, + // this line is used by starport scaffolding # stargate/app/initGenesis + cronostypes.ModuleName, + ) + } app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) @@ -721,7 +853,7 @@ func GetMaccPerms() map[string][]string { } // initParamsKeeper init params keeper and its subspaces -func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey, experimental bool) paramskeeper.Keeper { paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) paramsKeeper.Subspace(authtypes.ModuleName) @@ -735,6 +867,9 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(ibctransfertypes.ModuleName) paramsKeeper.Subspace(ibchost.ModuleName) paramsKeeper.Subspace(evmtypes.ModuleName) + if experimental { + paramsKeeper.Subspace(gravitytypes.ModuleName) + } // this line is used by starport scaffolding # stargate/app/paramSubspace paramsKeeper.Subspace(cronostypes.ModuleName) diff --git a/app/test_helpers.go b/app/test_helpers.go index 888800ae4b..37a77e9d8d 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -9,6 +9,9 @@ import ( "testing" "time" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/crypto-org-chain/cronos/x/cronos" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -58,10 +61,27 @@ var DefaultConsensusParams = &abci.ConsensusParams{ }, } -func setup(withGenesis bool, invCheckPeriod uint) (*App, GenesisState) { +// ExperimentalAppOptions is a stub implementing AppOptions +type ExperimentalAppOptions struct{} + +// Get implements AppOptions +func (ao ExperimentalAppOptions) Get(o string) interface{} { + if o == cronos.ExperimentalFlag { + return true + } + return nil +} + +func setup(withGenesis bool, invCheckPeriod uint, experimental bool) (*App, GenesisState) { db := dbm.NewMemDB() encCdc := MakeEncodingConfig() - app := New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) + var appOption servertypes.AppOptions + if experimental { + appOption = ExperimentalAppOptions{} + } else { + appOption = EmptyAppOptions{} + } + app := New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, appOption) if withGenesis { return app, NewDefaultGenesisState(encCdc.Marshaler) } @@ -69,8 +89,8 @@ func setup(withGenesis bool, invCheckPeriod uint) (*App, GenesisState) { } // Setup initializes a new App. A Nop logger is set in App. -func Setup(isCheckTx bool, cronosAdmin string) *App { - app, genesisState := setup(!isCheckTx, 5) +func Setup(isCheckTx bool, cronosAdmin string, experimental bool) *App { + app, genesisState := setup(!isCheckTx, 5, experimental) // set cronos_admin for test cronosGen := cronostypes.DefaultGenesis() @@ -105,7 +125,7 @@ func Setup(isCheckTx bool, cronosAdmin string) *App { // of one consensus engine unit (10^6) in the default token of the simapp from first genesis // account. A Nop logger is set in App. func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *App { - app, genesisState := setup(true, 5) + app, genesisState := setup(true, 5, true) // set genesis accounts authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) @@ -185,7 +205,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs // SetupWithGenesisAccounts initializes a new App with the provided genesis // accounts and possible balances. func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *App { - app, genesisState := setup(true, 0) + app, genesisState := setup(true, 0, true) authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) diff --git a/cmd/cronosd/cmd/root.go b/cmd/cronosd/cmd/root.go index 09e2f854c2..07ee56ad5b 100644 --- a/cmd/cronosd/cmd/root.go +++ b/cmd/cronosd/cmd/root.go @@ -6,6 +6,9 @@ import ( "os" "path/filepath" + "github.com/crypto-org-chain/cronos/cmd/cronosd/experimental" + "github.com/crypto-org-chain/cronos/x/cronos" + "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/snapshots" @@ -122,6 +125,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { a := appCreator{encodingConfig} ethermintserver.AddCommands(rootCmd, app.DefaultNodeHome, a.newApp, a.appExport, addModuleInitFlags) + experimental.AddCommands(rootCmd) // add keybase, auxiliary RPC, query, and tx child commands rootCmd.AddCommand( @@ -137,6 +141,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { func addModuleInitFlags(startCmd *cobra.Command) { crisis.AddModuleInitFlags(startCmd) + cronos.AddModuleInitFlags(startCmd) // this line is used by starport scaffolding # stargate/root/initFlags } diff --git a/cmd/cronosd/experimental/util.go b/cmd/cronosd/experimental/util.go new file mode 100644 index 0000000000..50a43522f6 --- /dev/null +++ b/cmd/cronosd/experimental/util.go @@ -0,0 +1,23 @@ +package experimental + +import ( + "github.com/crypto-org-chain/cronos/app" + gravitycmd "github.com/peggyjv/gravity-bridge/module/cmd/gravity/cmd" + "github.com/spf13/cobra" +) + +// add server commands +func AddCommands(rootCmd *cobra.Command) { + experimentalCmd := &cobra.Command{ + Use: "experimental", + Short: "experimental subcommands (unsafe)", + } + + experimentalCmd.AddCommand( + gravitycmd.Commands(app.DefaultNodeHome), + ) + + rootCmd.AddCommand( + experimentalCmd, + ) +} diff --git a/docs/architecture/adr-001.md b/docs/architecture/adr-001.md index 0ac25ec3ec..9ec43af07c 100644 --- a/docs/architecture/adr-001.md +++ b/docs/architecture/adr-001.md @@ -3,6 +3,7 @@ ## Changelog * 12-10-2021: Initial Draft * 13-10-2021: Code deletion instead of conditional compilation +* 16-11-2021: Experimental activation flag ## Context @@ -13,6 +14,8 @@ and the codebase contains several known issues. ## Decision +** Removal + The Gravity Bridge module will be removed from: 1. "app/app.go" which contains the module initialization boilerplate 2. "cmd/cronosd/cmd/root.go" which adds Gravity Bridge-related commands. @@ -23,7 +26,16 @@ In addition to that, the "x/cronos" module contains Gravity Bridge-related code Existing integration tests related to Gravity Bridge can be temporarily disabled and later enabled when Gravity Bridge is added back. -Once the Gravity Bridge code is added back, the x/cronos module `ConsensusVersion` should be increased and the corresponding upgrade handler should be added. +** Restoration + +In order to mitigate risks and avoid heavy maintenance cost, we took the decision to add an activation flag when reintroducing back the Gravity Bridge. + +The reason is that the timeline of releasing Gravity Bridge is still unclear and major breaking changes might need to be release in mainnet before we enable it definitely. This solution avoid us to maintain forks of the repository. + +To run the node with `x/gravity` enable in the app, the runtime flag `unsafe-experimental` needs to be set to true with the `start` command. + +Once we decide that the Gravity Bridge code is mature enough to be deployed in mainnet, the x/cronos module `ConsensusVersion` should be increased and the corresponding upgrade handler should be added. At that time, we could also remove the flag. + ## Status diff --git a/go.mod b/go.mod index 660aa80986..23e3fcdca2 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/peggyjv/gravity-bridge/module v0.2.0 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.4.1 github.com/spf13/cobra v1.2.1 @@ -165,3 +166,7 @@ replace github.com/ethereum/go-ethereum => github.com/crypto-org-chain/go-ethere replace github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.44.2 replace github.com/tharsis/ethermint => github.com/crypto-org-chain/ethermint v0.7.2-cronos-4 + +// FIXME: update after PR merged: https://github.com/PeggyJV/gravity-bridge/pull/182 +// https://github.com/crypto-org-chain/gravity-bridge/tree/cronos +replace github.com/peggyjv/gravity-bridge/module => github.com/crypto-org-chain/gravity-bridge/module v0.1.22-0.20211011065300-a09cf050d304 \ No newline at end of file diff --git a/go.sum b/go.sum index f5d74f4616..4e889af310 100644 --- a/go.sum +++ b/go.sum @@ -265,6 +265,8 @@ github.com/crypto-org-chain/ethermint v0.7.2-cronos-4 h1:NaCc0L5zN2u7B9WofmhJfur github.com/crypto-org-chain/ethermint v0.7.2-cronos-4/go.mod h1:J96LX4KvLyl+5jV6+mt/4l6srtGX/mdDTuqQQuYrdDk= github.com/crypto-org-chain/go-ethereum v1.10.3-patched h1:kr6oQIYOi2VC8SZwkhlUDZE1Omit/YHVysKMgCB2nes= github.com/crypto-org-chain/go-ethereum v1.10.3-patched/go.mod h1:99onQmSd1GRGOziyGldI41YQb7EESX3Q4H41IfJgIQQ= +github.com/crypto-org-chain/gravity-bridge/module v0.1.22-0.20211011065300-a09cf050d304 h1:5A6pzj5uQPQCCO96SqsCCoar/DfjOxIdadBHX3ncotU= +github.com/crypto-org-chain/gravity-bridge/module v0.1.22-0.20211011065300-a09cf050d304/go.mod h1:7sqblrhSS7+I94DDIWz98N2mcXR5hL7f+EaV054sBMQ= github.com/crypto-org-chain/ibc-go v1.2.1-hooks h1:wuWaQqm/TFKJQwuFgjCPiPumQio+Yik5Z1DObDExrrU= github.com/crypto-org-chain/ibc-go v1.2.1-hooks/go.mod h1:YieSs25Y0TSFR67qg6Elge34yJNEOjYhYB+HNQQLoSQ= github.com/crypto-org-chain/keyring v1.1.6-fixes h1:AUFSu56NY6XobY6XfRoDx6v3loiOrHK5MNUm32GEjwA= diff --git a/gomod2nix.toml b/gomod2nix.toml index 2c93adee02..67d093d76b 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -3872,6 +3872,16 @@ rev = "8b1b92947f46224e3b97bb1a3a5b0382be00d31e" sha256 = "0f146yjqwx2mr110kl8scjhqd08hys7vr5z0d0a3lskb6yy22gfg" +["github.com/peggyjv/gravity-bridge/module"] + sumVersion = "v0.1.22-0.20211011065300-a09cf050d304" + relPath = "module" + vendorPath = "github.com/crypto-org-chain/gravity-bridge/module" + ["github.com/peggyjv/gravity-bridge/module".fetch] + type = "git" + url = "https://github.com/crypto-org-chain/gravity-bridge" + rev = "a09cf050d304156a7b6db5f95bcb31bad3681c33" + sha256 = "03fdvfpzb7bqzqpgw7chd8rm6dsk6a2vybr5pvmg3h4nm5v9wm3l" + ["github.com/pelletier/go-toml"] sumVersion = "v1.9.4" ["github.com/pelletier/go-toml".fetch] diff --git a/integration_tests/configs/disable_auto_deployment.yaml b/integration_tests/configs/disable_auto_deployment.yaml index c5d3bb1df1..4cd8ea5b9e 100644 --- a/integration_tests/configs/disable_auto_deployment.yaml +++ b/integration_tests/configs/disable_auto_deployment.yaml @@ -1,6 +1,6 @@ cronos_777-1: cmd: cronosd - start-flags: "--trace" + start-flags: "--trace --unsafe-experimental" app-config: json-rpc: address: "0.0.0.0:{EVMRPC_PORT}" diff --git a/integration_tests/configs/genesis_token_mapping.yaml b/integration_tests/configs/genesis_token_mapping.yaml index 493b33574e..1a2567f2f2 100644 --- a/integration_tests/configs/genesis_token_mapping.yaml +++ b/integration_tests/configs/genesis_token_mapping.yaml @@ -1,6 +1,6 @@ cronos_777-1: cmd: cronosd - start-flags: "--trace" + start-flags: "--trace --unsafe-experimental" app-config: json-rpc: address: "0.0.0.0:{EVMRPC_PORT}" diff --git a/integration_tests/network.py b/integration_tests/network.py index b4c7e33488..8c30b4ca00 100644 --- a/integration_tests/network.py +++ b/integration_tests/network.py @@ -91,6 +91,15 @@ def setup_cronos(path, base_port, enable_auto_deployment=True): yield from setup_custom_cronos(path, base_port, cfg) +def setup_cronos_experimental(path, base_port, enable_auto_deployment=True): + cfg = Path(__file__).parent / ( + "../scripts/cronos-experimental-devnet.yaml" + if enable_auto_deployment + else "configs/disable_auto_deployment.yaml" + ) + yield from setup_custom_cronos(path, base_port, cfg) + + def setup_chainmain(path, base_port): cmd = ["start-chainmain", path, "--base_port", str(base_port)] print(*cmd) diff --git a/integration_tests/test_gravity.py b/integration_tests/test_gravity.py index 7067c60e26..f4aa332931 100644 --- a/integration_tests/test_gravity.py +++ b/integration_tests/test_gravity.py @@ -8,9 +8,8 @@ from hexbytes import HexBytes from pystarport import ports -from .conftest import setup_cronos, setup_geth from .gorc import GoRc -from .network import GravityBridge +from .network import GravityBridge, setup_cronos_experimental, setup_geth from .utils import ( ADDRS, KEYS, @@ -28,7 +27,7 @@ wait_for_new_blocks, ) -pytestmark = pytest.mark.skip +pytestmark = pytest.mark.gravity Account.enable_unaudited_hdwallet_features() @@ -85,7 +84,9 @@ def geth(tmp_path_factory): @pytest.fixture(scope="module", params=[True, False]) def cronos(request, tmp_path_factory): "start-cronos" - yield from setup_cronos(tmp_path_factory.mktemp("cronos"), 26700, request.param) + yield from setup_cronos_experimental( + tmp_path_factory.mktemp("cronos_experimental"), 26700, request.param + ) @pytest.fixture(scope="module") diff --git a/scripts/cronos-experimental-devnet.yaml b/scripts/cronos-experimental-devnet.yaml new file mode 100644 index 0000000000..6157460750 --- /dev/null +++ b/scripts/cronos-experimental-devnet.yaml @@ -0,0 +1,52 @@ +cronos_777-1: + cmd: cronosd + start-flags: "--trace --unsafe-experimental" + app-config: + minimum-gas-prices: 5000000000000basetcro + json-rpc: + address: "0.0.0.0:{EVMRPC_PORT}" + ws-address: "0.0.0.0:{EVMRPC_PORT_WS}" + validators: + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: visit craft resemble online window solution west chuckle music diesel vital settle comic tribe project blame bulb armed flower region sausage mercy arrive release + - coins: 1000000000000000000stake,10000000000000000000000basetcro + staked: 1000000000000000000stake + mnemonic: direct travel shrug hand twice agent sail sell jump phone velvet pilot mango charge usual multiply orient garment bleak virtual action mention panda vast + accounts: + - name: community + coins: 10000000000000000000000basetcro + mnemonic: "notable error gospel wave pair ugly measure elite toddler cost various fly make eye ketchup despair slab throw tribe swarm word fruit into inmate" + - name: signer1 + coins: 20000000000000000000000basetcro + mnemonic: shed crumble dismiss loyal latin million oblige gesture shrug still oxygen custom remove ribbon disorder palace addict again blanket sad flock consider obey popular + - name: signer2 + coins: 30000000000000000000000basetcro + mnemonic: night renew tonight dinner shaft scheme domain oppose echo summer broccoli agent face guitar surface belt veteran siren poem alcohol menu custom crunch index + + genesis: + consensus_params: + block: + max_bytes: "1048576" + max_gas: "81500000" + app_state: + evm: + params: + evm_denom: basetcro + cronos: + params: + cronos_admin: crc12luku6uxehhak02py4rcz65zu0swh7wjsrw0pp + enable_auto_deployment: true + ibc_cro_denom: ibc/6411AE2ADA1E73DB59DB151A8988F9B7D5E7E233D8414DB6817F8F1A01611F86 + gov: + voting_params: + voting_period: "10s" + deposit_params: + max_deposit_period: "10s" + min_deposit: + - denom: "basetcro" + amount: "1" + transfer: + params: + receive_enabled: true + send_enabled: true diff --git a/scripts/devnet.yaml b/scripts/devnet.yaml index dfc5c207a3..0c0fdef72d 100644 --- a/scripts/devnet.yaml +++ b/scripts/devnet.yaml @@ -1,6 +1,6 @@ cronos_777-1: cmd: ./build/cronosd - start-flags: "--trace" + start-flags: "--trace --unsafe-experimental" app-config: json-rpc: address: "0.0.0.0:{EVMRPC_PORT}" diff --git a/x/cronos/handler_test.go b/x/cronos/handler_test.go index 1251d6b874..5d26adf108 100644 --- a/x/cronos/handler_test.go +++ b/x/cronos/handler_test.go @@ -35,7 +35,7 @@ func (suite *CronosTestSuite) SetupTest() { privKey, err := ethsecp256k1.GenerateKey() suite.Require().NoError(err) suite.address = sdk.AccAddress(privKey.PubKey().Address()) - suite.app = app.Setup(false, suite.address.String()) + suite.app = app.Setup(false, suite.address.String(), true) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: app.TestAppChainID, Time: time.Now().UTC()}) suite.handler = cronos.NewHandler(suite.app.CronosKeeper) diff --git a/x/cronos/keeper/evm_hooks_test.go b/x/cronos/keeper/evm_hooks_test.go index 8cb3cd62df..62286601e7 100644 --- a/x/cronos/keeper/evm_hooks_test.go +++ b/x/cronos/keeper/evm_hooks_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "fmt" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" "math/big" "github.com/crypto-org-chain/cronos/app" @@ -125,7 +126,7 @@ func (suite *KeeperTestSuite) TestEvmHooks() { }, }, { - "fail send to ethereum", // gravity feature is removed + "success send to ethereum", func() { suite.SetupTest() denom := "gravity0x0000000000000000000000000000000000000000" @@ -152,7 +153,16 @@ func (suite *KeeperTestSuite) TestEvmHooks() { }, } err = suite.app.EvmKeeper.PostTxProcessing(txHash, logs) - suite.Require().Error(err) + suite.Require().NoError(err) + + // sender's balance deducted + balance = suite.app.BankKeeper.GetBalance(suite.ctx, sdk.AccAddress(contract.Bytes()), denom) + suite.Require().Equal(sdk.NewCoin(denom, sdk.NewInt(0)), balance) + // query unbatched SendToEthereum message exist + rsp, err := suite.app.GravityKeeper.UnbatchedSendToEthereums(sdk.WrapSDKContext(suite.ctx), &gravitytypes.UnbatchedSendToEthereumsRequest{ + SenderAddress: sdk.AccAddress(contract.Bytes()).String(), + }) + suite.Require().Equal(1, len(rsp.SendToEthereums)) }, }, { @@ -167,6 +177,7 @@ func (suite *KeeperTestSuite) TestEvmHooks() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) suite.app.CronosKeeper = cronosKeeper diff --git a/x/cronos/keeper/evm_log_handlers.go b/x/cronos/keeper/evm_log_handlers.go index 524162c77b..1b332c124c 100644 --- a/x/cronos/keeper/evm_log_handlers.go +++ b/x/cronos/keeper/evm_log_handlers.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" "github.com/crypto-org-chain/cronos/x/cronos/types" ) @@ -160,11 +161,13 @@ func (h SendToAccountHandler) Handle(ctx sdk.Context, contract common.Address, d // SendToEthereumHandler handles `__CronosSendToEthereum` log type SendToEthereumHandler struct { + gravitySrv gravitytypes.MsgServer cronosKeeper Keeper } -func NewSendToEthereumHandler(cronosKeeper Keeper) *SendToEthereumHandler { +func NewSendToEthereumHandler(gravitySrv gravitytypes.MsgServer, cronosKeeper Keeper) *SendToEthereumHandler { return &SendToEthereumHandler{ + gravitySrv: gravitySrv, cronosKeeper: cronosKeeper, } } @@ -173,11 +176,43 @@ func (h SendToEthereumHandler) EventID() common.Hash { return SendToEthereumEvent.ID } -// Handle returns error unconditionally. -// Since gravity bridge is removed and could be added later, -// we keep this event handler, but returns error unconditionally to prevent accidental access. +// Handle `__CronosSendToEthereum` log only if gravity is activated. func (h SendToEthereumHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error { - return fmt.Errorf("native action %s is not implemented", SendToEthereumEventName) + if h.gravitySrv == nil { + return fmt.Errorf("native action %s is not implemented", SendToEthereumEventName) + } + + unpacked, err := SendToEthereumEvent.Inputs.Unpack(data) + if err != nil { + // log and ignore + h.cronosKeeper.Logger(ctx).Info("log signature matches but failed to decode") + return nil + } + + denom, found := h.cronosKeeper.GetDenomByContract(ctx, contract) + if !found { + return fmt.Errorf("contract %s is not connected to native token", contract) + } + + if !types.IsValidGravityDenom(denom) { + return fmt.Errorf("the native token associated with the contract %s is not a gravity voucher", contract) + } + + contractAddr := sdk.AccAddress(contract.Bytes()) + ethRecipient := unpacked[0].(common.Address) + amount := sdk.NewIntFromBigInt(unpacked[1].(*big.Int)) + bridgeFee := sdk.NewIntFromBigInt(unpacked[2].(*big.Int)) + msg := gravitytypes.MsgSendToEthereum{ + Sender: contractAddr.String(), + EthereumRecipient: ethRecipient.Hex(), + Amount: sdk.NewCoin(denom, amount), + BridgeFee: sdk.NewCoin(denom, bridgeFee), + } + _, err = h.gravitySrv.SendToEthereum(sdk.WrapSDKContext(ctx), &msg) + if err != nil { + return err + } + return nil } // SendToIbcHandler handles `__CronosSendToIbc` log diff --git a/x/cronos/keeper/evm_log_handlers_test.go b/x/cronos/keeper/evm_log_handlers_test.go index 1ebdef7690..502e975edb 100644 --- a/x/cronos/keeper/evm_log_handlers_test.go +++ b/x/cronos/keeper/evm_log_handlers_test.go @@ -12,6 +12,8 @@ import ( keepertest "github.com/crypto-org-chain/cronos/x/cronos/keeper/mock" "github.com/crypto-org-chain/cronos/x/cronos/types" "github.com/ethereum/go-ethereum/common" + gravitykeeper "github.com/peggyjv/gravity-bridge/module/x/gravity/keeper" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" ) func (suite *KeeperTestSuite) TestSendToAccountHandler() { @@ -96,6 +98,7 @@ func (suite *KeeperTestSuite) TestSendToEthereumHandler() { contract := common.BigToAddress(big.NewInt(1)) recipient := common.BigToAddress(big.NewInt(3)) + invalidDenom := "testdenom" validDenom := "gravity0x0000000000000000000000000000000000000000" var data []byte @@ -106,7 +109,48 @@ func (suite *KeeperTestSuite) TestSendToEthereumHandler() { error error }{ { - "not implemented, expect fail", + "non gravity denom, expect fail", + func() { + suite.app.CronosKeeper.SetExternalContractForDenom(suite.ctx, invalidDenom, contract) + coin := sdk.NewCoin(invalidDenom, sdk.NewInt(100)) + err := suite.MintCoins(sdk.AccAddress(contract.Bytes()), sdk.NewCoins(coin)) + suite.Require().NoError(err) + + balance := suite.app.BankKeeper.GetBalance(suite.ctx, sdk.AccAddress(contract.Bytes()), invalidDenom) + suite.Require().Equal(coin, balance) + + input, err := keeper.SendToEthereumEvent.Inputs.Pack( + recipient, + coin.Amount.BigInt(), + big.NewInt(0), + ) + data = input + }, + func() {}, + errors.New("the native token associated with the contract 0x0000000000000000000000000000000000000001 is not a gravity voucher"), + }, + { + "non associated coin denom, expect fail", + func() { + coin := sdk.NewCoin(invalidDenom, sdk.NewInt(100)) + err := suite.MintCoins(sdk.AccAddress(contract.Bytes()), sdk.NewCoins(coin)) + suite.Require().NoError(err) + + balance := suite.app.BankKeeper.GetBalance(suite.ctx, sdk.AccAddress(contract.Bytes()), invalidDenom) + suite.Require().Equal(coin, balance) + + input, err := keeper.SendToEthereumEvent.Inputs.Pack( + recipient, + coin.Amount.BigInt(), + big.NewInt(0), + ) + data = input + }, + func() {}, + errors.New("contract 0x0000000000000000000000000000000000000001 is not connected to native token"), + }, + { + "success send to ethereum", func() { suite.app.CronosKeeper.SetExternalContractForDenom(suite.ctx, validDenom, contract) coin := sdk.NewCoin(validDenom, sdk.NewInt(100)) @@ -123,15 +167,26 @@ func (suite *KeeperTestSuite) TestSendToEthereumHandler() { ) data = input }, - func() {}, - errors.New("native action __CronosSendToEthereum is not implemented"), + func() { + // sender's balance deducted + balance := suite.app.BankKeeper.GetBalance(suite.ctx, sdk.AccAddress(contract.Bytes()), validDenom) + suite.Require().Equal(sdk.NewCoin(validDenom, sdk.NewInt(0)), balance) + // query unbatched SendToEthereum message exist + rsp, err := suite.app.GravityKeeper.UnbatchedSendToEthereums(sdk.WrapSDKContext(suite.ctx), &gravitytypes.UnbatchedSendToEthereumsRequest{ + SenderAddress: sdk.AccAddress(contract.Bytes()).String(), + }) + suite.Require().Equal(1, len(rsp.SendToEthereums)) + suite.Require().NoError(err) + }, + nil, }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() - handler := keeper.NewSendToEthereumHandler(suite.app.CronosKeeper) + handler := keeper.NewSendToEthereumHandler( + gravitykeeper.NewMsgServerImpl(suite.app.GravityKeeper), suite.app.CronosKeeper) tc.malleate() err := handler.Handle(suite.ctx, contract, data) if tc.error != nil { @@ -232,6 +287,7 @@ func (suite *KeeperTestSuite) TestSendToIbcHandler() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) handler := keeper.NewSendToIbcHandler(suite.app.BankKeeper, cronosKeeper) @@ -320,6 +376,7 @@ func (suite *KeeperTestSuite) TestSendCroToIbcHandler() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) handler := keeper.NewSendCroToIbcHandler(suite.app.BankKeeper, cronosKeeper) diff --git a/x/cronos/keeper/gravity_hooks.go b/x/cronos/keeper/gravity_hooks.go new file mode 100644 index 0000000000..6cf270a731 --- /dev/null +++ b/x/cronos/keeper/gravity_hooks.go @@ -0,0 +1,94 @@ +package keeper + +import ( + "fmt" + "strconv" + + sdk "github.com/cosmos/cosmos-sdk/types" + cronostypes "github.com/crypto-org-chain/cronos/x/cronos/types" + "github.com/ethereum/go-ethereum/common" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" +) + +// TODO Implements GravityHooks interface +func (k Keeper) AfterContractCallExecutedEvent(ctx sdk.Context, event gravitytypes.ContractCallExecutedEvent) { + if k.gravityKeeper == nil { + return + } +} + +func (k Keeper) AfterERC20DeployedEvent(ctx sdk.Context, event gravitytypes.ERC20DeployedEvent) { + if k.gravityKeeper == nil { + return + } +} + +func (k Keeper) AfterSignerSetExecutedEvent(ctx sdk.Context, event gravitytypes.SignerSetTxExecutedEvent) { + if k.gravityKeeper == nil { + return + } +} + +func (k Keeper) AfterBatchExecutedEvent(ctx sdk.Context, event gravitytypes.BatchExecutedEvent) { + if k.gravityKeeper == nil { + return + } +} + +func (k Keeper) AfterSendToCosmosEvent(ctx sdk.Context, event gravitytypes.SendToCosmosEvent) { + if k.gravityKeeper == nil { + return + } + + cacheCtx, commit := ctx.CacheContext() + err := k.doAfterSendToCosmosEvent(cacheCtx, event) + if err == nil { + commit() + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) + } else { + k.Logger(ctx).Error("AfterSendToCosmosEvent hook failed", "error", err) + } +} + +func (k Keeper) doAfterSendToCosmosEvent(ctx sdk.Context, event gravitytypes.SendToCosmosEvent) error { + if k.gravityKeeper == nil { + return fmt.Errorf("gravity is not enable") + } + + isCosmosOriginated, denom := k.gravityKeeper.ERC20ToDenomLookup(ctx, event.TokenContract) + coin := sdk.NewCoin(denom, event.Amount) + // TODO: Remove after event is emitted at Gravity module https://github.com/crypto-org-chain/gravity-bridge/pull/12 + coins := sdk.Coins{sdk.NewCoin(denom, event.Amount)} + ctx.EventManager().EmitEvent(sdk.NewEvent( + cronostypes.EventTypeEthereumSendToCosmosHandled, + sdk.NewAttribute(sdk.AttributeKeyModule, gravitytypes.ModuleName), + sdk.NewAttribute(cronostypes.AttributeKeySender, event.GetEthereumSender()), + sdk.NewAttribute(cronostypes.AttributeKeyReceiver, event.GetCosmosReceiver()), + sdk.NewAttribute(cronostypes.AttributeKeyAmount, coins.String()), + sdk.NewAttribute(gravitytypes.AttributeKeyBridgeChainID, strconv.FormatUint( + k.gravityKeeper.GetParams(ctx).BridgeChainId, 10, + )), + sdk.NewAttribute(cronostypes.AttributeKeyEthereumTokenContract, event.GetTokenContract()), + sdk.NewAttribute(gravitytypes.AttributeKeyNonce, strconv.FormatUint(event.GetEventNonce(), 10)), + sdk.NewAttribute(gravitytypes.AttributeKeyEthereumEventVoteRecordID, + string(gravitytypes.MakeEthereumEventVoteRecordKey(event.GetEventNonce(), event.Hash()))), + )) + + if isCosmosOriginated { + // ignore cosmos originated transfer + return nil + } + // Try to convert the newly minted native tokens to erc20 contract + cosmosAddr, err := sdk.AccAddressFromBech32(event.CosmosReceiver) + if err != nil { + return err + } + addr := common.BytesToAddress(cosmosAddr.Bytes()) + enableAutoDeployment := k.GetParams(ctx).EnableAutoDeployment + err = k.ConvertCoinFromNativeToCRC20(ctx, addr, coin, enableAutoDeployment) + if err != nil { + return err + } + + return nil +} diff --git a/x/cronos/keeper/ibc_hooks_test.go b/x/cronos/keeper/ibc_hooks_test.go index b339476f61..e20c8b4ced 100644 --- a/x/cronos/keeper/ibc_hooks_test.go +++ b/x/cronos/keeper/ibc_hooks_test.go @@ -75,6 +75,7 @@ func (suite *KeeperTestSuite) TestOnRecvVouchers() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) suite.app.CronosKeeper = cronosKeeper diff --git a/x/cronos/keeper/ibc_test.go b/x/cronos/keeper/ibc_test.go index 8a925c9f56..0cde6c7892 100644 --- a/x/cronos/keeper/ibc_test.go +++ b/x/cronos/keeper/ibc_test.go @@ -262,6 +262,7 @@ func (suite *KeeperTestSuite) TestIbcTransferCoins() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) suite.app.CronosKeeper = cronosKeeper diff --git a/x/cronos/keeper/keeper.go b/x/cronos/keeper/keeper.go index 57fd307a87..d4e06bed68 100644 --- a/x/cronos/keeper/keeper.go +++ b/x/cronos/keeper/keeper.go @@ -28,6 +28,8 @@ type ( bankKeeper types.BankKeeper // ibc transfer operations transferKeeper types.TransferKeeper + // gravity bridge keeper + gravityKeeper types.GravityKeeper // ethermint evm keeper evmKeeper *evmkeeper.Keeper @@ -42,6 +44,7 @@ func NewKeeper( paramSpace paramtypes.Subspace, bankKeeper types.BankKeeper, transferKeeper types.TransferKeeper, + gravityKeeper types.GravityKeeper, evmKeeper *evmkeeper.Keeper, // this line is used by starport scaffolding # ibc/keeper/parameter ) *Keeper { @@ -58,6 +61,7 @@ func NewKeeper( paramSpace: paramSpace, bankKeeper: bankKeeper, transferKeeper: transferKeeper, + gravityKeeper: gravityKeeper, evmKeeper: evmKeeper, // this line is used by starport scaffolding # ibc/keeper/return } diff --git a/x/cronos/keeper/keeper_test.go b/x/cronos/keeper/keeper_test.go index a298708f46..960dc54fe3 100644 --- a/x/cronos/keeper/keeper_test.go +++ b/x/cronos/keeper/keeper_test.go @@ -54,7 +54,7 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) { require.NoError(t, err) consAddress := sdk.ConsAddress(priv.PubKey().Address()) - suite.app = app.Setup(checkTx, sdk.AccAddress(suite.address.Bytes()).String()) + suite.app = app.Setup(checkTx, sdk.AccAddress(suite.address.Bytes()).String(), true) suite.ctx = suite.app.NewContext(checkTx, tmproto.Header{ Height: 1, ChainID: app.TestAppChainID, diff --git a/x/cronos/keeper/params_test.go b/x/cronos/keeper/params_test.go index e87ce8e0d5..39edd470e5 100644 --- a/x/cronos/keeper/params_test.go +++ b/x/cronos/keeper/params_test.go @@ -44,6 +44,7 @@ func (suite *KeeperTestSuite) TestGetSourceChannelID() { suite.app.GetSubspace(types.ModuleName), suite.app.BankKeeper, keepertest.IbcKeeperMock{}, + suite.app.GravityKeeper, suite.app.EvmKeeper, ) suite.app.CronosKeeper = cronosKeeper diff --git a/x/cronos/module.go b/x/cronos/module.go index 3d359e73df..29776b7332 100644 --- a/x/cronos/module.go +++ b/x/cronos/module.go @@ -30,6 +30,10 @@ var ( // this line is used by starport scaffolding # ibc/module/interface ) +const ( + ExperimentalFlag = "unsafe-experimental" +) + // ---------------------------------------------------------------------------- // AppModuleBasic // ---------------------------------------------------------------------------- @@ -43,6 +47,11 @@ func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic { return AppModuleBasic{cdc: cdc} } +// AddModuleInitFlags implements servertypes.ModuleInitFlags interface. +func AddModuleInitFlags(startCmd *cobra.Command) { + startCmd.Flags().Bool(ExperimentalFlag, false, "Start the node with experimental features") +} + // Name returns the capability module's name. func (AppModuleBasic) Name() string { return types.ModuleName diff --git a/x/cronos/types/interfaces.go b/x/cronos/types/interfaces.go index 93716669c9..fca526b0a1 100644 --- a/x/cronos/types/interfaces.go +++ b/x/cronos/types/interfaces.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/ibc-go/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" "github.com/ethereum/go-ethereum/common" + gravitytypes "github.com/peggyjv/gravity-bridge/module/x/gravity/types" tmbytes "github.com/tendermint/tendermint/libs/bytes" ) @@ -39,6 +40,12 @@ type AccountKeeper interface { GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI } +// GravityKeeper defines the expected gravity keeper interface +type GravityKeeper interface { + ERC20ToDenomLookup(ctx sdk.Context, tokenContract string) (bool, string) + GetParams(ctx sdk.Context) (params gravitytypes.Params) +} + // EvmLogHandler defines the interface for evm log handler type EvmLogHandler interface { // Return the id of the log signature it handles