diff --git a/CHANGELOG.md b/CHANGELOG.md index dfaed44f89..66f86b3c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - (store) [#12](https://github.com/EscanBE/evermint/pull/12) Add local `snapshots` management commands - (store) [#14](https://github.com/EscanBE/evermint/pull/14) Add `inspect` command and sub-commands - (test+rpc) [#74](https://github.com/EscanBE/evermint/pull/74) Add integration test util + add IT skeleton for Json-RPC +- (vesting) [#144](https://github.com/EscanBE/evermint/pull/144) Support vesting account creation via new module `x/vauth` ### Improvement diff --git a/README.md b/README.md index 3cba3c9253..6d3705ac17 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ evmd convert-address evm1sv9m0g7ycejwr3s369km58h5qe7xj77hxrsmsz evmos # alias: "ca" ``` 2. [Rename chain](https://github.com/EscanBE/evermint/blob/main/RENAME_CHAIN.md) -3. [`snapshots` command](https://github.com/EscanBE/evermint/pull/12) -4. [`inspect` command](https://github.com/EscanBE/evermint/pull/14) -5. [Flag `--allow-insecure-unlock`](https://github.com/EscanBE/evermint/pull/142) -6. Dependencies updated: `Cosmos-SDK v0.47.13`, `CometBFT v0.37.5`, `ibc-go v7.8.0`, `go-ethereum v1.10.26` \ No newline at end of file +3. [Support vesting account creation](https://github.com/EscanBE/evermint/pull/144) with help from module `x/vauth` +4. [`snapshots` command](https://github.com/EscanBE/evermint/pull/12) +5. [`inspect` command](https://github.com/EscanBE/evermint/pull/14) +6. [Flag `--allow-insecure-unlock`](https://github.com/EscanBE/evermint/pull/142) +7. Dependencies updated: `Cosmos-SDK v0.47.13`, `CometBFT v0.37.5`, `ibc-go v7.8.0`, `go-ethereum v1.10.26` \ No newline at end of file diff --git a/app/ante/cosmos/interfaces.go b/app/ante/cosmos/interfaces.go index b1955db763..22cba79039 100644 --- a/app/ante/cosmos/interfaces.go +++ b/app/ante/cosmos/interfaces.go @@ -10,3 +10,7 @@ type BankKeeper interface { SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error } + +type VAuthKeeper interface { + HasProofExternalOwnedAccount(ctx sdk.Context, accAddr sdk.AccAddress) bool +} diff --git a/app/ante/cosmos/reject_msgs.go b/app/ante/cosmos/reject_msgs.go index 1957bc92ba..e882997fb6 100644 --- a/app/ante/cosmos/reject_msgs.go +++ b/app/ante/cosmos/reject_msgs.go @@ -5,7 +5,6 @@ import ( evmtypes "github.com/EscanBE/evermint/v12/x/evm/types" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" - vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" ) // RejectMessagesDecorator prevents invalid msg types from being executed @@ -20,11 +19,6 @@ func (rmd RejectMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula errortypes.ErrInvalidType, "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", ) - case *vestingtypes.MsgCreateVestingAccount, *vestingtypes.MsgCreatePeriodicVestingAccount, *vestingtypes.MsgCreatePermanentLockedAccount: - return ctx, errorsmod.Wrapf( - errortypes.ErrInvalidType, - "vesting messages are prohibited from execution: %T", msg, - ) default: continue } diff --git a/app/ante/cosmos/vesting.go b/app/ante/cosmos/vesting.go new file mode 100644 index 0000000000..066ab83da9 --- /dev/null +++ b/app/ante/cosmos/vesting.go @@ -0,0 +1,49 @@ +package cosmos + +import ( + errorsmod "cosmossdk.io/errors" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +// VestingMessagesAuthorizationDecorator authorize vesting account creation msg execution. +// - If the target account has proof of EOA via `x/vauth`, the message can keep going. +// - Otherwise, the message will be rejected. +type VestingMessagesAuthorizationDecorator struct { + vAuthKeeper VAuthKeeper +} + +// NewVestingMessagesAuthorizationDecorator creates a new VestingMessagesAuthorizationDecorator. +func NewVestingMessagesAuthorizationDecorator(vak VAuthKeeper) VestingMessagesAuthorizationDecorator { + return VestingMessagesAuthorizationDecorator{ + vAuthKeeper: vak, + } +} + +// AnteHandle (read VestingMessagesAuthorizationDecorator) +func (vd VestingMessagesAuthorizationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + var account string + if m, ok := msg.(*vestingtypes.MsgCreateVestingAccount); ok { + account = m.ToAddress + } else if m, ok := msg.(*vestingtypes.MsgCreatePeriodicVestingAccount); ok { + account = m.ToAddress + } else if m, ok := msg.(*vestingtypes.MsgCreatePermanentLockedAccount); ok { + account = m.ToAddress + } else { + continue + } + + if vd.vAuthKeeper.HasProofExternalOwnedAccount(ctx, sdk.MustAccAddressFromBech32(account)) { + continue + } + + return ctx, errorsmod.Wrapf( + errortypes.ErrUnauthorized, + "must prove account is external owned account (EOA) via `x/%s` module before able to create vesting account: %s", vauthtypes.ModuleName, account, + ) + } + return next(ctx, tx, simulate) +} diff --git a/app/ante/cosmos/vesting_test.go b/app/ante/cosmos/vesting_test.go new file mode 100644 index 0000000000..898f62c75f --- /dev/null +++ b/app/ante/cosmos/vesting_test.go @@ -0,0 +1,103 @@ +package cosmos_test + +import ( + sdkmath "cosmossdk.io/math" + "encoding/hex" + "fmt" + cosmosante "github.com/EscanBE/evermint/v12/app/ante/cosmos" + "github.com/EscanBE/evermint/v12/constants" + "github.com/EscanBE/evermint/v12/rename_chain/marker" + "github.com/EscanBE/evermint/v12/testutil" + testutiltx "github.com/EscanBE/evermint/v12/testutil/tx" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + sdk "github.com/cosmos/cosmos-sdk/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/ethereum/go-ethereum/crypto" + "time" +) + +//goland:noinspection ALL +func (suite *AnteTestSuite) TestNewVestingMessagesAuthorizationDecorator() { + proof := vauthtypes.ProofExternalOwnedAccount{ + Account: marker.ReplaceAbleAddress("evm1xx2enpw8wzlr64xkdz2gh3c7epucfdftnqtcem"), + Hash: "0x" + hex.EncodeToString(crypto.Keccak256([]byte(vauthtypes.MessageToSign))), + Signature: "0xe665110439b1d18002ef866285f7e532090065ad74274560db5e8373d0cdb6297afefc70a5dd46c23e74bd3f0f262195f089b2923242a14e8e0791f4b0621a2c00", + } + + submitter := marker.ReplaceAbleAddress("evm1x8fhpj9nmhqk8z9kpgjt95ck2xwyue0ppeqynn") + nonProvedAddress := marker.ReplaceAbleAddress("evm1dx67l23hz9l0k9hcher8xz04uj7wf3yuqpfj0p") + + amount := sdk.NewCoins(sdk.NewInt64Coin(constants.BaseDenom, 1e18)) + + testCases := []struct { + name string + malleate func(ctx sdk.Context) sdk.Tx + expPass bool + errMsg string + }{ + { + name: "pass - invalid cosmos tx type", + malleate: func(_ sdk.Context) sdk.Tx { + return &testutiltx.InvalidTx{} + }, + expPass: true, + }, + { + name: "pass - account has proof", + malleate: func(ctx sdk.Context) sdk.Tx { + suite.app.VAuthKeeper.SaveProofExternalOwnedAccount(ctx, proof) + + txBuilder := suite.CreateTestCosmosTxBuilder(sdkmath.NewInt(0), constants.BaseDenom, &vestingtypes.MsgCreateVestingAccount{ + FromAddress: submitter, + ToAddress: proof.Account, + Amount: amount, + EndTime: time.Now().Add(24 * time.Hour).Unix(), + Delayed: true, + }) + return txBuilder.GetTx() + }, + expPass: true, + }, + { + name: "fail - reject account does not have proof", + malleate: func(ctx sdk.Context) sdk.Tx { + txBuilder := suite.CreateTestCosmosTxBuilder(sdkmath.NewInt(0), constants.BaseDenom, &vestingtypes.MsgCreateVestingAccount{ + FromAddress: submitter, + ToAddress: nonProvedAddress, + Amount: amount, + EndTime: time.Now().Add(24 * time.Hour).Unix(), + Delayed: true, + }) + return txBuilder.GetTx() + }, + expPass: false, + errMsg: "must prove account is external owned account (EOA)", + }, + } + + var execTypes = []struct { + name string + isCheckTx bool + simulate bool + }{ + {"deliverTx", false, false}, + {"deliverTxSimulate", false, true}, + } + + for _, et := range execTypes { + for _, tc := range testCases { + suite.Run(fmt.Sprintf("%s - %s", et.name, tc.name), func() { + // s.SetupTest(et.isCheckTx) + ctx := suite.ctx.WithIsReCheckTx(et.isCheckTx) + dec := cosmosante.NewVestingMessagesAuthorizationDecorator(suite.app.VAuthKeeper) + _, err := dec.AnteHandle(ctx, tc.malleate(ctx), et.simulate, testutil.NextFn) + + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().ErrorContains(err, tc.errMsg) + } + }) + } + } +} diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go index b367c719b5..9a9009e4bc 100644 --- a/app/ante/handler_options.go +++ b/app/ante/handler_options.go @@ -3,6 +3,7 @@ package ante import ( errorsmod "cosmossdk.io/errors" anteutils "github.com/EscanBE/evermint/v12/app/ante/utils" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" @@ -32,6 +33,7 @@ type HandlerOptions struct { StakingKeeper *stakingkeeper.Keeper FeeMarketKeeper evmante.FeeMarketKeeper EvmKeeper evmante.EVMKeeper + VAuthKeeper cosmosante.VAuthKeeper FeegrantKeeper ante.FeegrantKeeper ExtensionOptionChecker ante.ExtensionOptionChecker SignModeHandler authsigning.SignModeHandler @@ -75,6 +77,9 @@ func (options HandlerOptions) Validate() error { if options.EvmKeeper == nil { return errorsmod.Wrap(errortypes.ErrLogic, "evm keeper is required for AnteHandler") } + if options.VAuthKeeper == nil { + return errorsmod.Wrapf(errortypes.ErrLogic, "%s keeper is required for AnteHandler", vauthtypes.ModuleName) + } if options.SigGasConsumer == nil { return errorsmod.Wrap(errortypes.ErrLogic, "signature gas consumer is required for AnteHandler") } @@ -127,6 +132,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { ante.NewSetUpContextDecorator(), ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), ante.NewValidateBasicDecorator(), + cosmosante.NewVestingMessagesAuthorizationDecorator(options.VAuthKeeper), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), cosmosante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), @@ -151,6 +157,7 @@ func newLegacyCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler { ), ante.NewSetUpContextDecorator(), ante.NewValidateBasicDecorator(), + cosmosante.NewVestingMessagesAuthorizationDecorator(options.VAuthKeeper), ante.NewTxTimeoutHeightDecorator(), cosmosante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), ante.NewValidateMemoDecorator(options.AccountKeeper), diff --git a/app/ante/handler_options_test.go b/app/ante/handler_options_test.go index 7a98be99a4..659ff55598 100644 --- a/app/ante/handler_options_test.go +++ b/app/ante/handler_options_test.go @@ -111,6 +111,21 @@ func (suite *AnteTestSuite) TestValidateHandlerOptions() { }, false, }, + { + "fail - empty VAuth keeper", + ante.HandlerOptions{ + Cdc: suite.app.AppCodec(), + AccountKeeper: suite.app.AccountKeeper, + BankKeeper: suite.app.BankKeeper, + DistributionKeeper: suite.app.DistrKeeper, + IBCKeeper: suite.app.IBCKeeper, + StakingKeeper: suite.app.StakingKeeper, + FeeMarketKeeper: suite.app.FeeMarketKeeper, + EvmKeeper: suite.app.EvmKeeper, + VAuthKeeper: &suite.app.VAuthKeeper, + }, + false, + }, { "fail - empty signature gas consumer", ante.HandlerOptions{ @@ -188,6 +203,7 @@ func (suite *AnteTestSuite) TestValidateHandlerOptions() { DistributionKeeper: suite.app.DistrKeeper, ExtensionOptionChecker: types.HasDynamicFeeExtensionOption, EvmKeeper: suite.app.EvmKeeper, + VAuthKeeper: &suite.app.VAuthKeeper, StakingKeeper: suite.app.StakingKeeper, FeegrantKeeper: suite.app.FeeGrantKeeper, IBCKeeper: suite.app.IBCKeeper, @@ -202,11 +218,13 @@ func (suite *AnteTestSuite) TestValidateHandlerOptions() { } for _, tc := range cases { - err := tc.options.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } + suite.Run(tc.name, func() { + err := tc.options.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + }) } } diff --git a/app/app.go b/app/app.go index b2eb3a0667..d5cb1e9dc2 100644 --- a/app/app.go +++ b/app/app.go @@ -4,8 +4,6 @@ import ( "context" "encoding/json" "fmt" - "github.com/EscanBE/evermint/v12/constants" - "github.com/cosmos/cosmos-sdk/x/mint" "io" "net/http" "os" @@ -44,6 +42,8 @@ import ( authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" "github.com/cosmos/cosmos-sdk/x/authz" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" @@ -54,6 +54,8 @@ import ( capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" @@ -74,6 +76,9 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" @@ -90,8 +95,6 @@ import ( upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" - ibctransfer "github.com/cosmos/ibc-go/v7/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" @@ -100,44 +103,42 @@ import ( ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v7/testing" + ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" icahost "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host" icahostkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" - ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + // unnamed import of statik for swagger UI support + _ "github.com/EscanBE/evermint/v12/client/docs/statik" + + "github.com/EscanBE/evermint/v12/app/ante" ethante "github.com/EscanBE/evermint/v12/app/ante/evm" + "github.com/EscanBE/evermint/v12/app/upgrades/v3_sample" + "github.com/EscanBE/evermint/v12/constants" "github.com/EscanBE/evermint/v12/encoding" "github.com/EscanBE/evermint/v12/ethereum/eip712" srvflags "github.com/EscanBE/evermint/v12/server/flags" evertypes "github.com/EscanBE/evermint/v12/types" + "github.com/EscanBE/evermint/v12/x/erc20" + erc20client "github.com/EscanBE/evermint/v12/x/erc20/client" + erc20keeper "github.com/EscanBE/evermint/v12/x/erc20/keeper" + erc20types "github.com/EscanBE/evermint/v12/x/erc20/types" "github.com/EscanBE/evermint/v12/x/evm" evmkeeper "github.com/EscanBE/evermint/v12/x/evm/keeper" evmtypes "github.com/EscanBE/evermint/v12/x/evm/types" "github.com/EscanBE/evermint/v12/x/feemarket" feemarketkeeper "github.com/EscanBE/evermint/v12/x/feemarket/keeper" feemarkettypes "github.com/EscanBE/evermint/v12/x/feemarket/types" - consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" - consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - - // unnamed import of statik for swagger UI support - _ "github.com/EscanBE/evermint/v12/client/docs/statik" - - "github.com/EscanBE/evermint/v12/app/ante" - "github.com/EscanBE/evermint/v12/app/upgrades/v3_sample" - "github.com/EscanBE/evermint/v12/x/erc20" - erc20client "github.com/EscanBE/evermint/v12/x/erc20/client" - erc20keeper "github.com/EscanBE/evermint/v12/x/erc20/keeper" - erc20types "github.com/EscanBE/evermint/v12/x/erc20/types" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/EscanBE/evermint/v12/x/vauth" + vauthkeeper "github.com/EscanBE/evermint/v12/x/vauth/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" // Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes _ "github.com/ethereum/go-ethereum/eth/tracers/js" @@ -197,6 +198,7 @@ var ( evm.AppModuleBasic{}, feemarket.AppModuleBasic{}, erc20.AppModuleBasic{}, + vauth.AppModuleBasic{}, consensus.AppModuleBasic{}, ) @@ -212,6 +214,7 @@ var ( icatypes.ModuleName: nil, evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, // used for secure addition and subtraction of balance using module account erc20types.ModuleName: {authtypes.Minter, authtypes.Burner}, + vauthtypes.ModuleName: {authtypes.Burner}, } // module accounts that are allowed to receive tokens @@ -272,6 +275,7 @@ type Evermint struct { // Evermint keepers Erc20Keeper erc20keeper.Keeper + VAuthKeeper vauthkeeper.Keeper // the module manager mm *module.Manager @@ -327,7 +331,7 @@ func NewEvermint( // ethermint keys evmtypes.StoreKey, feemarkettypes.StoreKey, // evermint module keys - erc20types.StoreKey, + erc20types.StoreKey, vauthtypes.StoreKey, ) // Add the EVM transient store key @@ -461,9 +465,13 @@ func NewEvermint( chainApp.AccountKeeper, chainApp.BankKeeper, chainApp.EvmKeeper, chainApp.StakingKeeper, ) + chainApp.VAuthKeeper = vauthkeeper.NewKeeper( + appCodec, keys[vauthtypes.StoreKey], chainApp.BankKeeper, *chainApp.EvmKeeper, + ) + chainApp.GovKeeper = *govKeeper.SetHooks( govtypes.NewMultiGovHooks( - // + // ), ) @@ -529,6 +537,7 @@ func NewEvermint( slashing.NewAppModule(appCodec, chainApp.SlashingKeeper, chainApp.AccountKeeper, chainApp.BankKeeper, chainApp.StakingKeeper, chainApp.GetSubspace(slashingtypes.ModuleName)), distr.NewAppModule(appCodec, chainApp.DistrKeeper, chainApp.AccountKeeper, chainApp.BankKeeper, chainApp.StakingKeeper, chainApp.GetSubspace(distrtypes.ModuleName)), staking.NewAppModule(appCodec, chainApp.StakingKeeper, chainApp.AccountKeeper, chainApp.BankKeeper, chainApp.GetSubspace(stakingtypes.ModuleName)), + vesting.NewAppModule(chainApp.AccountKeeper, chainApp.BankKeeper), upgrade.NewAppModule(&chainApp.UpgradeKeeper), evidence.NewAppModule(chainApp.EvidenceKeeper), params.NewAppModule(chainApp.ParamsKeeper), @@ -545,9 +554,8 @@ func NewEvermint( evm.NewAppModule(chainApp.EvmKeeper, chainApp.AccountKeeper, chainApp.GetSubspace(evmtypes.ModuleName)), feemarket.NewAppModule(chainApp.FeeMarketKeeper, chainApp.GetSubspace(feemarkettypes.ModuleName)), // Evermint app modules - erc20.NewAppModule(chainApp.Erc20Keeper, chainApp.AccountKeeper, - chainApp.GetSubspace(erc20types.ModuleName)), - vesting.NewAppModule(chainApp.AccountKeeper, chainApp.BankKeeper), + erc20.NewAppModule(chainApp.Erc20Keeper, chainApp.AccountKeeper, chainApp.GetSubspace(erc20types.ModuleName)), + vauth.NewAppModule(appCodec, chainApp.VAuthKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -580,6 +588,7 @@ func NewEvermint( paramstypes.ModuleName, vestingtypes.ModuleName, erc20types.ModuleName, + vauthtypes.ModuleName, consensusparamtypes.ModuleName, ) @@ -597,6 +606,7 @@ func NewEvermint( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + vestingtypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, @@ -607,8 +617,8 @@ func NewEvermint( paramstypes.ModuleName, upgradetypes.ModuleName, // Evermint modules - vestingtypes.ModuleName, erc20types.ModuleName, + vauthtypes.ModuleName, consensusparamtypes.ModuleName, ) @@ -641,9 +651,10 @@ func NewEvermint( feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, - // Evermint modules vestingtypes.ModuleName, + // Evermint modules erc20types.ModuleName, + vauthtypes.ModuleName, consensusparamtypes.ModuleName, // NOTE: crisis module must go at the end to check for invariants on each module crisistypes.ModuleName, @@ -703,6 +714,7 @@ func (app *Evermint) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint6 BankKeeper: app.BankKeeper, ExtensionOptionChecker: evertypes.HasDynamicFeeExtensionOption, EvmKeeper: app.EvmKeeper, + VAuthKeeper: &app.VAuthKeeper, StakingKeeper: app.StakingKeeper, FeegrantKeeper: app.FeeGrantKeeper, DistributionKeeper: app.DistrKeeper, diff --git a/proto/evermint/vauth/v1/genesis.proto b/proto/evermint/vauth/v1/genesis.proto new file mode 100644 index 0000000000..5c7f2b75a6 --- /dev/null +++ b/proto/evermint/vauth/v1/genesis.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package evermint.vauth.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/EscanBE/evermint/v12/x/vauth/types"; + +// GenesisState defines the module's genesis state. +message GenesisState { +} diff --git a/proto/evermint/vauth/v1/query.proto b/proto/evermint/vauth/v1/query.proto new file mode 100644 index 0000000000..732aed45aa --- /dev/null +++ b/proto/evermint/vauth/v1/query.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package evermint.vauth.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "evermint/vauth/v1/vauth.proto"; + +option go_package = "github.com/EscanBE/evermint/v12/x/vauth/types"; + +// Query defines the gRPC querier service. +service Query { + // ProofExternalOwnedAccount returns proof of external owned account (EOA) + rpc ProofExternalOwnedAccount(QueryProofExternalOwnedAccountRequest) returns (QueryProofExternalOwnedAccountResponse) { + option (google.api.http).get = "/evermint/vauth/v1/proof_external_owned_account"; + } +} + +// QueryProofExternalOwnedAccountRequest is the request type for the Query/ProofExternalOwnedAccount RPC method. +message QueryProofExternalOwnedAccountRequest { + // account to query for + string account = 1; +} + +// QueryProofExternalOwnedAccountResponse is the response type for the Query/ProofExternalOwnedAccount RPC method. +message QueryProofExternalOwnedAccountResponse { + // proof is the proof account is EOA + ProofExternalOwnedAccount proof = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/evermint/vauth/v1/tx.proto b/proto/evermint/vauth/v1/tx.proto new file mode 100644 index 0000000000..be4be28e1a --- /dev/null +++ b/proto/evermint/vauth/v1/tx.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package evermint.vauth.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/EscanBE/evermint/v12/x/vauth/types"; + +// Msg defines the vauth Msg service. +service Msg { + // SubmitProofExternalOwnedAccount submit proof that an account is external owned account (EOA) + rpc SubmitProofExternalOwnedAccount(MsgSubmitProofExternalOwnedAccount) returns (MsgSubmitProofExternalOwnedAccountResponse) { + option (google.api.http).get = "/evermint/vauth/v1/tx/submit_proof_external_owned_account"; + }; +} + +// MsgSubmitProofExternalOwnedAccount defines a Msg to submit EOA proof +message MsgSubmitProofExternalOwnedAccount { + option (cosmos.msg.v1.signer) = "submitter"; + + // submitter is the cosmos bech32 address of the account who submitted the proof + string submitter = 1; + + // account is the cosmos bech32 address that submit proof for + string account = 2; + + // signature is the Ethereum signed message, used to proof that the account is EOA + string signature = 3; +} + +// MsgSubmitProofExternalOwnedAccountResponse returns no fields +message MsgSubmitProofExternalOwnedAccountResponse {} diff --git a/proto/evermint/vauth/v1/vauth.proto b/proto/evermint/vauth/v1/vauth.proto new file mode 100644 index 0000000000..103e050edf --- /dev/null +++ b/proto/evermint/vauth/v1/vauth.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package evermint.vauth.v1; + +import "gogoproto/gogo.proto"; +option go_package = "github.com/EscanBE/evermint/v12/x/vauth/types"; + +// ProofExternalOwnedAccount store the proof that account is external owned account (EOA) +message ProofExternalOwnedAccount { + // account is the cosmos bech32 address of the account that has proof + string account = 1; + // hash is the keccak256 of the message that was signed on + string hash = 2; + // signature is the signed message using private key + string signature = 3; +} diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh index f243ec0b77..efdc000359 100755 --- a/scripts/protoc-swagger-gen.sh +++ b/scripts/protoc-swagger-gen.sh @@ -12,6 +12,7 @@ cp ./proto/buf.gen.swagger.yaml "$SWAGGER_DIR/proto/buf.gen.swagger.yaml" # copy existing proto files cp -r ./proto/evmos "$SWAGGER_DIR/proto" cp -r ./proto/ethermint "$SWAGGER_DIR/proto" +cp -r ./proto/evermint "$SWAGGER_DIR/proto" # create temporary folder to store intermediate results from `buf generate` mkdir -p ./tmp-swagger-gen diff --git a/x/vauth/client/cli/query.go b/x/vauth/client/cli/query.go new file mode 100644 index 0000000000..a8776882c5 --- /dev/null +++ b/x/vauth/client/cli/query.go @@ -0,0 +1,29 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + // Group VAuth queries under a subcommand + cmd := &cobra.Command{ + Use: vauthtypes.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", vauthtypes.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + CmdQueryProofExternalOwnedAccountByAddress(), + ) + + return cmd +} diff --git a/x/vauth/client/cli/query_proof_eoa_by_addr.go b/x/vauth/client/cli/query_proof_eoa_by_addr.go new file mode 100644 index 0000000000..1c45a1f5ff --- /dev/null +++ b/x/vauth/client/cli/query_proof_eoa_by_addr.go @@ -0,0 +1,36 @@ +package cli + +import ( + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" +) + +// CmdQueryProofExternalOwnedAccountByAddress is the CLI command for querying the proof EOA by address +func CmdQueryProofExternalOwnedAccountByAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "proof-eoa [bech32/eth address]", + Aliases: []string{"proof"}, + Short: "Querying the proof external owned account by address", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := vauthtypes.NewQueryClient(clientCtx) + + res, err := queryClient.ProofExternalOwnedAccount(cmd.Context(), &vauthtypes.QueryProofExternalOwnedAccountRequest{ + Account: args[0], + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Proof) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/vauth/client/cli/tx.go b/x/vauth/client/cli/tx.go new file mode 100644 index 0000000000..4c25a64032 --- /dev/null +++ b/x/vauth/client/cli/tx.go @@ -0,0 +1,29 @@ +package cli + +import ( + "fmt" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: vauthtypes.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", vauthtypes.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + NewGenProofTxCmd(), + NewSubmitProofTxCmd(), + ) + + return cmd +} diff --git a/x/vauth/client/cli/tx_gen_proof.go b/x/vauth/client/cli/tx_gen_proof.go new file mode 100644 index 0000000000..3302e3f59c --- /dev/null +++ b/x/vauth/client/cli/tx_gen_proof.go @@ -0,0 +1,88 @@ +package cli + +import ( + "encoding/hex" + "fmt" + + "cosmossdk.io/errors" + + vauthutils "github.com/EscanBE/evermint/v12/x/vauth/utils" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" +) + +// NewGenProofTxCmd is the CLI command for generate proof. +func NewGenProofTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "generate-proof-eoa", + Aliases: []string{"gen-proof"}, + Short: "Generate proof external owned account", + Example: fmt.Sprintf( + "$ %s tx %s generate-proof-eoa --%s fresher", + version.AppName, vauthtypes.ModuleName, + flags.FlagFrom, + ), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + account := clientCtx.GetFromAddress().String() + + if account == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + accAddr := sdk.MustAccAddressFromBech32(account) + + fmt.Println("Bech32 address: ", account) + fmt.Println("EVM address: ", common.BytesToAddress(accAddr)) + + hash := crypto.Keccak256([]byte(vauthtypes.MessageToSign)) + signature, _, err := clientCtx.Keyring.SignByAddress(accAddr, hash) + if err != nil { + return errors.Wrap(err, "failed to sign") + } + + verified, err := vauthutils.VerifySignature(common.BytesToAddress(accAddr), signature, vauthtypes.MessageToSign) + if err != nil { + return errors.Wrap(err, "failed to verify locally") + } + if !verified { + panic("un-expected error, signed message does not match") + } + signatureStr := "0x" + hex.EncodeToString(signature) + + fmt.Println("Generated successfully!!!") + fmt.Println() + + fmt.Println("Use another account to submit the following information:") + fmt.Println("Address: ", account) + fmt.Println("Signature: ", signatureStr) + + fmt.Println() + fmt.Println("Sample submission command:") + fmt.Printf( + "$ %s tx %s %s %s %s --%s submitter\n", + version.AppName, vauthtypes.ModuleName, cmdSubmitProof, + account, signatureStr, + flags.FlagFrom, + ) + + return nil + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/vauth/client/cli/tx_submit_proof.go b/x/vauth/client/cli/tx_submit_proof.go new file mode 100644 index 0000000000..b35a8235cf --- /dev/null +++ b/x/vauth/client/cli/tx_submit_proof.go @@ -0,0 +1,99 @@ +package cli + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/cosmos/cosmos-sdk/client/tx" + + "cosmossdk.io/errors" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + vauthutils "github.com/EscanBE/evermint/v12/x/vauth/utils" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/ethereum/go-ethereum/common" + "github.com/spf13/cobra" +) + +const cmdSubmitProof = "submit-proof-eoa" + +// NewSubmitProofTxCmd is the CLI command for submit proof. +func NewSubmitProofTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s [address] [signature]", cmdSubmitProof), + Aliases: []string{"submit-proof"}, + Short: "Submit proof account is EOA", + Example: fmt.Sprintf( + "$ %s tx %s %s %s1... 0x1234... --%s submitter", + version.AppName, vauthtypes.ModuleName, cmdSubmitProof, + constants.Bech32PrefixAccAddr, + flags.FlagFrom, + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + submitter := clientCtx.GetFromAddress().String() + + if submitter == "" { + return fmt.Errorf("flag --%s is required", flags.FlagFrom) + } + + account := strings.ToLower(args[0]) + signature := strings.ToLower(args[1]) + + accAddr, err := sdk.AccAddressFromBech32(account) + if err == nil { + // ok + } else if strings.HasPrefix(account, "0x") && len(account) == 42 { + if addr := common.HexToAddress(account); addr != (common.Address{}) { + accAddr = addr.Bytes() + } + } + + if len(accAddr) == 0 { + return fmt.Errorf("input is not a valid address") + } + account = accAddr.String() + + if account == submitter { + return fmt.Errorf("submitter cannot be the proof account, please use another account") + } + + if !strings.HasPrefix(signature, "0x") { + signature = "0x" + signature + } + + bzSignature, err := hex.DecodeString(signature[2:]) + if err != nil { + return errors.Wrap(err, "failed to decode signature") + } + + verified, err := vauthutils.VerifySignature(common.BytesToAddress(accAddr), bzSignature, vauthtypes.MessageToSign) + if err != nil { + return errors.Wrap(err, "failed to verify locally") + } + if !verified { + return fmt.Errorf("un-expected error, signature does not match") + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: submitter, + Account: account, + Signature: signature, + }) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/vauth/keeper/constants.go b/x/vauth/keeper/constants.go new file mode 100644 index 0000000000..bb970c995d --- /dev/null +++ b/x/vauth/keeper/constants.go @@ -0,0 +1,4 @@ +package keeper + +// CostSubmitProofExternalOwnedAccount cost for each submission +const CostSubmitProofExternalOwnedAccount int64 = 1e18 diff --git a/x/vauth/keeper/grpc_query.go b/x/vauth/keeper/grpc_query.go new file mode 100644 index 0000000000..b069ca2141 --- /dev/null +++ b/x/vauth/keeper/grpc_query.go @@ -0,0 +1,50 @@ +package keeper + +import ( + "context" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" +) + +var _ vauthtypes.QueryServer = queryServer{} + +type queryServer struct { + Keeper +} + +// NewQueryServerImpl returns an implementation of the QueryServer interface +func NewQueryServerImpl(keeper Keeper) vauthtypes.QueryServer { + return &queryServer{Keeper: keeper} +} + +// ProofExternalOwnedAccount returns proof of external owned account (EOA) +func (q queryServer) ProofExternalOwnedAccount(goCtx context.Context, req *vauthtypes.QueryProofExternalOwnedAccountRequest) (*vauthtypes.QueryProofExternalOwnedAccountResponse, error) { + if req == nil || req.Account == "" { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + var proof *vauthtypes.ProofExternalOwnedAccount + if accAddr, err := sdk.AccAddressFromBech32(req.Account); err == nil { + proof = q.GetProofExternalOwnedAccount(ctx, accAddr) + } else if strings.HasPrefix(req.Account, "0x") && len(req.Account) == 42 { + if addr := common.HexToAddress(req.Account); addr != (common.Address{}) { + proof = q.GetProofExternalOwnedAccount(ctx, addr.Bytes()) + } + } + + if proof == nil { + return nil, status.Errorf(codes.NotFound, "no proof available for: %s", req.Account) + } + + return &vauthtypes.QueryProofExternalOwnedAccountResponse{ + Proof: *proof, + }, nil +} diff --git a/x/vauth/keeper/grpc_query_test.go b/x/vauth/keeper/grpc_query_test.go new file mode 100644 index 0000000000..f4b09ac48f --- /dev/null +++ b/x/vauth/keeper/grpc_query_test.go @@ -0,0 +1,70 @@ +package keeper_test + +import ( + "strings" + + vauthkeeper "github.com/EscanBE/evermint/v12/x/vauth/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + "github.com/ethereum/go-ethereum/common" +) + +func (s *KeeperTestSuite) Test_queryServer_ProofExternalOwnedAccount() { + proof := vauthtypes.ProofExternalOwnedAccount{ + Account: s.accAddr.String(), + Hash: s.HashToStr(vauthtypes.MessageToSign), + Signature: s.SignToStr(vauthtypes.MessageToSign), + } + + err := s.keeper.SaveProofExternalOwnedAccount(s.ctx, proof) + s.Require().NoError(err) + + queryServer := vauthkeeper.NewQueryServerImpl(s.keeper) + query := func(addr string) (*vauthtypes.QueryProofExternalOwnedAccountResponse, error) { + return queryServer.ProofExternalOwnedAccount(s.ctx, &vauthtypes.QueryProofExternalOwnedAccountRequest{ + Account: addr, + }) + } + + s.Run("pass - query by bech32 address", func() { + resp, err := query(s.accAddr.String()) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal(proof, resp.Proof) + }) + + s.Run("pass - query by 0x address, checksum format", func() { + resp, err := query(common.BytesToAddress(s.accAddr).String()) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal(proof, resp.Proof) + }) + + s.Run("pass - query by 0x address, lowercase format", func() { + resp, err := query(strings.ToLower(common.BytesToAddress(s.accAddr).String())) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Equal(proof, resp.Proof) + }) + + s.Run("fail - address not exists", func() { + resp, err := query(s.submitterAccAddr.String()) + s.Require().Error(err) + s.Require().Nil(resp) + + resp, err = query(common.BytesToAddress(s.submitterAccAddr).String()) + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("fail - empty address", func() { + resp, err := query("") + s.Require().Error(err) + s.Require().Nil(resp) + }) + + s.Run("fail - invalid address", func() { + resp, err := query("0x1") + s.Require().Error(err) + s.Require().Nil(resp) + }) +} diff --git a/x/vauth/keeper/keeper.go b/x/vauth/keeper/keeper.go new file mode 100644 index 0000000000..a50f9c2699 --- /dev/null +++ b/x/vauth/keeper/keeper.go @@ -0,0 +1,42 @@ +package keeper + +import ( + "fmt" + + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + evmkeeper "github.com/EscanBE/evermint/v12/x/evm/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Keeper of the VAuth store +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + bankKeeper bankkeeper.Keeper + evmKeeper evmkeeper.Keeper +} + +// NewKeeper returns a new instance of the VAuth keeper +func NewKeeper( + cdc codec.BinaryCodec, + key storetypes.StoreKey, + bk bankkeeper.Keeper, + ek evmkeeper.Keeper, +) Keeper { + return Keeper{ + cdc: cdc, + storeKey: key, + bankKeeper: bk, + evmKeeper: ek, + } +} + +// Logger returns a module-specific logger. +func (k *Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", vauthtypes.ModuleName)) +} diff --git a/x/vauth/keeper/keeper_suite_test.go b/x/vauth/keeper/keeper_suite_test.go new file mode 100644 index 0000000000..e41d6dc40a --- /dev/null +++ b/x/vauth/keeper/keeper_suite_test.go @@ -0,0 +1,234 @@ +package keeper_test + +import ( + "crypto/ecdsa" + "encoding/hex" + "testing" + "time" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/EscanBE/evermint/v12/rename_chain/marker" + evmkeeper "github.com/EscanBE/evermint/v12/x/evm/keeper" + evmtypes "github.com/EscanBE/evermint/v12/x/evm/types" + vauthkeeper "github.com/EscanBE/evermint/v12/x/vauth/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/ethereum/go-ethereum/crypto" + + tmdb "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/suite" +) + +type KeeperTestSuite struct { + suite.Suite + + savedCtx sdk.Context + ctx sdk.Context + + chainId string + now time.Time + + cdc codec.BinaryCodec + + // keepers + authKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + evmKeeper evmkeeper.Keeper + keeper vauthkeeper.Keeper + + // test account + privateKey *ecdsa.PrivateKey + accAddr sdk.AccAddress + submitterAccAddr sdk.AccAddress +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (s *KeeperTestSuite) SetupSuite() { +} + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) SetupTest() { + var ctx sdk.Context + + var cdc codec.BinaryCodec + + var ak authkeeper.AccountKeeper + var bk bankkeeper.Keeper + var ek evmkeeper.Keeper + var vk vauthkeeper.Keeper + + { + // initialization + authStoreKey := sdk.NewKVStoreKey(authtypes.StoreKey) + bankStoreKey := sdk.NewKVStoreKey(banktypes.StoreKey) + evmStoreKey := sdk.NewKVStoreKey(evmtypes.StoreKey) + vAuthStoreKey := sdk.NewKVStoreKey(vauthtypes.StoreKey) + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(authStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(bankStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(evmStoreKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(vAuthStoreKey, storetypes.StoreTypeIAVL, db) + s.Require().NoError(stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc = codec.NewProtoCodec(registry) + amino := codec.NewLegacyAmino() + + evmParamsSubspace := typesparams.NewSubspace(cdc, + amino, + evmStoreKey, + nil, + "EvmParams", + ) + + ak = authkeeper.NewAccountKeeper( + cdc, + authStoreKey, + authtypes.ProtoBaseAccount, + map[string][]string{ + banktypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + vauthtypes.ModuleName: {authtypes.Burner}, + }, + constants.Bech32PrefixAccAddr, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + authtypes.RegisterInterfaces(registry) + + bk = bankkeeper.NewBaseKeeper( + cdc, + bankStoreKey, + ak, + map[string]bool{}, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + banktypes.RegisterInterfaces(registry) + + ek = *evmkeeper.NewKeeper( + cdc, + evmStoreKey, + nil, // transient key + authtypes.NewModuleAddress(govtypes.ModuleName), // authority + ak, + bk, + nil, // staking keeper + nil, // feemarket keeper + "", // tracer + evmParamsSubspace, + ) + + vk = vauthkeeper.NewKeeper( + cdc, + vAuthStoreKey, + bk, + ek, + ) + + ctx = sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + } + + privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") + s.Require().NoError(err) + + // set + s.chainId = constants.DevnetChainID + s.now = time.Now().UTC() + s.savedCtx = sdk.Context{} + s.ctx = ctx.WithBlockTime(s.now).WithChainID(constants.DevnetChainID) + s.cdc = cdc + s.authKeeper = ak + s.bankKeeper = bk + s.evmKeeper = ek + s.keeper = vk + s.privateKey = privateKey + s.accAddr = sdk.MustAccAddressFromBech32(marker.ReplaceAbleAddress("evm1jcsksjwyjdvtzqjhed2m9r4xq0y8fvz7zqvgem")) + s.submitterAccAddr = sdk.MustAccAddressFromBech32(marker.ReplaceAbleAddress("evm13zqksjwyjdvtzqjhed2m9r4xq0y8fvyg85jr6a")) + + // others + + s.Require().NoError( + s.evmKeeper.SetParams(s.ctx, evmtypes.DefaultParams()), + ) + + s.SaveCurrentContext() +} + +func (s *KeeperTestSuite) AfterTest(_, _ string) { +} + +// SaveCurrentContext saves the current context and convert current context into a branch context. +// This is useful when you want to set up a context and reuse multiple times. +// This is less expensive than call SetupTest. +func (s *KeeperTestSuite) SaveCurrentContext() { + s.savedCtx = s.ctx + s.RefreshContext() +} + +// RefreshContext clear any change to the current context and use a new copy of the saved context. +func (s *KeeperTestSuite) RefreshContext() { + if s.savedCtx.ChainID() == "" { + panic("saved context not set") + } + s.ctx, _ = s.savedCtx.CacheContext() + if gasMeter := s.ctx.GasMeter(); gasMeter != nil { + gasMeter.RefundGas(gasMeter.GasConsumed(), "reset gas meter") + } + if blockGasMeter := s.ctx.BlockGasMeter(); blockGasMeter != nil { + blockGasMeter.RefundGas(blockGasMeter.GasConsumed(), "reset block gas meter") + } +} + +func (s *KeeperTestSuite) Hash(message string) []byte { + return crypto.Keccak256([]byte(message)) +} + +func (s *KeeperTestSuite) HashToStr(message string) string { + return "0x" + hex.EncodeToString(s.Hash(message)) +} + +func (s *KeeperTestSuite) Sign(message string) []byte { + signature, err := crypto.Sign(s.Hash(message), s.privateKey) + s.Require().NoError(err) + return signature +} + +func (s *KeeperTestSuite) SignToStr(message string) string { + return "0x" + hex.EncodeToString(s.Sign(message)) +} + +func (s *KeeperTestSuite) mintToModuleAccount(coins sdk.Coins) { + err := s.bankKeeper.MintCoins(s.ctx, banktypes.ModuleName, coins) + s.Require().NoError(err) +} + +func (s *KeeperTestSuite) mintToAccount(accAddr sdk.AccAddress, coins sdk.Coins) { + s.mintToModuleAccount(coins) + err := s.bankKeeper.SendCoinsFromModuleToAccount( + s.ctx, + banktypes.ModuleName, + accAddr, + coins, + ) + s.Require().NoError(err) +} + +func init() { + sdk.GetConfig().SetBech32PrefixForAccount(constants.Bech32PrefixAccAddr, "") +} diff --git a/x/vauth/keeper/msg_server.go b/x/vauth/keeper/msg_server.go new file mode 100644 index 0000000000..638d6acd64 --- /dev/null +++ b/x/vauth/keeper/msg_server.go @@ -0,0 +1,17 @@ +package keeper + +import ( + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" +) + +var _ vauthtypes.MsgServer = msgServer{} + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) vauthtypes.MsgServer { + return &msgServer{Keeper: keeper} +} diff --git a/x/vauth/keeper/msg_server_submit_proof_external_owned_account.go b/x/vauth/keeper/msg_server_submit_proof_external_owned_account.go new file mode 100644 index 0000000000..25b98a3932 --- /dev/null +++ b/x/vauth/keeper/msg_server_submit_proof_external_owned_account.go @@ -0,0 +1,57 @@ +package keeper + +import ( + "context" + "encoding/hex" + "github.com/cosmos/cosmos-sdk/types/errors" + + errorsmod "cosmossdk.io/errors" + + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// SubmitProofExternalOwnedAccount submit proof that an account is external owned account (EOA) +func (m msgServer) SubmitProofExternalOwnedAccount(goCtx context.Context, msg *vauthtypes.MsgSubmitProofExternalOwnedAccount) (*vauthtypes.MsgSubmitProofExternalOwnedAccountResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + if m.HasProofExternalOwnedAccount(ctx, sdk.MustAccAddressFromBech32(msg.Account)) { + return nil, errorsmod.Wrapf(errors.ErrConflict, "account already have proof: %s", msg.Account) + } + + // charge fee + + evmParams := m.evmKeeper.GetParams(ctx) + fees := sdk.NewCoins(sdk.NewInt64Coin(evmParams.EvmDenom, CostSubmitProofExternalOwnedAccount)) + err := m.bankKeeper.SendCoinsFromAccountToModule( + ctx, + sdk.MustAccAddressFromBech32(msg.Submitter), vauthtypes.ModuleName, + fees, + ) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to deduct fee from submitter") + } + err = m.bankKeeper.BurnCoins(ctx, vauthtypes.ModuleName, fees) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to burn coin from module account: %s", vauthtypes.ModuleName) + } + + // persist + + proof := vauthtypes.ProofExternalOwnedAccount{ + Account: msg.Account, + Hash: "0x" + hex.EncodeToString(crypto.Keccak256([]byte(vauthtypes.MessageToSign))), + Signature: msg.Signature, + } + + if err := m.SaveProofExternalOwnedAccount(ctx, proof); err != nil { + panic(err) + } + + return &vauthtypes.MsgSubmitProofExternalOwnedAccountResponse{}, nil +} diff --git a/x/vauth/keeper/msg_server_submit_proof_external_owned_account_test.go b/x/vauth/keeper/msg_server_submit_proof_external_owned_account_test.go new file mode 100644 index 0000000000..42f15ba2d1 --- /dev/null +++ b/x/vauth/keeper/msg_server_submit_proof_external_owned_account_test.go @@ -0,0 +1,166 @@ +package keeper_test + +import ( + "github.com/EscanBE/evermint/v12/constants" + "github.com/EscanBE/evermint/v12/rename_chain/marker" + vauthkeeper "github.com/EscanBE/evermint/v12/x/vauth/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" +) + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) Test_msgServer_SubmitProofExternalOwnedAccount() { + tests := []struct { + name string + msg *vauthtypes.MsgSubmitProofExternalOwnedAccount + submitterBalance int64 + preRunFunc func(s *KeeperTestSuite) + wantErr bool + wantErrContains string + postRunFunc func(s *KeeperTestSuite) + }{ + { + name: "pass - can submit and persist", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.submitterAccAddr.String(), + Account: s.accAddr.String(), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount, + wantErr: false, + postRunFunc: func(s *KeeperTestSuite) { + s.Require().True(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + s.Equal(vauthtypes.ProofExternalOwnedAccount{ + Account: s.accAddr.String(), + Hash: s.HashToStr(vauthtypes.MessageToSign), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, *s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + + s.False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.submitterAccAddr)) + s.Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.submitterAccAddr)) + }, + }, + { + name: "fail - can not proof twice", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.submitterAccAddr.String(), + Account: s.accAddr.String(), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount, + preRunFunc: func(s *KeeperTestSuite) { + err := s.keeper.SaveProofExternalOwnedAccount(s.ctx, vauthtypes.ProofExternalOwnedAccount{ + Account: s.accAddr.String(), + Hash: s.HashToStr(vauthtypes.MessageToSign), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }) + s.Require().NoError(err) + + s.Require().True(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + wantErr: true, + wantErrContains: "account already have proof", + postRunFunc: func(s *KeeperTestSuite) { + s.Require().True(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + }, + { + name: "fail - fail tx does not persist, mis-match message", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.submitterAccAddr.String(), + Account: s.accAddr.String(), + Signature: s.SignToStr("invalid"), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount, + wantErr: true, + wantErrContains: errors.ErrInvalidRequest.Error(), + postRunFunc: func(s *KeeperTestSuite) { + s.False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + s.Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + }, + { + name: "fail - fail tx does not persist, submitter and account to prove are equals", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.accAddr.String(), + Account: s.accAddr.String(), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount, + wantErr: true, + wantErrContains: errors.ErrInvalidRequest.Error(), + postRunFunc: func(s *KeeperTestSuite) { + s.False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + s.Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + }, + { + name: "fail - fail tx does not persist, mis-match address", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.submitterAccAddr.String(), + Account: marker.ReplaceAbleAddress("evm13zqksjwyjdvtzqjhed2m9r4xq0y8fvz79xjsqd"), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount, + wantErr: true, + wantErrContains: errors.ErrInvalidRequest.Error(), + postRunFunc: func(s *KeeperTestSuite) { + s.False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + s.Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + }, + { + name: "fail - insufficient balance", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{ + Submitter: s.submitterAccAddr.String(), + Account: s.accAddr.String(), + Signature: s.SignToStr(vauthtypes.MessageToSign), + }, + submitterBalance: vauthkeeper.CostSubmitProofExternalOwnedAccount - 1, + wantErr: true, + wantErrContains: "failed to deduct fee from submitter", + postRunFunc: func(s *KeeperTestSuite) { + s.False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + s.Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + }, + }, + { + name: "fail - reject bad message", + msg: &vauthtypes.MsgSubmitProofExternalOwnedAccount{}, + wantErr: true, + wantErrContains: errors.ErrInvalidRequest.Error(), + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + s.RefreshContext() + + if tt.submitterBalance > 0 && tt.msg != nil { + if submitterAccAddr, err := sdk.AccAddressFromBech32(tt.msg.Submitter); err == nil { + s.mintToAccount(submitterAccAddr, sdk.NewCoins(sdk.NewInt64Coin(constants.BaseDenom, tt.submitterBalance))) + } + } + + if tt.preRunFunc != nil { + tt.preRunFunc(s) + } + + resp, err := vauthkeeper.NewMsgServerImpl(s.keeper).SubmitProofExternalOwnedAccount(s.ctx, tt.msg) + + defer func() { + if tt.postRunFunc != nil { + tt.postRunFunc(s) + } + }() + + if tt.wantErr { + s.Require().ErrorContains(err, tt.wantErrContains) + s.Nil(resp) + return + } + + s.Require().NotNil(resp) + }) + } +} diff --git a/x/vauth/keeper/proof.go b/x/vauth/keeper/proof.go new file mode 100644 index 0000000000..226e7c929c --- /dev/null +++ b/x/vauth/keeper/proof.go @@ -0,0 +1,44 @@ +package keeper + +import ( + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SaveProofExternalOwnedAccount persists proof into KVStore. +func (k Keeper) SaveProofExternalOwnedAccount(ctx sdk.Context, proof vauthtypes.ProofExternalOwnedAccount) error { + if err := proof.ValidateBasic(); err != nil { + return err + } + + // persist record + store := ctx.KVStore(k.storeKey) + key := vauthtypes.KeyProofExternalOwnedAccountByAddress(sdk.MustAccAddressFromBech32(proof.Account)) + bz := k.cdc.MustMarshal(&proof) + store.Set(key, bz) + + return nil +} + +// GetProofExternalOwnedAccount retrieves proof from KVStore. +// It returns nil if not found +func (k Keeper) GetProofExternalOwnedAccount(ctx sdk.Context, accAddr sdk.AccAddress) *vauthtypes.ProofExternalOwnedAccount { + store := ctx.KVStore(k.storeKey) + key := vauthtypes.KeyProofExternalOwnedAccountByAddress(accAddr) + bz := store.Get(key) + if len(bz) == 0 { + return nil + } + + var proof vauthtypes.ProofExternalOwnedAccount + k.cdc.MustUnmarshal(bz, &proof) + + return &proof +} + +// HasProofExternalOwnedAccount check if proof of EOA of the account exists in KVStore. +func (k Keeper) HasProofExternalOwnedAccount(ctx sdk.Context, accAddr sdk.AccAddress) bool { + store := ctx.KVStore(k.storeKey) + key := vauthtypes.KeyProofExternalOwnedAccountByAddress(accAddr) + return store.Has(key) +} diff --git a/x/vauth/keeper/proof_test.go b/x/vauth/keeper/proof_test.go new file mode 100644 index 0000000000..6ba9f246e2 --- /dev/null +++ b/x/vauth/keeper/proof_test.go @@ -0,0 +1,40 @@ +package keeper_test + +import vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + +//goland:noinspection SpellCheckingInspection +func (s *KeeperTestSuite) TestKeeper_GetSaveHasProofExternalOwnedAccount() { + s.Run("get - proof not set, returns nil", func() { + s.Require().Nil(s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr)) + }) + + s.Run("has - proof not set, returns false", func() { + s.Require().False(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + }) + + proof := vauthtypes.ProofExternalOwnedAccount{ + Account: s.accAddr.String(), + Hash: s.HashToStr(vauthtypes.MessageToSign), + Signature: s.SignToStr(vauthtypes.MessageToSign), + } + + s.Run("set - success", func() { + err := s.keeper.SaveProofExternalOwnedAccount(s.ctx, proof) + s.Require().NoError(err) + }) + + s.Run("get - proof had been set, returns proof", func() { + gotProof := s.keeper.GetProofExternalOwnedAccount(s.ctx, s.accAddr) + s.Require().NotNil(gotProof) + s.Require().Equal(proof, *gotProof) + }) + + s.Run("has - proof had been set, returns true", func() { + s.Require().True(s.keeper.HasProofExternalOwnedAccount(s.ctx, s.accAddr)) + }) + + s.Run("set - reject invalid proof", func() { + err := s.keeper.SaveProofExternalOwnedAccount(s.ctx, vauthtypes.ProofExternalOwnedAccount{}) + s.Require().Error(err) + }) +} diff --git a/x/vauth/module.go b/x/vauth/module.go new file mode 100644 index 0000000000..e1f2fdbd17 --- /dev/null +++ b/x/vauth/module.go @@ -0,0 +1,132 @@ +package vauth + +import ( + "context" + "encoding/json" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + vauthclient "github.com/EscanBE/evermint/v12/x/vauth/client/cli" + vauthkeeper "github.com/EscanBE/evermint/v12/x/vauth/keeper" + vauthtypes "github.com/EscanBE/evermint/v12/x/vauth/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +// NewAppModuleBasic creates a new AppModuleBasic object +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return vauthtypes.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + vauthtypes.RegisterCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + vauthtypes.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(vauthtypes.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(_ codec.JSONCodec, _ client.TxEncodingConfig, _ json.RawMessage) error { + return nil +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + _ = vauthtypes.RegisterQueryHandlerClient(context.Background(), mux, vauthtypes.NewQueryClient(clientCtx)) +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return vauthclient.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return vauthclient.GetQueryCmd() +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper vauthkeeper.Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule( + cdc codec.Codec, + keeper vauthkeeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + vauthtypes.RegisterMsgServer(cfg.MsgServer(), vauthkeeper.NewMsgServerImpl(am.keeper)) + vauthtypes.RegisterQueryServer(cfg.QueryServer(), vauthkeeper.NewQueryServerImpl(am.keeper)) +} + +// RegisterInvariants registers the module's invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONCodec, _ json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(vauthtypes.DefaultGenesis()) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock contains the logic that is automatically triggered at the beginning of each block +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock contains the logic that is automatically triggered at the end of each block +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/vauth/types/codec.go b/x/vauth/types/codec.go new file mode 100644 index 0000000000..b7d49d6395 --- /dev/null +++ b/x/vauth/types/codec.go @@ -0,0 +1,28 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterCodec registers the necessary types and interfaces for the module +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgSubmitProofExternalOwnedAccount{}, "vauth/SubmitProofExternalOwnedAccount", nil) +} + +// RegisterInterfaces registers implementations by its interface, for the module +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgSubmitProofExternalOwnedAccount{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/vauth/types/codec_test.go b/x/vauth/types/codec_test.go new file mode 100644 index 0000000000..bdb14b6989 --- /dev/null +++ b/x/vauth/types/codec_test.go @@ -0,0 +1,25 @@ +package types + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/stretchr/testify/require" +) + +func TestRegisterCodec(t *testing.T) { + t.Run("can register codec", func(t *testing.T) { + require.NotPanics(t, func() { + RegisterCodec(codec.NewLegacyAmino()) + }) + }) +} + +func TestRegisterInterfaces(t *testing.T) { + t.Run("can register interfaces", func(t *testing.T) { + require.NotPanics(t, func() { + RegisterInterfaces(cdctypes.NewInterfaceRegistry()) + }) + }) +} diff --git a/x/vauth/types/constants.go b/x/vauth/types/constants.go new file mode 100644 index 0000000000..9d9e583f30 --- /dev/null +++ b/x/vauth/types/constants.go @@ -0,0 +1,4 @@ +package types + +// MessageToSign is the message for user to sign on to prove account is EOA +const MessageToSign = ModuleName diff --git a/x/vauth/types/genesis.go b/x/vauth/types/genesis.go new file mode 100644 index 0000000000..3e9d48a162 --- /dev/null +++ b/x/vauth/types/genesis.go @@ -0,0 +1,12 @@ +package types + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{} +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (m GenesisState) Validate() error { + return nil +} diff --git a/x/vauth/types/genesis.pb.go b/x/vauth/types/genesis.pb.go new file mode 100644 index 0000000000..be0dc8f191 --- /dev/null +++ b/x/vauth/types/genesis.pb.go @@ -0,0 +1,265 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: evermint/vauth/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the module's genesis state. +type GenesisState struct { +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_0cec357fc483e78f, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func init() { + proto.RegisterType((*GenesisState)(nil), "evermint.vauth.v1.GenesisState") +} + +func init() { proto.RegisterFile("evermint/vauth/v1/genesis.proto", fileDescriptor_0cec357fc483e78f) } + +var fileDescriptor_0cec357fc483e78f = []byte{ + // 159 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0x2d, 0x4b, 0x2d, + 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x4b, 0x2c, 0x2d, 0xc9, 0xd0, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, + 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0x29, 0xd0, + 0x03, 0x2b, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83, 0x58, + 0x10, 0x85, 0x4a, 0x7c, 0x5c, 0x3c, 0xee, 0x10, 0x9d, 0xc1, 0x25, 0x89, 0x25, 0xa9, 0x4e, 0xee, + 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, + 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9b, 0x9e, 0x59, 0x92, 0x51, + 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0xef, 0x5a, 0x9c, 0x9c, 0x98, 0xe7, 0xe4, 0xaa, 0x8f, 0x70, + 0x86, 0xa1, 0x91, 0x7e, 0x05, 0xd4, 0x31, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0xf3, + 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x23, 0x7d, 0x02, 0xbd, 0xab, 0x00, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/vauth/types/keys.go b/x/vauth/types/keys.go new file mode 100644 index 0000000000..e7c5964ea2 --- /dev/null +++ b/x/vauth/types/keys.go @@ -0,0 +1,25 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + // ModuleName defines the module name + ModuleName = "vauth" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName +) + +// prefix bytes for the VAuth persistent store. +const ( + prefixProofExternalOwnedAccount = iota + 1 +) + +var KeyPrefixProofExternalOwnedAccount = []byte{prefixProofExternalOwnedAccount} + +func KeyProofExternalOwnedAccountByAddress(accAddr sdk.AccAddress) []byte { + return append(KeyPrefixProofExternalOwnedAccount, accAddr.Bytes()...) +} diff --git a/x/vauth/types/msg_submit_proof_external_owned_account.go b/x/vauth/types/msg_submit_proof_external_owned_account.go new file mode 100644 index 0000000000..d68da2bc46 --- /dev/null +++ b/x/vauth/types/msg_submit_proof_external_owned_account.go @@ -0,0 +1,77 @@ +package types + +import ( + "bytes" + "encoding/hex" + "strings" + + vauthutils "github.com/EscanBE/evermint/v12/x/vauth/utils" + "github.com/ethereum/go-ethereum/common" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" +) + +var _ sdk.Msg = &MsgSubmitProofExternalOwnedAccount{} + +// ValidateBasic performs basic validation for the MsgSubmitProofExternalOwnedAccount. +func (m *MsgSubmitProofExternalOwnedAccount) ValidateBasic() error { + submitterAccAddr, err := sdk.AccAddressFromBech32(m.Submitter) + if err != nil { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "submitter is not a valid bech32 account address: %s", m.Submitter) + } + + accAddr, err := sdk.AccAddressFromBech32(m.Account) + if err != nil { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "account to prove is not a valid bech32 account address: %s", m.Account) + } + + if bytes.Equal(submitterAccAddr, accAddr) { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "submitter and account to prove are equals: %s", m.Account) + } + + if !strings.HasPrefix(m.Signature, "0x") { + return errorsmod.Wrap(errors.ErrInvalidRequest, "signature must starts with 0x") + } + + bzSignature, err := hex.DecodeString(m.Signature[2:]) + if err != nil || len(bzSignature) < 1 { + return errorsmod.Wrap(errors.ErrInvalidRequest, "bad signature") + } + + verified, err := vauthutils.VerifySignature(common.BytesToAddress(accAddr), bzSignature, MessageToSign) + if err != nil { + return errorsmod.Wrap(errors.ErrInvalidRequest, "bad signature or mis-match") + } + if !verified { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "mis-match signature with provided account: %s", common.BytesToAddress(accAddr)) + } + + return nil +} + +// GetSigners returns the required signers for the MsgSubmitProofExternalOwnedAccount. +func (m *MsgSubmitProofExternalOwnedAccount) GetSigners() []sdk.AccAddress { + owner, err := sdk.AccAddressFromBech32(m.Submitter) + if err != nil { + panic(err) + } + return []sdk.AccAddress{owner} +} + +// Route returns the message router key for the MsgSubmitProofExternalOwnedAccount. +func (m *MsgSubmitProofExternalOwnedAccount) Route() string { + return RouterKey +} + +// Type returns the message type for the MsgSubmitProofExternalOwnedAccount. +func (m *MsgSubmitProofExternalOwnedAccount) Type() string { + return TypeMsgSubmitProofExternalOwnedAccount +} + +// GetSignBytes returns the raw bytes for the MsgSubmitProofExternalOwnedAccount. +func (m *MsgSubmitProofExternalOwnedAccount) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} diff --git a/x/vauth/types/msg_submit_proof_external_owned_account_test.go b/x/vauth/types/msg_submit_proof_external_owned_account_test.go new file mode 100644 index 0000000000..dbee58ef0b --- /dev/null +++ b/x/vauth/types/msg_submit_proof_external_owned_account_test.go @@ -0,0 +1,121 @@ +package types + +import ( + "encoding/hex" + "testing" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/EscanBE/evermint/v12/rename_chain/marker" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestMsgSubmitProofExternalOwnedAccount_ValidateBasic(t *testing.T) { + privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") + require.NoError(t, err) + + submitterBech32 := marker.ReplaceAbleAddress("evm13zqksjwyjdvtzqjhed2m9r4xq0y8fvyg85jr6a") + addressBech32 := marker.ReplaceAbleAddress("evm1jcsksjwyjdvtzqjhed2m9r4xq0y8fvz7zqvgem") + + signature := func(message string) []byte { + signature, err := crypto.Sign(crypto.Keccak256([]byte(message)), privateKey) + require.NoError(t, err) + return signature + } + + tests := []struct { + name string + submitter string + address string + signature string + wantErr bool + wantErrContains string + }{ + { + name: "pass - verify success", + submitter: submitterBech32, + address: addressBech32, + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: false, + }, + { + name: "fail - submitter and address must be different", + submitter: addressBech32, + address: addressBech32, + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "submitter and account to prove are equals", + }, + { + name: "fail - not address of the signature", + submitter: submitterBech32, + address: marker.ReplaceAbleAddress("evm13zqksjwyjdvtzqjhed2m9r4xq0y8fvz79xjsqd"), + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "mis-match signature with provided account:", + }, + { + name: "fail - signature of another message", + submitter: submitterBech32, + address: addressBech32, + signature: "0x" + hex.EncodeToString(signature("another")), + wantErr: true, + wantErrContains: "mis-match signature with provided account:", + }, + { + name: "fail - bad submitter", + submitter: "", + address: addressBech32, + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "submitter is not a valid bech32 account address", + }, + { + name: "fail - bad address", + submitter: submitterBech32, + address: "", + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "account to prove is not a valid bech32 account address", + }, + { + name: "fail - bad signature, missing 0x prefix", + submitter: submitterBech32, + address: addressBech32, + signature: hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "signature must starts with 0x", + }, + { + name: "fail - bad signature", + submitter: submitterBech32, + address: addressBech32, + signature: "0x1", + wantErr: true, + wantErrContains: "bad signature:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &MsgSubmitProofExternalOwnedAccount{ + Submitter: tt.submitter, + Account: tt.address, + Signature: tt.signature, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.ErrorContains(t, err, tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func init() { + sdk.GetConfig().SetBech32PrefixForAccount(constants.Bech32PrefixAccAddr, "") +} diff --git a/x/vauth/types/msgs.go b/x/vauth/types/msgs.go new file mode 100644 index 0000000000..a30790bce5 --- /dev/null +++ b/x/vauth/types/msgs.go @@ -0,0 +1,4 @@ +package types + +// TypeMsgSubmitProofExternalOwnedAccount is type for MsgSubmitProofExternalOwnedAccount. +const TypeMsgSubmitProofExternalOwnedAccount = "submit_proof_external_owned_account" diff --git a/x/vauth/types/proof_external_owned_account.go b/x/vauth/types/proof_external_owned_account.go new file mode 100644 index 0000000000..77cd5e45be --- /dev/null +++ b/x/vauth/types/proof_external_owned_account.go @@ -0,0 +1,50 @@ +package types + +import ( + "encoding/hex" + "strings" + + errorsmod "cosmossdk.io/errors" + + vauthutils "github.com/EscanBE/evermint/v12/x/vauth/utils" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" +) + +func (m *ProofExternalOwnedAccount) ValidateBasic() error { + accAddr, err := sdk.AccAddressFromBech32(m.Account) + if err != nil { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "account is not a valid bech32 account address: %s", m.Account) + } + + if !strings.HasPrefix(m.Hash, "0x") { + return errorsmod.Wrap(errors.ErrInvalidRequest, "hash must starts with 0x") + } + + if strings.ToLower(common.HexToHash(m.Hash).String()) != m.Hash { + return errorsmod.Wrap(errors.ErrInvalidRequest, "hash must be 32 bytes lowercase") + } + + if !strings.HasPrefix(m.Signature, "0x") { + return errorsmod.Wrap(errors.ErrInvalidRequest, "signature must starts with 0x") + } + if strings.ToLower(m.Signature) != m.Signature { + return errorsmod.Wrap(errors.ErrInvalidRequest, "signature must be lowercase") + } + + bzSignature, err := hex.DecodeString(m.Signature[2:]) + if err != nil || len(bzSignature) < 1 { + return errorsmod.Wrap(errors.ErrInvalidRequest, "bad signature") + } + + verified, err := vauthutils.VerifySignature(common.BytesToAddress(accAddr), bzSignature, MessageToSign) + if err != nil { + return errorsmod.Wrap(errors.ErrInvalidRequest, "bad signature or mis-match") + } + if !verified { + return errorsmod.Wrapf(errors.ErrInvalidRequest, "mis-match signature with provided address: %s", common.BytesToAddress(accAddr)) + } + + return nil +} diff --git a/x/vauth/types/proof_external_owned_account_test.go b/x/vauth/types/proof_external_owned_account_test.go new file mode 100644 index 0000000000..4c0fc5be49 --- /dev/null +++ b/x/vauth/types/proof_external_owned_account_test.go @@ -0,0 +1,138 @@ +package types + +import ( + "encoding/hex" + "strings" + "testing" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/EscanBE/evermint/v12/rename_chain/marker" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +//goland:noinspection SpellCheckingInspection +func TestProofExternalOwnedAccount_ValidateBasic(t *testing.T) { + privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") + require.NoError(t, err) + + addressBech32 := marker.ReplaceAbleAddress("evm1jcsksjwyjdvtzqjhed2m9r4xq0y8fvz7zqvgem") + + signature := func(message string) []byte { + signature, err := crypto.Sign(crypto.Keccak256([]byte(message)), privateKey) + require.NoError(t, err) + return signature + } + + tests := []struct { + name string + address string + hash string + signature string + wantErr bool + wantErrContains string + }{ + { + name: "pass - verify success", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: false, + }, + { + name: "fail - not address of the signature", + address: marker.ReplaceAbleAddress("evm13zqksjwyjdvtzqjhed2m9r4xq0y8fvz79xjsqd"), + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "mis-match signature with provided address:", + }, + { + name: "fail - signature of another message", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x" + hex.EncodeToString(signature("another")), + wantErr: true, + wantErrContains: "mis-match signature with provided address:", + }, + { + name: "fail - bad address", + address: "", + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "account is not a valid bech32 account address", + }, + { + name: "fail - bad hash, missing 0x prefix", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String()[2:], + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "hash must starts with 0x", + }, + { + name: "fail - bad hash", + address: addressBech32, + hash: "0x1", + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "hash must be 32 bytes lowercase:", + }, + { + name: "fail - hash is not lowercase", + address: addressBech32, + hash: "0x" + strings.ToUpper(common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String()[2:]), + signature: "0x" + hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "hash must be 32 bytes lowercase:", + }, + { + name: "fail - bad signature, missing 0x prefix", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: hex.EncodeToString(signature(MessageToSign)), + wantErr: true, + wantErrContains: "signature must starts with 0x", + }, + { + name: "fail - bad signature", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x1", + wantErr: true, + wantErrContains: "bad signature:", + }, + { + name: "fail - signature is not lowercase", + address: addressBech32, + hash: common.BytesToHash(crypto.Keccak256([]byte(MessageToSign))).String(), + signature: "0x" + strings.ToUpper(hex.EncodeToString(signature(MessageToSign))), + wantErr: true, + wantErrContains: "signature must be lowercase:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ProofExternalOwnedAccount{ + Account: tt.address, + Hash: tt.hash, + Signature: tt.signature, + } + + err := m.ValidateBasic() + if tt.wantErr { + require.ErrorContains(t, err, tt.wantErrContains) + return + } + + require.NoError(t, err) + }) + } +} + +func init() { + sdk.GetConfig().SetBech32PrefixForAccount(constants.Bech32PrefixAccAddr, "") +} diff --git a/x/vauth/types/query.pb.go b/x/vauth/types/query.pb.go new file mode 100644 index 0000000000..c7f6f8809d --- /dev/null +++ b/x/vauth/types/query.pb.go @@ -0,0 +1,592 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: evermint/vauth/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryProofExternalOwnedAccountRequest is the request type for the Query/ProofExternalOwnedAccount RPC method. +type QueryProofExternalOwnedAccountRequest struct { + // account to query for + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (m *QueryProofExternalOwnedAccountRequest) Reset() { *m = QueryProofExternalOwnedAccountRequest{} } +func (m *QueryProofExternalOwnedAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProofExternalOwnedAccountRequest) ProtoMessage() {} +func (*QueryProofExternalOwnedAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_466659dc6fde2434, []int{0} +} +func (m *QueryProofExternalOwnedAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProofExternalOwnedAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProofExternalOwnedAccountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProofExternalOwnedAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProofExternalOwnedAccountRequest.Merge(m, src) +} +func (m *QueryProofExternalOwnedAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProofExternalOwnedAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProofExternalOwnedAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProofExternalOwnedAccountRequest proto.InternalMessageInfo + +func (m *QueryProofExternalOwnedAccountRequest) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +// QueryProofExternalOwnedAccountResponse is the response type for the Query/ProofExternalOwnedAccount RPC method. +type QueryProofExternalOwnedAccountResponse struct { + // proof is the proof account is EOA + Proof ProofExternalOwnedAccount `protobuf:"bytes,1,opt,name=proof,proto3" json:"proof"` +} + +func (m *QueryProofExternalOwnedAccountResponse) Reset() { + *m = QueryProofExternalOwnedAccountResponse{} +} +func (m *QueryProofExternalOwnedAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProofExternalOwnedAccountResponse) ProtoMessage() {} +func (*QueryProofExternalOwnedAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_466659dc6fde2434, []int{1} +} +func (m *QueryProofExternalOwnedAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProofExternalOwnedAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProofExternalOwnedAccountResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProofExternalOwnedAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProofExternalOwnedAccountResponse.Merge(m, src) +} +func (m *QueryProofExternalOwnedAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProofExternalOwnedAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProofExternalOwnedAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProofExternalOwnedAccountResponse proto.InternalMessageInfo + +func (m *QueryProofExternalOwnedAccountResponse) GetProof() ProofExternalOwnedAccount { + if m != nil { + return m.Proof + } + return ProofExternalOwnedAccount{} +} + +func init() { + proto.RegisterType((*QueryProofExternalOwnedAccountRequest)(nil), "evermint.vauth.v1.QueryProofExternalOwnedAccountRequest") + proto.RegisterType((*QueryProofExternalOwnedAccountResponse)(nil), "evermint.vauth.v1.QueryProofExternalOwnedAccountResponse") +} + +func init() { proto.RegisterFile("evermint/vauth/v1/query.proto", fileDescriptor_466659dc6fde2434) } + +var fileDescriptor_466659dc6fde2434 = []byte{ + // 327 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4d, 0x2d, 0x4b, 0x2d, + 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x4b, 0x2c, 0x2d, 0xc9, 0xd0, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, + 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0x49, 0xeb, 0x81, 0xa5, + 0xf5, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, + 0x94, 0x4c, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x7e, 0x62, 0x41, 0xa6, 0x7e, 0x62, 0x5e, 0x5e, + 0x7e, 0x49, 0x62, 0x49, 0x66, 0x7e, 0x5e, 0x31, 0x54, 0x16, 0x8b, 0x2d, 0x10, 0xf3, 0xc0, 0xd2, + 0x4a, 0x8e, 0x5c, 0xaa, 0x81, 0x20, 0x4b, 0x03, 0x8a, 0xf2, 0xf3, 0xd3, 0x5c, 0x2b, 0x4a, 0x52, + 0x8b, 0xf2, 0x12, 0x73, 0xfc, 0xcb, 0xf3, 0x52, 0x53, 0x1c, 0x93, 0x93, 0xf3, 0x4b, 0xf3, 0x4a, + 0x82, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x13, 0x21, 0x22, 0x12, 0x8c, + 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x30, 0xae, 0x52, 0x11, 0x97, 0x1a, 0x21, 0x23, 0x8a, 0x0b, 0xf2, + 0xf3, 0x8a, 0x53, 0x85, 0x3c, 0xb8, 0x58, 0x0b, 0x40, 0x8a, 0xc0, 0x26, 0x70, 0x1b, 0xe9, 0xe8, + 0x61, 0x78, 0x51, 0x0f, 0xa7, 0x21, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0x41, 0x0c, 0x30, + 0xba, 0xcc, 0xc8, 0xc5, 0x0a, 0xb6, 0x54, 0xe8, 0x24, 0x23, 0x97, 0x24, 0x4e, 0x4d, 0x42, 0x16, + 0x58, 0xac, 0x20, 0xca, 0xbf, 0x52, 0x96, 0x64, 0xe8, 0x84, 0x78, 0x53, 0xc9, 0xbc, 0xe9, 0xf2, + 0x93, 0xc9, 0x4c, 0x86, 0x42, 0xfa, 0xfa, 0x98, 0x61, 0x0f, 0x76, 0x7e, 0x7c, 0x2a, 0x54, 0x7b, + 0x7c, 0x3e, 0x48, 0x7f, 0x3c, 0x34, 0x24, 0x9d, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, + 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, + 0x58, 0x8e, 0x21, 0x4a, 0x37, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xdf, + 0xb5, 0x38, 0x39, 0x31, 0xcf, 0xc9, 0x15, 0xc9, 0x70, 0x43, 0x23, 0xfd, 0x0a, 0xa8, 0x15, 0x25, + 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0xe0, 0xc8, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xdf, + 0x98, 0xd4, 0x6c, 0x63, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // ProofExternalOwnedAccount returns proof of external owned account (EOA) + ProofExternalOwnedAccount(ctx context.Context, in *QueryProofExternalOwnedAccountRequest, opts ...grpc.CallOption) (*QueryProofExternalOwnedAccountResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) ProofExternalOwnedAccount(ctx context.Context, in *QueryProofExternalOwnedAccountRequest, opts ...grpc.CallOption) (*QueryProofExternalOwnedAccountResponse, error) { + out := new(QueryProofExternalOwnedAccountResponse) + err := c.cc.Invoke(ctx, "/evermint.vauth.v1.Query/ProofExternalOwnedAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // ProofExternalOwnedAccount returns proof of external owned account (EOA) + ProofExternalOwnedAccount(context.Context, *QueryProofExternalOwnedAccountRequest) (*QueryProofExternalOwnedAccountResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) ProofExternalOwnedAccount(ctx context.Context, req *QueryProofExternalOwnedAccountRequest) (*QueryProofExternalOwnedAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProofExternalOwnedAccount not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_ProofExternalOwnedAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProofExternalOwnedAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ProofExternalOwnedAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/evermint.vauth.v1.Query/ProofExternalOwnedAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ProofExternalOwnedAccount(ctx, req.(*QueryProofExternalOwnedAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "evermint.vauth.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ProofExternalOwnedAccount", + Handler: _Query_ProofExternalOwnedAccount_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "evermint/vauth/v1/query.proto", +} + +func (m *QueryProofExternalOwnedAccountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProofExternalOwnedAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProofExternalOwnedAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryProofExternalOwnedAccountResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProofExternalOwnedAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProofExternalOwnedAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryProofExternalOwnedAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Account) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryProofExternalOwnedAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Proof.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryProofExternalOwnedAccountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProofExternalOwnedAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProofExternalOwnedAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Account = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProofExternalOwnedAccountResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProofExternalOwnedAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProofExternalOwnedAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/vauth/types/query.pb.gw.go b/x/vauth/types/query.pb.gw.go new file mode 100644 index 0000000000..c985dcd040 --- /dev/null +++ b/x/vauth/types/query.pb.gw.go @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: evermint/vauth/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +var ( + filter_Query_ProofExternalOwnedAccount_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ProofExternalOwnedAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProofExternalOwnedAccountRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ProofExternalOwnedAccount_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ProofExternalOwnedAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ProofExternalOwnedAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProofExternalOwnedAccountRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ProofExternalOwnedAccount_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ProofExternalOwnedAccount(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_ProofExternalOwnedAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ProofExternalOwnedAccount_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProofExternalOwnedAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_ProofExternalOwnedAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ProofExternalOwnedAccount_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ProofExternalOwnedAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_ProofExternalOwnedAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"evermint", "vauth", "v1", "proof_external_owned_account"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_ProofExternalOwnedAccount_0 = runtime.ForwardResponseMessage +) diff --git a/x/vauth/types/tx.pb.go b/x/vauth/types/tx.pb.go new file mode 100644 index 0000000000..5cb8c36113 --- /dev/null +++ b/x/vauth/types/tx.pb.go @@ -0,0 +1,648 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: evermint/vauth/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgSubmitProofExternalOwnedAccount defines a Msg to submit EOA proof +type MsgSubmitProofExternalOwnedAccount struct { + // submitter is the cosmos bech32 address of the account who submitted the proof + Submitter string `protobuf:"bytes,1,opt,name=submitter,proto3" json:"submitter,omitempty"` + // account is the cosmos bech32 address that submit proof for + Account string `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` + // signature is the Ethereum signed message, used to proof that the account is EOA + Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (m *MsgSubmitProofExternalOwnedAccount) Reset() { *m = MsgSubmitProofExternalOwnedAccount{} } +func (m *MsgSubmitProofExternalOwnedAccount) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitProofExternalOwnedAccount) ProtoMessage() {} +func (*MsgSubmitProofExternalOwnedAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_74b474a4acdc4171, []int{0} +} +func (m *MsgSubmitProofExternalOwnedAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitProofExternalOwnedAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitProofExternalOwnedAccount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSubmitProofExternalOwnedAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitProofExternalOwnedAccount.Merge(m, src) +} +func (m *MsgSubmitProofExternalOwnedAccount) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitProofExternalOwnedAccount) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitProofExternalOwnedAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitProofExternalOwnedAccount proto.InternalMessageInfo + +func (m *MsgSubmitProofExternalOwnedAccount) GetSubmitter() string { + if m != nil { + return m.Submitter + } + return "" +} + +func (m *MsgSubmitProofExternalOwnedAccount) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +func (m *MsgSubmitProofExternalOwnedAccount) GetSignature() string { + if m != nil { + return m.Signature + } + return "" +} + +// MsgSubmitProofExternalOwnedAccountResponse returns no fields +type MsgSubmitProofExternalOwnedAccountResponse struct { +} + +func (m *MsgSubmitProofExternalOwnedAccountResponse) Reset() { + *m = MsgSubmitProofExternalOwnedAccountResponse{} +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) String() string { + return proto.CompactTextString(m) +} +func (*MsgSubmitProofExternalOwnedAccountResponse) ProtoMessage() {} +func (*MsgSubmitProofExternalOwnedAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74b474a4acdc4171, []int{1} +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitProofExternalOwnedAccountResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitProofExternalOwnedAccountResponse.Merge(m, src) +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitProofExternalOwnedAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitProofExternalOwnedAccountResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSubmitProofExternalOwnedAccount)(nil), "evermint.vauth.v1.MsgSubmitProofExternalOwnedAccount") + proto.RegisterType((*MsgSubmitProofExternalOwnedAccountResponse)(nil), "evermint.vauth.v1.MsgSubmitProofExternalOwnedAccountResponse") +} + +func init() { proto.RegisterFile("evermint/vauth/v1/tx.proto", fileDescriptor_74b474a4acdc4171) } + +var fileDescriptor_74b474a4acdc4171 = []byte{ + // 361 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x31, 0x4b, 0xc3, 0x40, + 0x14, 0xc7, 0x7b, 0x2d, 0x28, 0xcd, 0x20, 0x18, 0x04, 0x63, 0x28, 0x51, 0x32, 0x49, 0xd1, 0x1c, + 0xad, 0x38, 0xa8, 0x38, 0xb4, 0x50, 0x9c, 0x8a, 0x52, 0x37, 0x97, 0x70, 0x8d, 0xd7, 0x6b, 0xa0, + 0xb9, 0x17, 0x72, 0x97, 0x58, 0xd7, 0x8e, 0x4e, 0x82, 0x5f, 0xc4, 0x8f, 0xe1, 0x58, 0x70, 0x11, + 0x27, 0x49, 0x05, 0xbf, 0x86, 0x24, 0x97, 0x50, 0x41, 0xa1, 0xe0, 0x76, 0xef, 0xfd, 0xde, 0xfb, + 0xbf, 0x77, 0xef, 0xaf, 0x99, 0x34, 0xa1, 0x51, 0xe0, 0x73, 0x89, 0x13, 0x12, 0xcb, 0x31, 0x4e, + 0x5a, 0x58, 0x4e, 0x9d, 0x30, 0x02, 0x09, 0xfa, 0x66, 0xc9, 0x9c, 0x9c, 0x39, 0x49, 0xcb, 0xdc, + 0x62, 0xc0, 0x20, 0xa7, 0x38, 0x7b, 0xa9, 0x42, 0xb3, 0xc1, 0x00, 0xd8, 0x84, 0x62, 0x12, 0xfa, + 0x98, 0x70, 0x0e, 0x92, 0x48, 0x1f, 0xb8, 0x28, 0xe8, 0xb6, 0x07, 0x22, 0x00, 0x81, 0x03, 0xc1, + 0x32, 0xf9, 0x40, 0xb0, 0x02, 0xec, 0x28, 0xe0, 0x2a, 0x3d, 0x15, 0x28, 0x64, 0x3f, 0x20, 0xcd, + 0xee, 0x0b, 0x76, 0x1d, 0x0f, 0x03, 0x5f, 0x5e, 0x45, 0x00, 0xa3, 0xde, 0x54, 0xd2, 0x88, 0x93, + 0xc9, 0xe5, 0x1d, 0xa7, 0xb7, 0x1d, 0xcf, 0x83, 0x98, 0x4b, 0xbd, 0xa1, 0xd5, 0x45, 0x5e, 0x22, + 0x69, 0x64, 0xa0, 0x3d, 0xb4, 0x5f, 0x1f, 0x2c, 0x13, 0xba, 0xa1, 0xad, 0x13, 0x55, 0x68, 0x54, + 0x73, 0x56, 0x86, 0x79, 0x9f, 0xcf, 0x38, 0x91, 0x71, 0x44, 0x8d, 0x5a, 0xd1, 0x57, 0x26, 0x4e, + 0x37, 0x66, 0x5f, 0xcf, 0xcd, 0xa5, 0x8e, 0x7d, 0xa0, 0x35, 0x57, 0xef, 0x32, 0xa0, 0x22, 0x04, + 0x2e, 0x68, 0x3b, 0x45, 0x5a, 0xad, 0x2f, 0x98, 0xfe, 0x8e, 0xb4, 0xdd, 0x55, 0xfb, 0x1f, 0x3b, + 0xbf, 0x4e, 0xec, 0xac, 0x1e, 0x65, 0x9e, 0xff, 0xab, 0xad, 0xdc, 0xd0, 0xee, 0xcc, 0x5e, 0x3f, + 0x9f, 0xaa, 0x67, 0xfa, 0x09, 0xfe, 0xcb, 0x7c, 0xac, 0x3e, 0x9e, 0xb9, 0x02, 0x23, 0x97, 0x16, + 0x42, 0x2e, 0x64, 0x4a, 0x6e, 0x71, 0xc0, 0xee, 0xc5, 0x4b, 0x6a, 0xa1, 0x79, 0x6a, 0xa1, 0x8f, + 0xd4, 0x42, 0x8f, 0x0b, 0xab, 0x32, 0x5f, 0x58, 0x95, 0xb7, 0x85, 0x55, 0xb9, 0x39, 0x64, 0xbe, + 0x1c, 0xc7, 0x43, 0xc7, 0x83, 0x00, 0xf7, 0x84, 0x47, 0x78, 0xb7, 0xf7, 0x63, 0x4c, 0xab, 0x8d, + 0xa7, 0xc5, 0x30, 0x79, 0x1f, 0x52, 0x31, 0x5c, 0xcb, 0xfd, 0x3e, 0xfa, 0x0e, 0x00, 0x00, 0xff, + 0xff, 0xc1, 0xe7, 0x51, 0x55, 0x88, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // SubmitProofExternalOwnedAccount submit proof that an account is external owned account (EOA) + SubmitProofExternalOwnedAccount(ctx context.Context, in *MsgSubmitProofExternalOwnedAccount, opts ...grpc.CallOption) (*MsgSubmitProofExternalOwnedAccountResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SubmitProofExternalOwnedAccount(ctx context.Context, in *MsgSubmitProofExternalOwnedAccount, opts ...grpc.CallOption) (*MsgSubmitProofExternalOwnedAccountResponse, error) { + out := new(MsgSubmitProofExternalOwnedAccountResponse) + err := c.cc.Invoke(ctx, "/evermint.vauth.v1.Msg/SubmitProofExternalOwnedAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // SubmitProofExternalOwnedAccount submit proof that an account is external owned account (EOA) + SubmitProofExternalOwnedAccount(context.Context, *MsgSubmitProofExternalOwnedAccount) (*MsgSubmitProofExternalOwnedAccountResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SubmitProofExternalOwnedAccount(ctx context.Context, req *MsgSubmitProofExternalOwnedAccount) (*MsgSubmitProofExternalOwnedAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitProofExternalOwnedAccount not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SubmitProofExternalOwnedAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSubmitProofExternalOwnedAccount) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SubmitProofExternalOwnedAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/evermint.vauth.v1.Msg/SubmitProofExternalOwnedAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SubmitProofExternalOwnedAccount(ctx, req.(*MsgSubmitProofExternalOwnedAccount)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "evermint.vauth.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SubmitProofExternalOwnedAccount", + Handler: _Msg_SubmitProofExternalOwnedAccount_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "evermint/vauth/v1/tx.proto", +} + +func (m *MsgSubmitProofExternalOwnedAccount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSubmitProofExternalOwnedAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitProofExternalOwnedAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x1a + } + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintTx(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0x12 + } + if len(m.Submitter) > 0 { + i -= len(m.Submitter) + copy(dAtA[i:], m.Submitter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Submitter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSubmitProofExternalOwnedAccountResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSubmitProofExternalOwnedAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitProofExternalOwnedAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgSubmitProofExternalOwnedAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Submitter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Account) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitProofExternalOwnedAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgSubmitProofExternalOwnedAccount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitProofExternalOwnedAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitProofExternalOwnedAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Submitter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Submitter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Account = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSubmitProofExternalOwnedAccountResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitProofExternalOwnedAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitProofExternalOwnedAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/vauth/types/tx.pb.gw.go b/x/vauth/types/tx.pb.gw.go new file mode 100644 index 0000000000..adc297c902 --- /dev/null +++ b/x/vauth/types/tx.pb.gw.go @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: evermint/vauth/v1/tx.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +var ( + filter_Msg_SubmitProofExternalOwnedAccount_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Msg_SubmitProofExternalOwnedAccount_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgSubmitProofExternalOwnedAccount + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SubmitProofExternalOwnedAccount_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.SubmitProofExternalOwnedAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Msg_SubmitProofExternalOwnedAccount_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgSubmitProofExternalOwnedAccount + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_SubmitProofExternalOwnedAccount_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.SubmitProofExternalOwnedAccount(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux". +// UnaryRPC :call MsgServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead. +func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error { + + mux.Handle("GET", pattern_Msg_SubmitProofExternalOwnedAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Msg_SubmitProofExternalOwnedAccount_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_SubmitProofExternalOwnedAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterMsgHandler(ctx, mux, conn) +} + +// RegisterMsgHandler registers the http handlers for service Msg to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn)) +} + +// RegisterMsgHandlerClient registers the http handlers for service Msg +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "MsgClient" to call the correct interceptors. +func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error { + + mux.Handle("GET", pattern_Msg_SubmitProofExternalOwnedAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Msg_SubmitProofExternalOwnedAccount_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_SubmitProofExternalOwnedAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Msg_SubmitProofExternalOwnedAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"evermint", "vauth", "v1", "tx", "submit_proof_external_owned_account"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Msg_SubmitProofExternalOwnedAccount_0 = runtime.ForwardResponseMessage +) diff --git a/x/vauth/types/vauth.pb.go b/x/vauth/types/vauth.pb.go new file mode 100644 index 0000000000..e21adc67db --- /dev/null +++ b/x/vauth/types/vauth.pb.go @@ -0,0 +1,425 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: evermint/vauth/v1/vauth.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ProofExternalOwnedAccount store the proof that account is external owned account (EOA) +type ProofExternalOwnedAccount struct { + // account is the cosmos bech32 address of the account that has proof + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + // hash is the keccak256 of the message that was signed on + Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` + // signature is the signed message using private key + Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (m *ProofExternalOwnedAccount) Reset() { *m = ProofExternalOwnedAccount{} } +func (m *ProofExternalOwnedAccount) String() string { return proto.CompactTextString(m) } +func (*ProofExternalOwnedAccount) ProtoMessage() {} +func (*ProofExternalOwnedAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_9389809a2e1cd610, []int{0} +} +func (m *ProofExternalOwnedAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProofExternalOwnedAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProofExternalOwnedAccount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProofExternalOwnedAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProofExternalOwnedAccount.Merge(m, src) +} +func (m *ProofExternalOwnedAccount) XXX_Size() int { + return m.Size() +} +func (m *ProofExternalOwnedAccount) XXX_DiscardUnknown() { + xxx_messageInfo_ProofExternalOwnedAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_ProofExternalOwnedAccount proto.InternalMessageInfo + +func (m *ProofExternalOwnedAccount) GetAccount() string { + if m != nil { + return m.Account + } + return "" +} + +func (m *ProofExternalOwnedAccount) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +func (m *ProofExternalOwnedAccount) GetSignature() string { + if m != nil { + return m.Signature + } + return "" +} + +func init() { + proto.RegisterType((*ProofExternalOwnedAccount)(nil), "evermint.vauth.v1.ProofExternalOwnedAccount") +} + +func init() { proto.RegisterFile("evermint/vauth/v1/vauth.proto", fileDescriptor_9389809a2e1cd610) } + +var fileDescriptor_9389809a2e1cd610 = []byte{ + // 216 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4d, 0x2d, 0x4b, 0x2d, + 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x4b, 0x2c, 0x2d, 0xc9, 0xd0, 0x2f, 0x33, 0x84, 0x30, 0xf4, + 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x61, 0xd2, 0x7a, 0x10, 0xd1, 0x32, 0x43, 0x29, 0x91, + 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0xac, 0x3e, 0x88, 0x05, 0x51, 0xa8, 0x94, 0xce, 0x25, 0x19, 0x50, + 0x94, 0x9f, 0x9f, 0xe6, 0x5a, 0x51, 0x92, 0x5a, 0x94, 0x97, 0x98, 0xe3, 0x5f, 0x9e, 0x97, 0x9a, + 0xe2, 0x98, 0x9c, 0x9c, 0x5f, 0x9a, 0x57, 0x22, 0x24, 0xc1, 0xc5, 0x9e, 0x08, 0x61, 0x4a, 0x30, + 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x42, 0x42, 0x5c, 0x2c, 0x19, 0x89, 0xc5, 0x19, 0x12, + 0x4c, 0x60, 0x61, 0x30, 0x5b, 0x48, 0x86, 0x8b, 0xb3, 0x38, 0x33, 0x3d, 0x2f, 0xb1, 0xa4, 0xb4, + 0x28, 0x55, 0x82, 0x19, 0x2c, 0x81, 0x10, 0x70, 0x72, 0x3f, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, + 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, + 0x63, 0x39, 0x86, 0x28, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, + 0xd7, 0xe2, 0xe4, 0xc4, 0x3c, 0x27, 0x57, 0x7d, 0x84, 0xef, 0x0c, 0x8d, 0xf4, 0x2b, 0xa0, 0x7e, + 0x2c, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, 0x3b, 0xdc, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, + 0xca, 0x39, 0x32, 0x9d, 0x02, 0x01, 0x00, 0x00, +} + +func (m *ProofExternalOwnedAccount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProofExternalOwnedAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProofExternalOwnedAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintVauth(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x1a + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintVauth(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x12 + } + if len(m.Account) > 0 { + i -= len(m.Account) + copy(dAtA[i:], m.Account) + i = encodeVarintVauth(dAtA, i, uint64(len(m.Account))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintVauth(dAtA []byte, offset int, v uint64) int { + offset -= sovVauth(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ProofExternalOwnedAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Account) + if l > 0 { + n += 1 + l + sovVauth(uint64(l)) + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovVauth(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovVauth(uint64(l)) + } + return n +} + +func sovVauth(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozVauth(x uint64) (n int) { + return sovVauth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ProofExternalOwnedAccount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVauth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProofExternalOwnedAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProofExternalOwnedAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVauth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthVauth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVauth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Account = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVauth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthVauth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVauth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVauth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthVauth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVauth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipVauth(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVauth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipVauth(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVauth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVauth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVauth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthVauth + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupVauth + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthVauth + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthVauth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowVauth = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupVauth = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/vauth/utils/verification.go b/x/vauth/utils/verification.go new file mode 100644 index 0000000000..a8716138c2 --- /dev/null +++ b/x/vauth/utils/verification.go @@ -0,0 +1,25 @@ +package utils + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func VerifySignature(address common.Address, signature []byte, message string) (bool, error) { + if len(signature) == 0 { + panic("signature cannot be empty") + } + if message == "" { + panic("message cannot be empty") + } + messageHash := crypto.Keccak256([]byte(message)) + + sigPublicKey, err := crypto.Ecrecover(messageHash, signature) + if err != nil { + return false, err + } + + buf := crypto.Keccak256(sigPublicKey[1:]) + publicAddress := common.BytesToAddress(buf[12:]) + return address == publicAddress, nil +} diff --git a/x/vauth/utils/verification_test.go b/x/vauth/utils/verification_test.go new file mode 100644 index 0000000000..4c529bd923 --- /dev/null +++ b/x/vauth/utils/verification_test.go @@ -0,0 +1,60 @@ +package utils + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +//goland:noinspection ALL +func TestVerifySignature(t *testing.T) { + privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") + require.NoError(t, err) + + address := common.HexToAddress("0x96216849c49358B10257cb55b28eA603c874b05E") + + signature := func(message string) []byte { + signature, err := crypto.Sign(crypto.Keccak256([]byte(message)), privateKey) + require.NoError(t, err) + return signature + } + + tests := []struct { + name string + address common.Address + signature []byte + message string + want bool + }{ + { + name: "verified", + address: address, + signature: signature("hello"), + message: "hello", + want: true, + }, + { + name: "mis-match message", + address: address, + signature: signature("hello"), + message: "world", + want: false, + }, + { + name: "mis-match address", + address: common.HexToAddress("0x88816849c49358B10257cb55b28eA603c874b05E"), + signature: signature("hello"), + message: "hello", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := VerifySignature(tt.address, tt.signature, tt.message) + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +}