diff --git a/UPGRADING.md b/UPGRADING.md index f6f2a5289b4a..d63a335803bf 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -421,6 +421,23 @@ if err != nil { } ``` +##### TX Decoder + +In order to support x/accounts properly we need to init a `TxDecoder`, modify your `app.go`: + +```diff +import ( ++ txdecode "cosmossdk.io/x/tx/decode" +) ++ txDecoder, err := txdecode.NewDecoder(txdecode.Options{ ++ SigningContext: signingCtx, ++ ProtoCodec: appCodec, ++ }) ++ if err != nil { ++ panic(err) ++ } +``` + #### `x/crisis` The `x/crisis` module was removed due to it not being supported or functional any longer. diff --git a/simapp/app.go b/simapp/app.go index 0ed9271f5a0f..67cd8534b620 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -77,6 +77,7 @@ import ( "cosmossdk.io/x/staking" stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtypes "cosmossdk.io/x/staking/types" + txdecode "cosmossdk.io/x/tx/decode" "cosmossdk.io/x/tx/signing" "cosmossdk.io/x/upgrade" upgradekeeper "cosmossdk.io/x/upgrade/keeper" @@ -215,6 +216,13 @@ func NewSimApp( appCodec := codec.NewProtoCodec(interfaceRegistry) legacyAmino := codec.NewLegacyAmino() signingCtx := interfaceRegistry.SigningContext() + txDecoder, err := txdecode.NewDecoder(txdecode.Options{ + SigningContext: signingCtx, + ProtoCodec: appCodec, + }) + if err != nil { + panic(err) + } txConfig := authtx.NewTxConfig(appCodec, signingCtx.AddressCodec(), signingCtx.ValidatorAddressCodec(), authtx.DefaultSignModes) govModuleAddr, err := signingCtx.AddressCodec().BytesToString(authtypes.NewModuleAddress(govtypes.ModuleName)) @@ -306,6 +314,7 @@ func NewSimApp( runtime.NewEnvironment(runtime.NewKVStoreService(keys[accounts.StoreKey]), logger.With(log.ModuleKey, "x/accounts"), runtime.EnvWithMsgRouterService(app.MsgServiceRouter()), runtime.EnvWithQueryRouterService(app.GRPCQueryRouter())), signingCtx.AddressCodec(), appCodec.InterfaceRegistry(), + txDecoder, // TESTING: do not add accountstd.AddAccount("counter", counter.NewAccount), accountstd.AddAccount("aa_minimal", account_abstraction.NewMinimalAbstractedAccount), diff --git a/simapp/go.mod b/simapp/go.mod index f3c8bd373bd5..1bd8afd790b0 100644 --- a/simapp/go.mod +++ b/simapp/go.mod @@ -51,8 +51,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/auth v0.5.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect diff --git a/simapp/go.sum b/simapp/go.sum index 05225f6c175c..da40ce9e321a 100644 --- a/simapp/go.sum +++ b/simapp/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= diff --git a/tests/e2e/accounts/account_abstraction_test.go b/tests/e2e/accounts/account_abstraction_test.go deleted file mode 100644 index a7d5f98d6786..000000000000 --- a/tests/e2e/accounts/account_abstraction_test.go +++ /dev/null @@ -1,103 +0,0 @@ -//go:build app_v1 - -package accounts - -import ( - "context" - "testing" - - "cosmossdk.io/simapp" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" - gogoproto "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/require" -) - -var ( - privKey = secp256k1.GenPrivKey() - accCreator = []byte("creator") - bundlerAddr = secp256k1.GenPrivKey().PubKey().Address() - aliceAddr = secp256k1.GenPrivKey().PubKey().Address() -) - -/* -func TestAccountAbstraction(t *testing.T) { - app := setupApp(t) - ak := app.AccountsKeeper - ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()) - - _, aaAddr, err := ak.Init(ctx, "aa_minimal", accCreator, &rotationv1.MsgInit{ - PubKeyBytes: privKey.PubKey().Bytes(), - }, nil) - require.NoError(t, err) - - _, aaFullAddr, err := ak.Init(ctx, "aa_full", accCreator, &rotationv1.MsgInit{ - PubKeyBytes: privKey.PubKey().Bytes(), - }, nil) - require.NoError(t, err) - - aaAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(aaAddr) - require.NoError(t, err) - - aaFullAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(aaFullAddr) - require.NoError(t, err) - - // let's give aa some coins. - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, aaAddr, sdk.NewCoins(sdk.NewInt64Coin("stake", 100000000000)))) - require.NoError(t, testutil.FundAccount(ctx, app.BankKeeper, aaFullAddr, sdk.NewCoins(sdk.NewInt64Coin("stake", 100000000000)))) - - bundlerAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(bundlerAddr) - require.NoError(t, err) - - aliceAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(aliceAddr) - require.NoError(t, err) - - t.Run("ok - pay bundler not implemented", func(t *testing.T) {}) - t.Run("pay bundle impersonation", func(t *testing.T) {}) - t.Run("auth failure", func(t *testing.T) {}) - t.Run("pay bundle failure", func(t *testing.T) {}) - t.Run("exec message failure", func(t *testing.T) {}) - - t.Run("implements bundler payment - fail ", func(t *testing.T) {}) - - t.Run("implements execution - fail", func(t *testing.T) {}) - - t.Run("implements bundler payment and execution - success", func(t *testing.T) {}) - - t.Run("Simulate - OK", func(t *testing.T) {}) - - t.Run("Simulate - Fail empty user operation", func(t *testing.T) {}) -} -*/ - -func intoAny(t *testing.T, msgs ...gogoproto.Message) (anys []*codectypes.Any) { - t.Helper() - for _, msg := range msgs { - any, err := codectypes.NewAnyWithValue(msg) - require.NoError(t, err) - anys = append(anys, any) - } - return -} - -func coins(t *testing.T, s string) sdk.Coins { - t.Helper() - coins, err := sdk.ParseCoinsNormalized(s) - require.NoError(t, err) - return coins -} - -func balanceIs(t *testing.T, ctx context.Context, app *simapp.SimApp, addr sdk.AccAddress, s string) { - t.Helper() - balance := app.BankKeeper.GetAllBalances(ctx, addr) - require.Equal(t, s, balance.String()) -} - -var mockSignature = &codectypes.Any{TypeUrl: "signature", Value: []byte("signature")} - -func setupApp(t *testing.T) *simapp.SimApp { - t.Helper() - app := simapp.Setup(t, false) - return app -} diff --git a/tests/go.mod b/tests/go.mod index a266bac67c65..5fab8aee9c26 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -53,8 +53,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/auth v0.5.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect diff --git a/tests/go.sum b/tests/go.sum index 7688e1229977..a301e368a486 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= diff --git a/tests/integration/accounts/bundler_test.go b/tests/integration/accounts/bundler_test.go new file mode 100644 index 000000000000..1b94ddd78fa1 --- /dev/null +++ b/tests/integration/accounts/bundler_test.go @@ -0,0 +1,261 @@ +package accounts + +import ( + "context" + "fmt" + "testing" + + gogoproto "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + account_abstractionv1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1" + banktypes "cosmossdk.io/x/bank/types" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +func TestMsgServer_ExecuteBundle(t *testing.T) { + t.Run("bundle success", func(t *testing.T) { + f := initFixture(t, func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error) { + return &account_abstractionv1.MsgAuthenticateResponse{}, nil + }) + + recipient := f.mustAddr([]byte("recipient")) + feeAmt := sdk.NewInt64Coin("atom", 100) + sendAmt := sdk.NewInt64Coin("atom", 200) + + f.mint(f.mockAccountAddress, feeAmt, sendAmt) + + tx := makeTx(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: recipient, + Amount: sdk.NewCoins(sendAmt), + }, []byte("pass"), &account_abstractionv1.TxExtension{ + AuthenticationGasLimit: 2400, + BundlerPaymentMessages: []*codectypes.Any{wrapAny(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: f.bundler, + Amount: sdk.NewCoins(feeAmt), + })}, + BundlerPaymentGasLimit: 30000, + ExecutionGasLimit: 30000, + }) + + bundleResp := f.runBundle(tx) + require.Len(t, bundleResp.Responses, 1) + + txResp := bundleResp.Responses[0] + + require.Empty(t, txResp.Error) + require.NotZero(t, txResp.AuthenticationGasUsed) + require.NotZero(t, txResp.BundlerPaymentGasUsed) + require.NotZero(t, txResp.ExecutionGasUsed) + + // asses responses + require.Len(t, txResp.BundlerPaymentResponses, 1) + require.Equal(t, txResp.BundlerPaymentResponses[0].TypeUrl, "/cosmos.bank.v1beta1.MsgSendResponse") + + require.Len(t, txResp.ExecutionResponses, 1) + require.Equal(t, txResp.ExecutionResponses[0].TypeUrl, "/cosmos.bank.v1beta1.MsgSendResponse") + + // ensure sends have happened + require.Equal(t, f.balance(f.bundler, feeAmt.Denom), feeAmt) + require.Equal(t, f.balance(recipient, sendAmt.Denom), sendAmt) + }) + + t.Run("tx fails at auth step", func(t *testing.T) { + f := initFixture(t, func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error) { + return &account_abstractionv1.MsgAuthenticateResponse{}, fmt.Errorf("sentinel") + }) + recipient := f.mustAddr([]byte("recipient")) + feeAmt := sdk.NewInt64Coin("atom", 100) + sendAmt := sdk.NewInt64Coin("atom", 200) + f.mint(f.mockAccountAddress, feeAmt, sendAmt) + + tx := makeTx(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: recipient, + Amount: sdk.NewCoins(sendAmt), + }, []byte("pass"), &account_abstractionv1.TxExtension{ + AuthenticationGasLimit: 2400, + BundlerPaymentMessages: []*codectypes.Any{wrapAny(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: f.bundler, + Amount: sdk.NewCoins(feeAmt), + })}, + BundlerPaymentGasLimit: 30000, + ExecutionGasLimit: 30000, + }) + + bundleResp := f.runBundle(tx) + + require.Len(t, bundleResp.Responses, 1) + + txResp := bundleResp.Responses[0] + require.NotEmpty(t, txResp.Error) + require.Contains(t, txResp.Error, "sentinel") + require.NotZero(t, txResp.AuthenticationGasUsed) + require.Zero(t, txResp.BundlerPaymentGasUsed) + require.Zero(t, txResp.ExecutionGasUsed) + require.Empty(t, txResp.BundlerPaymentResponses) + require.Empty(t, txResp.ExecutionResponses) + + // ensure auth side effects are not persisted in case of failures + }) + + t.Run("tx fails at pay bundler step", func(t *testing.T) { + f := initFixture(t, func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error) { + return &account_abstractionv1.MsgAuthenticateResponse{}, nil + }) + + recipient := f.mustAddr([]byte("recipient")) + feeAmt := sdk.NewInt64Coin("atom", 100) + sendAmt := sdk.NewInt64Coin("atom", 200) + + f.mint(f.mockAccountAddress, feeAmt, sendAmt) + + tx := makeTx(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: recipient, + Amount: sdk.NewCoins(sendAmt), + }, []byte("pass"), &account_abstractionv1.TxExtension{ + AuthenticationGasLimit: 2400, + BundlerPaymentMessages: []*codectypes.Any{ + wrapAny(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: f.bundler, + Amount: sdk.NewCoins(feeAmt.AddAmount(feeAmt.Amount.AddRaw(100))), + }), + wrapAny(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: f.bundler, + Amount: sdk.NewCoins(feeAmt.AddAmount(feeAmt.Amount.AddRaw(30000))), + }), + }, + BundlerPaymentGasLimit: 30000, + ExecutionGasLimit: 30000, + }) + + bundleResp := f.runBundle(tx) + require.Len(t, bundleResp.Responses, 1) + + txResp := bundleResp.Responses[0] + + require.NotEmpty(t, txResp.Error) + require.Contains(t, txResp.Error, "bundler payment failed") + require.NotZero(t, txResp.AuthenticationGasUsed) + require.NotZero(t, txResp.BundlerPaymentGasUsed) + + require.Empty(t, txResp.BundlerPaymentResponses) + require.Zero(t, txResp.ExecutionGasUsed) + require.Empty(t, txResp.ExecutionResponses) + + // ensure bundler payment side effects are not persisted + require.True(t, f.balance(f.bundler, feeAmt.Denom).IsZero()) + }) + + t.Run("tx fails at execution step", func(t *testing.T) { + f := initFixture(t, func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error) { + return &account_abstractionv1.MsgAuthenticateResponse{}, nil + }) + + recipient := f.mustAddr([]byte("recipient")) + feeAmt := sdk.NewInt64Coin("atom", 100) + sendAmt := sdk.NewInt64Coin("atom", 40000) // this fails + + f.mint(f.mockAccountAddress, feeAmt) + + tx := makeTx(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: recipient, + Amount: sdk.NewCoins(sendAmt), + }, []byte("pass"), &account_abstractionv1.TxExtension{ + AuthenticationGasLimit: 2400, + BundlerPaymentMessages: []*codectypes.Any{ + wrapAny(t, &banktypes.MsgSend{ + FromAddress: f.mustAddr(f.mockAccountAddress), + ToAddress: f.bundler, + Amount: sdk.NewCoins(feeAmt), + }), + }, + BundlerPaymentGasLimit: 30000, + ExecutionGasLimit: 30000, + }) + + bundleResp := f.runBundle(tx) + require.Len(t, bundleResp.Responses, 1) + + txResp := bundleResp.Responses[0] + + require.NotEmpty(t, txResp.Error) + require.Contains(t, txResp.Error, "execution failed") + + require.NotZero(t, txResp.AuthenticationGasUsed) + + require.NotZero(t, txResp.BundlerPaymentGasUsed) + require.NotEmpty(t, txResp.BundlerPaymentResponses) + require.Equal(t, f.balance(f.bundler, feeAmt.Denom), feeAmt) // ensure bundler payment side effects are persisted + + require.NotZero(t, txResp.ExecutionGasUsed) + require.Empty(t, txResp.ExecutionResponses) + + // ensure execution side effects are not persisted + // aka recipient must not have money + require.True(t, f.balance(recipient, feeAmt.Denom).IsZero()) + }) +} + +func makeTx(t *testing.T, msg gogoproto.Message, sig []byte, xt *account_abstractionv1.TxExtension) []byte { + anyMsg, err := codectypes.NewAnyWithValue(msg) + require.NoError(t, err) + + anyXt, err := codectypes.NewAnyWithValue(xt) + require.NoError(t, err) + + tx := &txtypes.Tx{ + Body: &txtypes.TxBody{ + Messages: []*codectypes.Any{anyMsg}, + Memo: "", + TimeoutHeight: 0, + Unordered: false, + TimeoutTimestamp: nil, + ExtensionOptions: []*codectypes.Any{anyXt}, + NonCriticalExtensionOptions: nil, + }, + AuthInfo: &txtypes.AuthInfo{ + SignerInfos: []*txtypes.SignerInfo{ + { + PublicKey: nil, + ModeInfo: &txtypes.ModeInfo{Sum: &txtypes.ModeInfo_Single_{Single: &txtypes.ModeInfo_Single{Mode: signingtypes.SignMode_SIGN_MODE_UNSPECIFIED}}}, + Sequence: 0, + }, + }, + Fee: nil, + }, + Signatures: [][]byte{sig}, + } + + bodyBytes, err := tx.Body.Marshal() + require.NoError(t, err) + + authInfoBytes, err := tx.AuthInfo.Marshal() + require.NoError(t, err) + + txRaw, err := (&txtypes.TxRaw{ + BodyBytes: bodyBytes, + AuthInfoBytes: authInfoBytes, + Signatures: tx.Signatures, + }).Marshal() + require.NoError(t, err) + return txRaw +} + +func wrapAny(t *testing.T, msg gogoproto.Message) *codectypes.Any { + t.Helper() + any, err := codectypes.NewAnyWithValue(msg) + require.NoError(t, err) + return any +} diff --git a/tests/integration/accounts/fixture_test.go b/tests/integration/accounts/fixture_test.go new file mode 100644 index 000000000000..63c301bbf4df --- /dev/null +++ b/tests/integration/accounts/fixture_test.go @@ -0,0 +1,213 @@ +package accounts + +import ( + "context" + "testing" + + gogotypes "github.com/cosmos/gogoproto/types" + "github.com/stretchr/testify/require" + + "cosmossdk.io/core/appmodule" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/accounts" + "cosmossdk.io/x/accounts/accountstd" + account_abstractionv1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1" + accountsv1 "cosmossdk.io/x/accounts/v1" + "cosmossdk.io/x/bank" + bankkeeper "cosmossdk.io/x/bank/keeper" + banktypes "cosmossdk.io/x/bank/types" + minttypes "cosmossdk.io/x/mint/types" + txdecode "cosmossdk.io/x/tx/decode" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/integration" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var _ accountstd.Interface = (*mockAccount)(nil) + +type mockAccount struct { + authenticate func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error) +} + +func (m mockAccount) RegisterInitHandler(builder *accountstd.InitBuilder) { + accountstd.RegisterInitHandler(builder, func(ctx context.Context, req *gogotypes.Empty) (*gogotypes.Empty, error) { + return &gogotypes.Empty{}, nil + }) +} + +func (m mockAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) { + if m.authenticate == nil { + return + } + + accountstd.RegisterExecuteHandler(builder, m.authenticate) +} + +func (m mockAccount) RegisterQueryHandlers(_ *accountstd.QueryBuilder) {} + +type fixture struct { + t *testing.T + + app *integration.App + + cdc codec.Codec + ctx sdk.Context + + authKeeper authkeeper.AccountKeeper + accountsKeeper accounts.Keeper + bankKeeper bankkeeper.Keeper + + mockAccountAddress []byte + bundler string +} + +func (f fixture) mustAddr(address []byte) string { + s, _ := f.authKeeper.AddressCodec().BytesToString(address) + return s +} + +func (f fixture) runBundle(txBytes ...[]byte) *accountsv1.MsgExecuteBundleResponse { + f.t.Helper() + + msgSrv := accounts.NewMsgServer(f.accountsKeeper) + + resp, err := msgSrv.ExecuteBundle(f.ctx, &accountsv1.MsgExecuteBundle{ + Bundler: f.bundler, + Txs: txBytes, + }) + require.NoError(f.t, err) + return resp +} + +func (f fixture) mint(address []byte, coins ...sdk.Coin) { + f.t.Helper() + for _, coin := range coins { + err := f.bankKeeper.MintCoins(f.ctx, minttypes.ModuleName, sdk.NewCoins(coin)) + require.NoError(f.t, err) + err = f.bankKeeper.SendCoinsFromModuleToAccount(f.ctx, minttypes.ModuleName, address, sdk.NewCoins(coin)) + require.NoError(f.t, err) + } +} + +func (f fixture) balance(recipient, denom string) sdk.Coin { + f.t.Helper() + balances, err := f.bankKeeper.Balance(f.ctx, &banktypes.QueryBalanceRequest{ + Address: recipient, + Denom: denom, + }) + require.NoError(f.t, err) + return *balances.Balance +} + +func initFixture(t *testing.T, f func(ctx context.Context, msg *account_abstractionv1.MsgAuthenticate) (*account_abstractionv1.MsgAuthenticateResponse, error)) *fixture { + t.Helper() + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, accounts.StoreKey, + ) + encodingCfg := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, auth.AppModule{}, bank.AppModule{}, accounts.AppModule{}) + cdc := encodingCfg.Codec + + logger := log.NewTestLogger(t) + cms := integration.CreateMultiStore(keys, logger) + + newCtx := sdk.NewContext(cms, true, logger) + + router := baseapp.NewMsgServiceRouter() + queryRouter := baseapp.NewGRPCQueryRouter() + + txDecoder, err := txdecode.NewDecoder(txdecode.Options{ + SigningContext: encodingCfg.TxConfig.SigningContext(), + ProtoCodec: encodingCfg.Codec, + }) + require.NoError(t, err) + + accountsKeeper, err := accounts.NewKeeper( + cdc, + runtime.NewEnvironment(runtime.NewKVStoreService(keys[accounts.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(router)), + addresscodec.NewBech32Codec("cosmos"), + cdc.InterfaceRegistry(), + txDecoder, + accountstd.AddAccount("mock", func(deps accountstd.Dependencies) (accountstd.Interface, error) { + return mockAccount{f}, nil + }), + ) + require.NoError(t, err) + accountsv1.RegisterQueryServer(queryRouter, accounts.NewQueryServer(accountsKeeper)) + + authority := authtypes.NewModuleAddress("gov") + + authKeeper := authkeeper.NewAccountKeeper( + runtime.NewEnvironment(runtime.NewKVStoreService(keys[authtypes.StoreKey]), log.NewNopLogger()), + cdc, + authtypes.ProtoBaseAccount, + accountsKeeper, + map[string][]string{minttypes.ModuleName: {authtypes.Minter}}, + addresscodec.NewBech32Codec(sdk.Bech32MainPrefix), + sdk.Bech32MainPrefix, + authority.String(), + ) + + blockedAddresses := map[string]bool{ + authKeeper.GetAuthority(): false, + } + bankKeeper := bankkeeper.NewBaseKeeper( + runtime.NewEnvironment(runtime.NewKVStoreService(keys[banktypes.StoreKey]), log.NewNopLogger()), + cdc, + authKeeper, + blockedAddresses, + authority.String(), + ) + + params := banktypes.DefaultParams() + require.NoError(t, bankKeeper.SetParams(newCtx, params)) + + accountsModule := accounts.NewAppModule(cdc, accountsKeeper) + authModule := auth.NewAppModule(cdc, authKeeper, accountsKeeper, authsims.RandomGenesisAccounts, nil) + bankModule := bank.NewAppModule(cdc, bankKeeper, authKeeper) + + integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, + encodingCfg.InterfaceRegistry.SigningContext().AddressCodec(), + encodingCfg.InterfaceRegistry.SigningContext().ValidatorAddressCodec(), + map[string]appmodule.AppModule{ + accounts.ModuleName: accountsModule, + authtypes.ModuleName: authModule, + banktypes.ModuleName: bankModule, + }, router, queryRouter) + + authtypes.RegisterInterfaces(cdc.InterfaceRegistry()) + banktypes.RegisterInterfaces(cdc.InterfaceRegistry()) + + authtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), authkeeper.NewMsgServerImpl(authKeeper)) + authtypes.RegisterQueryServer(integrationApp.QueryHelper(), authkeeper.NewQueryServer(authKeeper)) + + banktypes.RegisterMsgServer(router, bankkeeper.NewMsgServerImpl(bankKeeper)) + + // init account + _, addr, err := accountsKeeper.Init(newCtx, "mock", []byte("system"), &gogotypes.Empty{}, nil) + require.NoError(t, err) + + fixture := &fixture{ + t: t, + app: integrationApp, + cdc: cdc, + ctx: newCtx, + authKeeper: authKeeper, + accountsKeeper: accountsKeeper, + bankKeeper: bankKeeper, + mockAccountAddress: addr, + bundler: "", + } + fixture.bundler = fixture.mustAddr([]byte("bundler")) + return fixture +} diff --git a/tests/integration/auth/keeper/fixture_test.go b/tests/integration/auth/keeper/fixture_test.go index 45514c0cdba7..f8a2d10ba871 100644 --- a/tests/integration/auth/keeper/fixture_test.go +++ b/tests/integration/auth/keeper/fixture_test.go @@ -79,6 +79,7 @@ func initFixture(t *testing.T, extraAccs map[string]accountstd.Interface) *fixtu runtime.NewEnvironment(runtime.NewKVStoreService(keys[accounts.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(router)), addresscodec.NewBech32Codec("cosmos"), cdc.InterfaceRegistry(), + nil, append(accs, account)..., ) assert.NilError(t, err) diff --git a/x/accounts/README.md b/x/accounts/README.md index 16d38c552c2c..de2c6717fb89 100644 --- a/x/accounts/README.md +++ b/x/accounts/README.md @@ -539,4 +539,131 @@ For example, given the following `genesis.json` file: } ``` -The accounts module will run the lockup account initialization message. \ No newline at end of file +The accounts module will run the lockup account initialization message. + +## Bundling + +Transaction bundling enables a designated account (the bundler) to submit transactions on behalf of multiple users. This approach offers several advantages: + +1. Fee Abstraction: The bundler assumes responsibility for transaction fees, simplifying the process for end-users. +2. Flexible Fee Arrangements: Users and bundlers can negotiate fee structures off-chain, allowing for customized payment models. +3. Improved User Experience: By abstracting away fee complexities, bundling can make blockchain interactions more accessible to a wider audience. +4. Potential for Optimization: Bundlers can potentially optimize gas usage and reduce overall transaction costs. + +```mermaid +graph TD + A1[User 1] -->|Send Tx| B[Bundler] + A2[User 2] -->|Send Tx| B + A3[User 3] -->|Send Tx| B + B -->|Package Txs into MsgExecuteBundle| C[MsgExecuteBundle] + C -->|Submit| D[x/accounts module] + D -->|Execute Tx 1| E1[Execute independently] + D -->|Execute Tx 2| E2[Execute independently] + D -->|Execute Tx 3| E3[Execute independently] +``` + +### Tx Extension + +For a transaction to be processed by a bundler, it must include a `TxExtension`. This extension is defined in the [interface.proto](./proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto) file. + +```protobuf +// TxExtension is the extension option that AA's add to txs when they're bundled. +message TxExtension { + // authentication_gas_limit expresses the gas limit to be used for the authentication part of the + // bundled tx. + uint64 authentication_gas_limit = 1; + // bundler_payment_messages expresses a list of messages that the account + // executes to pay the bundler for submitting the bundled tx. + // It can be empty if the bundler does not need any form of payment, + // the handshake for submitting the UserOperation might have happened off-chain. + // Bundlers and accounts are free to use any form of payment, in fact the payment can + // either be empty or be expressed as: + // - NFT payment + // - IBC Token payment. + // - Payment through delegations. + repeated google.protobuf.Any bundler_payment_messages = 2; + // bundler_payment_gas_limit defines the gas limit to be used for the bundler payment. + // This ensures that, since the bundler executes a list of bundled tx and there needs to + // be minimal trust between bundler and the tx sender, the sender cannot consume + // the whole bundle gas. + uint64 bundler_payment_gas_limit = 3; + // execution_gas_limit defines the gas limit to be used for the execution of the UserOperation's + // execution messages. + uint64 execution_gas_limit = 4; +} +``` + +The purpose of the TxExtension is to provide crucial information for the bundler to process and execute the transaction efficiently and securely. It allows for fine-grained control over gas limits for different parts of the transaction execution and facilitates flexible payment arrangements between the user and the bundler. +Field explanations: + +1. **authentication_gas_limit (uint64)**: + +Specifies the maximum amount of gas that can be used for authenticating the bundled transaction. +Ensures that the authentication process doesn't consume excessive resources. + +2. **bundler_payment_messages (repeated google.protobuf.Any)**: + +Contains a list of messages defining how the account will pay the bundler for submitting the transaction. +Offers flexibility in payment methods, including NFTs, IBC tokens, or delegations. +Can be empty if payment arrangements are made off-chain or if the bundler doesn't require payment. + +3. **bundler_payment_gas_limit (uint64)**: + +Sets the maximum gas that can be used for processing the bundler payment. +Prevents a malicious sender from consuming all the gas allocated for the entire bundle, enhancing security in the bundling process. + +4. **execution_gas_limit (uint64)**: + +Defines the maximum gas allowed for executing the actual transaction messages (UserOperation). +Helps in accurately estimating and controlling the resources needed for the main transaction execution. + +### Compatibility of Your Chain with Bundling + +#### Important Considerations + +Bundling introduces a bypass mechanism for ante handler checks. This has significant implications for chains that rely on ante handlers for: + +* Message validation +* Admission control logic + +If your chain heavily depends on these ante handler functionalities, enabling bundling may compromise your chain's security or operational logic. + +#### Disabling Bundling + +For chains where bundling is incompatible with existing security measures or operational requirements, you can disable this feature. To do so: + +1. Locate your `app.go` file +2. Add the following method call: + +**Non depinject**: + +```go +// add keepers +func NewApp(...) { + ... + accountsKeeper, err := accounts.NewKeeper(...) + if err != nil { + panic(err) + } + accountsKeeper.DisableTxBundling() <-- // add this line + app.AccountsKeeper = accountsKeeper + ... +} +``` + +**Depinject**: + +```go + var appModules map[string]appmodule.AppModule + if err := depinject.Inject(appConfig, + &appBuilder, + ... + &app.AuthKeeper, + &app.AccountsKeeper, + ... + ); err != nil { + panic(err) + } + + app.AccountsKeeper.DisableBundling() // <- add this line +``` diff --git a/x/accounts/defaults/base/go.mod b/x/accounts/defaults/base/go.mod index 3d00ae413b8c..9e417cb5d598 100644 --- a/x/accounts/defaults/base/go.mod +++ b/x/accounts/defaults/base/go.mod @@ -18,8 +18,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // indirect; main cosmossdk.io/depinject v1.0.0 cosmossdk.io/errors v1.0.1 // indirect diff --git a/x/accounts/defaults/base/go.sum b/x/accounts/defaults/base/go.sum index f55e3012b0d7..a873e1623716 100644 --- a/x/accounts/defaults/base/go.sum +++ b/x/accounts/defaults/base/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= diff --git a/x/accounts/defaults/lockup/go.mod b/x/accounts/defaults/lockup/go.mod index 119d7a3bcb28..fd182bd1c986 100644 --- a/x/accounts/defaults/lockup/go.mod +++ b/x/accounts/defaults/lockup/go.mod @@ -14,8 +14,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cosmossdk.io/api v0.8.0 // indirect cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // indirect; main cosmossdk.io/depinject v1.0.0 // indirect diff --git a/x/accounts/defaults/lockup/go.sum b/x/accounts/defaults/lockup/go.sum index 55f675b10e42..580d83ed3929 100644 --- a/x/accounts/defaults/lockup/go.sum +++ b/x/accounts/defaults/lockup/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= diff --git a/x/accounts/defaults/multisig/go.mod b/x/accounts/defaults/multisig/go.mod index c4b81d975981..17eda3ab3579 100644 --- a/x/accounts/defaults/multisig/go.mod +++ b/x/accounts/defaults/multisig/go.mod @@ -16,8 +16,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cosmossdk.io/api v0.8.0 // indirect cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // indirect; main cosmossdk.io/depinject v1.0.0 // indirect diff --git a/x/accounts/defaults/multisig/go.sum b/x/accounts/defaults/multisig/go.sum index f55e3012b0d7..a873e1623716 100644 --- a/x/accounts/defaults/multisig/go.sum +++ b/x/accounts/defaults/multisig/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= diff --git a/x/accounts/depinject.go b/x/accounts/depinject.go index 4bdcfdc6479c..494d51a0cc89 100644 --- a/x/accounts/depinject.go +++ b/x/accounts/depinject.go @@ -7,6 +7,7 @@ import ( "cosmossdk.io/depinject" "cosmossdk.io/depinject/appconfig" "cosmossdk.io/x/accounts/accountstd" + txdecode "cosmossdk.io/x/tx/decode" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -48,8 +49,16 @@ func ProvideModule(in ModuleInputs) ModuleOutputs { accCreators[i] = acc.MakeAccount } + txDec, err := txdecode.NewDecoder(txdecode.Options{ + SigningContext: in.Registry.SigningContext(), + ProtoCodec: in.Cdc, + }) + if err != nil { + panic(err) + } + accountsKeeper, err := NewKeeper( - in.Cdc, in.Environment, in.AddressCodec, in.Registry, + in.Cdc, in.Environment, in.AddressCodec, in.Registry, txDec, accCreators..., ) if err != nil { diff --git a/x/accounts/errors.go b/x/accounts/errors.go new file mode 100644 index 000000000000..c0a55741aee2 --- /dev/null +++ b/x/accounts/errors.go @@ -0,0 +1,13 @@ +package accounts + +import "cosmossdk.io/errors" + +var ( + ErrAASemantics = errors.New(ModuleName, 0, "invalid account abstraction tx semantics") + // ErrAuthentication is returned when the authentication fails. + ErrAuthentication = errors.New(ModuleName, 1, "authentication failed") + // ErrBundlerPayment is returned when the bundler payment fails. + ErrBundlerPayment = errors.New(ModuleName, 2, "bundler payment failed") + // ErrExecution is returned when the execution fails. + ErrExecution = errors.New(ModuleName, 3, "execution failed") +) diff --git a/x/accounts/go.mod b/x/accounts/go.mod index 727a3bce072c..3680ffa25dd2 100644 --- a/x/accounts/go.mod +++ b/x/accounts/go.mod @@ -19,9 +19,9 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect - cosmossdk.io/errors v1.0.1 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect + cosmossdk.io/errors v1.0.1 cosmossdk.io/log v1.4.1 // indirect cosmossdk.io/math v1.3.0 cosmossdk.io/schema v0.3.1-0.20240930054013-7c6e0388a3f9 // indirect @@ -57,6 +57,7 @@ require ( github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/danieljoos/wincred v1.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/badger/v4 v4.3.0 // indirect github.com/dgraph-io/ristretto v1.0.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -166,8 +167,6 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -require github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - replace github.com/cosmos/cosmos-sdk => ../../. // TODO remove post spinning out all modules diff --git a/x/accounts/go.sum b/x/accounts/go.sum index 7cb1307533fa..dc763daddd07 100644 --- a/x/accounts/go.sum +++ b/x/accounts/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= diff --git a/x/accounts/interfaces/account_abstraction/v1/interface.pb.go b/x/accounts/interfaces/account_abstraction/v1/interface.pb.go index 3e6e784b2171..c75de916ee62 100644 --- a/x/accounts/interfaces/account_abstraction/v1/interface.pb.go +++ b/x/accounts/interfaces/account_abstraction/v1/interface.pb.go @@ -7,6 +7,7 @@ import ( fmt "fmt" tx "github.com/cosmos/cosmos-sdk/types/tx" proto "github.com/cosmos/gogoproto/proto" + any "github.com/cosmos/gogoproto/types/any" io "io" math "math" math_bits "math/bits" @@ -224,11 +225,98 @@ func (m *QueryAuthenticationMethodsResponse) GetAuthenticationMethods() []string return nil } +// TxExtension is the extension option that AA's add to txs when they're bundled. +type TxExtension struct { + // authentication_gas_limit expresses the gas limit to be used for the authentication part of the + // bundled tx. + AuthenticationGasLimit uint64 `protobuf:"varint,1,opt,name=authentication_gas_limit,json=authenticationGasLimit,proto3" json:"authentication_gas_limit,omitempty"` + // bundler_payment_messages expresses a list of messages that the account + // executes to pay the bundler for submitting the bundled tx. + // It can be empty if the bundler does not need any form of payment, + // the handshake for submitting the UserOperation might have happened off-chain. + // Bundlers and accounts are free to use any form of payment, in fact the payment can + // either be empty or be expressed as: + // - NFT payment + // - IBC Token payment. + // - Payment through delegations. + BundlerPaymentMessages []*any.Any `protobuf:"bytes,2,rep,name=bundler_payment_messages,json=bundlerPaymentMessages,proto3" json:"bundler_payment_messages,omitempty"` + // bundler_payment_gas_limit defines the gas limit to be used for the bundler payment. + // This ensures that, since the bundler executes a list of bundled tx and there needs to + // be minimal trust between bundler and the tx sender, the sender cannot consume + // the whole bundle gas. + BundlerPaymentGasLimit uint64 `protobuf:"varint,3,opt,name=bundler_payment_gas_limit,json=bundlerPaymentGasLimit,proto3" json:"bundler_payment_gas_limit,omitempty"` + // execution_gas_limit defines the gas limit to be used for the execution of the UserOperation's + // execution messages. + ExecutionGasLimit uint64 `protobuf:"varint,4,opt,name=execution_gas_limit,json=executionGasLimit,proto3" json:"execution_gas_limit,omitempty"` +} + +func (m *TxExtension) Reset() { *m = TxExtension{} } +func (m *TxExtension) String() string { return proto.CompactTextString(m) } +func (*TxExtension) ProtoMessage() {} +func (*TxExtension) Descriptor() ([]byte, []int) { + return fileDescriptor_56b360422260e9d1, []int{4} +} +func (m *TxExtension) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxExtension.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 *TxExtension) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxExtension.Merge(m, src) +} +func (m *TxExtension) XXX_Size() int { + return m.Size() +} +func (m *TxExtension) XXX_DiscardUnknown() { + xxx_messageInfo_TxExtension.DiscardUnknown(m) +} + +var xxx_messageInfo_TxExtension proto.InternalMessageInfo + +func (m *TxExtension) GetAuthenticationGasLimit() uint64 { + if m != nil { + return m.AuthenticationGasLimit + } + return 0 +} + +func (m *TxExtension) GetBundlerPaymentMessages() []*any.Any { + if m != nil { + return m.BundlerPaymentMessages + } + return nil +} + +func (m *TxExtension) GetBundlerPaymentGasLimit() uint64 { + if m != nil { + return m.BundlerPaymentGasLimit + } + return 0 +} + +func (m *TxExtension) GetExecutionGasLimit() uint64 { + if m != nil { + return m.ExecutionGasLimit + } + return 0 +} + func init() { proto.RegisterType((*MsgAuthenticate)(nil), "cosmos.accounts.interfaces.account_abstraction.v1.MsgAuthenticate") proto.RegisterType((*MsgAuthenticateResponse)(nil), "cosmos.accounts.interfaces.account_abstraction.v1.MsgAuthenticateResponse") proto.RegisterType((*QueryAuthenticationMethods)(nil), "cosmos.accounts.interfaces.account_abstraction.v1.QueryAuthenticationMethods") proto.RegisterType((*QueryAuthenticationMethodsResponse)(nil), "cosmos.accounts.interfaces.account_abstraction.v1.QueryAuthenticationMethodsResponse") + proto.RegisterType((*TxExtension)(nil), "cosmos.accounts.interfaces.account_abstraction.v1.TxExtension") } func init() { @@ -236,29 +324,37 @@ func init() { } var fileDescriptor_56b360422260e9d1 = []byte{ - // 338 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xb1, 0x4e, 0xeb, 0x30, - 0x14, 0x86, 0xeb, 0xf6, 0xde, 0xa2, 0xba, 0x20, 0xa4, 0x48, 0x85, 0x50, 0xa1, 0x28, 0x44, 0x42, - 0xca, 0x64, 0x2b, 0x20, 0x06, 0xc6, 0xb2, 0x31, 0x74, 0x20, 0x74, 0x82, 0x21, 0x72, 0x12, 0xd3, - 0x5a, 0x50, 0xbb, 0xb2, 0x4f, 0x5a, 0xf3, 0x16, 0x3c, 0x05, 0xcf, 0xc2, 0xd8, 0x91, 0x11, 0xb5, - 0x2f, 0x82, 0x68, 0x5a, 0x0a, 0xa8, 0x0c, 0x8c, 0xfe, 0xcf, 0xe7, 0xcf, 0xc7, 0xfa, 0x71, 0x27, - 0x53, 0x66, 0xa8, 0x0c, 0x65, 0x59, 0xa6, 0x0a, 0x09, 0x86, 0x0a, 0x09, 0x5c, 0xdf, 0xb1, 0x8c, - 0x7f, 0x66, 0x09, 0x4b, 0x0d, 0x68, 0x96, 0x81, 0x50, 0x92, 0x8e, 0xa3, 0x35, 0x41, 0x46, 0x5a, - 0x81, 0x72, 0xa2, 0x52, 0x41, 0x56, 0x0a, 0xb2, 0x56, 0x90, 0x0d, 0x0a, 0x32, 0x8e, 0xda, 0xed, - 0xe5, 0xab, 0x60, 0xe9, 0x38, 0x4a, 0x39, 0xb0, 0x88, 0x82, 0x2d, 0x75, 0xc1, 0x33, 0xc2, 0xbb, - 0x5d, 0xd3, 0xef, 0x14, 0x30, 0xe0, 0x12, 0x44, 0xc6, 0x80, 0x3b, 0x2e, 0xde, 0x4a, 0x0b, 0x99, - 0x3f, 0x70, 0xed, 0x22, 0x1f, 0x85, 0x8d, 0x78, 0x75, 0x74, 0x28, 0xae, 0x6b, 0x36, 0x49, 0xc0, - 0xba, 0x55, 0x1f, 0x85, 0xcd, 0x13, 0x97, 0x2c, 0xb7, 0x01, 0x4b, 0x96, 0x6a, 0xd2, 0xb3, 0x31, - 0x9b, 0xc4, 0xff, 0x35, 0x9b, 0xf4, 0xac, 0x73, 0x8c, 0xab, 0x60, 0xdd, 0xda, 0x02, 0x6e, 0x6d, - 0x86, 0xab, 0x60, 0x9d, 0x23, 0xbc, 0x6d, 0x44, 0x5f, 0x72, 0x9d, 0x08, 0x99, 0x73, 0xeb, 0xfe, - 0xf3, 0x51, 0xb8, 0x13, 0x37, 0xcb, 0xec, 0xf2, 0x23, 0x0a, 0x0e, 0xf0, 0xfe, 0x8f, 0x3d, 0x63, - 0x6e, 0x46, 0x4a, 0x1a, 0x1e, 0x1c, 0xe2, 0xf6, 0x55, 0xc1, 0xf5, 0xe3, 0x97, 0xa1, 0x50, 0xb2, - 0xcb, 0x61, 0xa0, 0x72, 0x13, 0xdc, 0xe2, 0xe0, 0xf7, 0xe9, 0xca, 0xe1, 0x9c, 0xe1, 0x3d, 0xf6, - 0x0d, 0x48, 0x86, 0x25, 0xe1, 0x22, 0xbf, 0x16, 0x36, 0xe2, 0x16, 0xdb, 0x74, 0xfd, 0xe2, 0xfa, - 0x65, 0xe6, 0xa1, 0xe9, 0xcc, 0x43, 0x6f, 0x33, 0x0f, 0x3d, 0xcd, 0xbd, 0xca, 0x74, 0xee, 0x55, - 0x5e, 0xe7, 0x5e, 0xe5, 0xe6, 0xbc, 0xfc, 0xac, 0xc9, 0xef, 0x89, 0x50, 0xd4, 0xfe, 0xa1, 0xf2, - 0xb4, 0xbe, 0xa8, 0xe6, 0xf4, 0x3d, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x1c, 0x3d, 0x0e, 0x2e, 0x02, - 0x00, 0x00, + // 467 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x4f, 0x8b, 0xd3, 0x40, + 0x18, 0xc6, 0x9b, 0xb6, 0xae, 0xec, 0x54, 0x11, 0xa3, 0xbb, 0x4e, 0x8b, 0x84, 0x5a, 0x10, 0x72, + 0x9a, 0x90, 0x15, 0xc1, 0x3d, 0x56, 0x10, 0x11, 0xac, 0x68, 0xec, 0x49, 0x0f, 0x61, 0x92, 0xbe, + 0x9b, 0x1d, 0x6c, 0x66, 0x4a, 0xe6, 0x4d, 0x3b, 0xbd, 0xfa, 0x09, 0xfc, 0x14, 0x7e, 0x16, 0x8f, + 0x7b, 0xf4, 0x28, 0xed, 0x17, 0x91, 0xcd, 0x9f, 0xd6, 0x0d, 0xf5, 0xb0, 0xc7, 0xbc, 0xcf, 0xf3, + 0xfc, 0xf2, 0xe4, 0x7d, 0x09, 0x19, 0xc7, 0x4a, 0xa7, 0x4a, 0x7b, 0x3c, 0x8e, 0x55, 0x2e, 0x51, + 0x7b, 0x42, 0x22, 0x64, 0x17, 0x3c, 0x86, 0xdd, 0x2c, 0xe4, 0x91, 0xc6, 0x8c, 0xc7, 0x28, 0x94, + 0xf4, 0x96, 0xfe, 0xde, 0xc1, 0x16, 0x99, 0x42, 0x65, 0xfb, 0x25, 0x82, 0xd5, 0x08, 0xb6, 0x47, + 0xb0, 0x03, 0x08, 0xb6, 0xf4, 0x07, 0xfd, 0x44, 0xa9, 0x64, 0x0e, 0x5e, 0x01, 0x88, 0xf2, 0x0b, + 0x8f, 0xcb, 0x75, 0x49, 0x1b, 0x0c, 0xaa, 0x42, 0x68, 0xbc, 0xa5, 0x1f, 0x01, 0x72, 0xdf, 0x43, + 0x53, 0x6a, 0xa3, 0x9f, 0x16, 0x79, 0x30, 0xd1, 0xc9, 0x38, 0xc7, 0x4b, 0x90, 0x28, 0x62, 0x8e, + 0x60, 0x53, 0x72, 0x37, 0xca, 0xe5, 0x6c, 0x0e, 0x19, 0xb5, 0x86, 0x96, 0x7b, 0x1c, 0xd4, 0x8f, + 0xb6, 0x47, 0x8e, 0x32, 0xbe, 0x0a, 0xd1, 0xd0, 0xf6, 0xd0, 0x72, 0x7b, 0x67, 0x94, 0x55, 0x45, + 0xd1, 0xb0, 0x0a, 0xcd, 0xa6, 0x26, 0xe0, 0xab, 0xe0, 0x4e, 0xc6, 0x57, 0x53, 0x63, 0x3f, 0x27, + 0x6d, 0x34, 0xb4, 0x53, 0x98, 0x4f, 0x0e, 0x9b, 0xdb, 0x68, 0xec, 0x67, 0xe4, 0x9e, 0x16, 0x89, + 0x84, 0x2c, 0x14, 0x72, 0x06, 0x86, 0x76, 0x87, 0x96, 0x7b, 0x3f, 0xe8, 0x95, 0xb3, 0x77, 0xd7, + 0xa3, 0x51, 0x9f, 0x3c, 0x69, 0xf4, 0x0c, 0x40, 0x2f, 0x94, 0xd4, 0x30, 0x7a, 0x4a, 0x06, 0x9f, + 0x72, 0xc8, 0xd6, 0xff, 0x88, 0x42, 0xc9, 0x09, 0xe0, 0xa5, 0x9a, 0xe9, 0xd1, 0x57, 0x32, 0xfa, + 0xbf, 0x5a, 0x33, 0xec, 0x97, 0xe4, 0x94, 0xdf, 0x30, 0x84, 0x69, 0xe9, 0xa0, 0xd6, 0xb0, 0xe3, + 0x1e, 0x07, 0x27, 0xfc, 0x20, 0xfc, 0x7b, 0x9b, 0xf4, 0xa6, 0xe6, 0x8d, 0x41, 0x90, 0x5a, 0x28, + 0x69, 0xbf, 0x22, 0xb4, 0x81, 0x49, 0xb8, 0x0e, 0xe7, 0x22, 0x15, 0x58, 0xec, 0xb2, 0x1b, 0x34, + 0x5e, 0xf3, 0x96, 0xeb, 0xf7, 0xd7, 0xaa, 0xfd, 0x81, 0xd0, 0x6a, 0xcb, 0xe1, 0x82, 0xaf, 0x53, + 0x90, 0x18, 0xa6, 0xa0, 0x35, 0x4f, 0x40, 0xd3, 0xf6, 0xb0, 0xe3, 0xf6, 0xce, 0x1e, 0xb3, 0xf2, + 0xc4, 0xac, 0x3e, 0x31, 0x1b, 0xcb, 0x75, 0x70, 0x5a, 0xa5, 0x3e, 0x96, 0xa1, 0x49, 0x95, 0xb1, + 0xcf, 0x49, 0xbf, 0xc9, 0xdb, 0x57, 0xe9, 0x94, 0x55, 0x6e, 0x46, 0x77, 0x55, 0x18, 0x79, 0x04, + 0x06, 0xe2, 0xbc, 0xd1, 0xbf, 0x5b, 0x84, 0x1e, 0xee, 0xa4, 0xda, 0xff, 0xfa, 0xf3, 0xaf, 0x8d, + 0x63, 0x5d, 0x6d, 0x1c, 0xeb, 0xcf, 0xc6, 0xb1, 0x7e, 0x6c, 0x9d, 0xd6, 0xd5, 0xd6, 0x69, 0xfd, + 0xde, 0x3a, 0xad, 0x2f, 0xe7, 0xe5, 0xc5, 0xf5, 0xec, 0x1b, 0x13, 0xca, 0x33, 0xb7, 0xf8, 0x25, + 0xa2, 0xa3, 0xe2, 0x2b, 0x5f, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x24, 0x94, 0x3c, 0x65, 0x4e, + 0x03, 0x00, 0x00, } func (m *MsgAuthenticate) Marshal() (dAtA []byte, err error) { @@ -398,6 +494,58 @@ func (m *QueryAuthenticationMethodsResponse) MarshalToSizedBuffer(dAtA []byte) ( return len(dAtA) - i, nil } +func (m *TxExtension) 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 *TxExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ExecutionGasLimit != 0 { + i = encodeVarintInterface(dAtA, i, uint64(m.ExecutionGasLimit)) + i-- + dAtA[i] = 0x20 + } + if m.BundlerPaymentGasLimit != 0 { + i = encodeVarintInterface(dAtA, i, uint64(m.BundlerPaymentGasLimit)) + i-- + dAtA[i] = 0x18 + } + if len(m.BundlerPaymentMessages) > 0 { + for iNdEx := len(m.BundlerPaymentMessages) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BundlerPaymentMessages[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintInterface(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.AuthenticationGasLimit != 0 { + i = encodeVarintInterface(dAtA, i, uint64(m.AuthenticationGasLimit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintInterface(dAtA []byte, offset int, v uint64) int { offset -= sovInterface(v) base := offset @@ -466,6 +614,30 @@ func (m *QueryAuthenticationMethodsResponse) Size() (n int) { return n } +func (m *TxExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthenticationGasLimit != 0 { + n += 1 + sovInterface(uint64(m.AuthenticationGasLimit)) + } + if len(m.BundlerPaymentMessages) > 0 { + for _, e := range m.BundlerPaymentMessages { + l = e.Size() + n += 1 + l + sovInterface(uint64(l)) + } + } + if m.BundlerPaymentGasLimit != 0 { + n += 1 + sovInterface(uint64(m.BundlerPaymentGasLimit)) + } + if m.ExecutionGasLimit != 0 { + n += 1 + sovInterface(uint64(m.ExecutionGasLimit)) + } + return n +} + func sovInterface(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -827,6 +999,147 @@ func (m *QueryAuthenticationMethodsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *TxExtension) 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 ErrIntOverflowInterface + } + 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: TxExtension: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxExtension: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthenticationGasLimit", wireType) + } + m.AuthenticationGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInterface + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AuthenticationGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BundlerPaymentMessages", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInterface + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthInterface + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthInterface + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BundlerPaymentMessages = append(m.BundlerPaymentMessages, &any.Any{}) + if err := m.BundlerPaymentMessages[len(m.BundlerPaymentMessages)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BundlerPaymentGasLimit", wireType) + } + m.BundlerPaymentGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInterface + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BundlerPaymentGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutionGasLimit", wireType) + } + m.ExecutionGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInterface + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExecutionGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipInterface(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthInterface + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipInterface(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/accounts/keeper.go b/x/accounts/keeper.go index 003e55715fb0..cf306dab0e01 100644 --- a/x/accounts/keeper.go +++ b/x/accounts/keeper.go @@ -18,6 +18,7 @@ import ( "cosmossdk.io/x/accounts/accountstd" "cosmossdk.io/x/accounts/internal/implementation" v1 "cosmossdk.io/x/accounts/v1" + txdecode "cosmossdk.io/x/tx/decode" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -48,14 +49,17 @@ func NewKeeper( env appmodule.Environment, addressCodec address.Codec, ir InterfaceRegistry, + txDecoder *txdecode.Decoder, accounts ...accountstd.AccountCreatorFunc, ) (Keeper, error) { sb := collections.NewSchemaBuilder(env.KVStoreService) keeper := Keeper{ Environment: env, - codec: cdc, + txDecoder: txDecoder, addressCodec: addressCodec, + codec: cdc, makeSendCoinsMsg: defaultCoinsTransferMsgFunc(addressCodec), + accounts: nil, Schema: collections.Schema{}, AccountNumber: collections.NewSequence(sb, AccountNumberKey, "account_number"), AccountsByType: collections.NewMap(sb, AccountTypeKeyPrefix, "accounts_by_type", collections.BytesKey, collections.StringValue), @@ -79,6 +83,7 @@ func NewKeeper( type Keeper struct { appmodule.Environment + txDecoder *txdecode.Decoder addressCodec address.Codec codec codec.Codec makeSendCoinsMsg coinsTransferMsgFunc @@ -99,6 +104,8 @@ type Keeper struct { // Account set and get their own state but this helps providing a nice mapping // between: (account number, account state key) => account state value. AccountsState collections.Map[collections.Pair[uint64, []byte], []byte] + + bundlingDisabled bool // if this is set then bundling of txs is disallowed. } // IsAccountsModuleAccount check if an address belong to a smart account. @@ -340,7 +347,6 @@ func (k Keeper) makeAccountContext(ctx context.Context, accountNumber uint64, ac // sendAnyMessages it a helper function that executes untyped codectypes.Any messages // The messages must all belong to a module. -// nolint: unused // TODO: remove nolint when we bring back bundler payments func (k Keeper) sendAnyMessages(ctx context.Context, sender []byte, anyMessages []*implementation.Any) ([]*implementation.Any, error) { anyResponses := make([]*implementation.Any, len(anyMessages)) for i := range anyMessages { @@ -361,6 +367,37 @@ func (k Keeper) sendAnyMessages(ctx context.Context, sender []byte, anyMessages return anyResponses, nil } +func (k Keeper) sendManyMessagesReturnAnys(ctx context.Context, sender []byte, msgs []transaction.Msg) ([]*implementation.Any, error) { + resp, err := k.sendManyMessages(ctx, sender, msgs) + if err != nil { + return nil, err + } + anys := make([]*implementation.Any, len(resp)) + for i := range resp { + anypb, err := implementation.PackAny(resp[i]) + if err != nil { + return nil, err + } + anys[i] = anypb + } + return anys, nil +} + +// sendManyMessages is a helper function that sends many untyped messages on behalf of the sender +// then returns the respective results. Since the function calls into SendModuleMessage +// it is guaranteed to disallow impersonation attacks from the sender. +func (k Keeper) sendManyMessages(ctx context.Context, sender []byte, msgs []transaction.Msg) ([]transaction.Msg, error) { + resps := make([]transaction.Msg, len(msgs)) + for i, msg := range msgs { + resp, err := k.SendModuleMessage(ctx, sender, msg) + if err != nil { + return nil, fmt.Errorf("failed to execute message %d: %s", i, err.Error()) + } + resps[i] = resp + } + return resps, nil +} + // SendModuleMessage can be used to send a message towards a module. // It should be used when the response type is not known by the caller. func (k Keeper) SendModuleMessage(ctx context.Context, sender []byte, msg transaction.Msg) (transaction.Msg, error) { @@ -430,6 +467,10 @@ func (k Keeper) maybeSendFunds(ctx context.Context, from, to []byte, amt sdk.Coi return nil } +func (k *Keeper) DisableTxBundling() { + k.bundlingDisabled = true +} + const msgInterfaceName = "cosmos.accounts.v1.MsgInterface" // creates a new interface type which is an alias of the proto message interface to avoid conflicts with sdk.Msg diff --git a/x/accounts/keeper_account_abstraction.go b/x/accounts/keeper_account_abstraction.go index 47acac990d43..e919bd84f675 100644 --- a/x/accounts/keeper_account_abstraction.go +++ b/x/accounts/keeper_account_abstraction.go @@ -4,23 +4,22 @@ import ( "context" "errors" "fmt" + "log" + "strings" + "time" + + gogoproto "github.com/cosmos/gogoproto/proto" "cosmossdk.io/collections" aa_interface_v1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1" + "cosmossdk.io/x/accounts/internal/implementation" + v1 "cosmossdk.io/x/accounts/v1" + txdecode "cosmossdk.io/x/tx/decode" "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/tx" ) -var ( - // ErrAuthentication is returned when the authentication fails. - ErrAuthentication = errors.New("authentication failed") - // ErrBundlerPayment is returned when the bundler payment fails. - ErrBundlerPayment = errors.New("bundler payment failed") - // ErrExecution is returned when the execution fails. - ErrExecution = errors.New("execution failed") -) - // IsAbstractedAccount returns if the provided address is an abstracted account or not. func (k Keeper) IsAbstractedAccount(ctx context.Context, addr []byte) (bool, error) { accType, err := k.AccountsByType.Get(ctx, addr) @@ -38,6 +37,7 @@ func (k Keeper) IsAbstractedAccount(ctx context.Context, addr []byte) (bool, err return impl.HasExec(&aa_interface_v1.MsgAuthenticate{}), nil } +// AuthenticateAccount runs the authentication flow of an account. func (k Keeper) AuthenticateAccount(ctx context.Context, signer []byte, bundler string, rawTx *tx.TxRaw, protoTx *tx.Tx, signIndex uint32) error { msg := &aa_interface_v1.MsgAuthenticate{ Bundler: bundler, @@ -51,3 +51,153 @@ func (k Keeper) AuthenticateAccount(ctx context.Context, signer []byte, bundler } return nil } + +// ExecuteBundledTx will execute the single bundled tx. +func (k Keeper) ExecuteBundledTx(ctx context.Context, bundler string, txBytes []byte) *v1.BundledTxResponse { + resp, err := k.executeBundledTx(ctx, bundler, txBytes) + if err != nil { + if resp == nil { + return &v1.BundledTxResponse{ + Error: err.Error(), + } + } + // ensure partial information is not discarded + resp.Error = err.Error() + return resp + } + return resp +} + +func (k Keeper) executeBundledTx(ctx context.Context, bundler string, txBytes []byte) (*v1.BundledTxResponse, error) { + bundledTx, err := k.txDecoder.Decode(txBytes) + if err != nil { + return nil, fmt.Errorf("invalid tx bytes: %w", err) + } + blockInfo := k.HeaderService.HeaderInfo(ctx) + xt, err := verifyAndExtractAaXtFromTx(bundledTx, uint64(blockInfo.Height), blockInfo.Time) + if err != nil { + return nil, fmt.Errorf("%w: tx failed validation check: %w", ErrAASemantics, err) + } + + resp := new(v1.BundledTxResponse) + // to execute a bundled tx the first step is authentication. + signer := bundledTx.Signers[0] + authGasUsed, err := k.BranchService.ExecuteWithGasLimit(ctx, xt.AuthenticationGasLimit, func(ctx context.Context) error { + return k.AuthenticateAccount(ctx, signer, bundler, protov2TxRawToProtoV1(bundledTx.TxRaw), protoV2TxToProtoV1(bundledTx.Tx), 0) + }) + resp.AuthenticationGasUsed = authGasUsed // set independently of outcome + if err != nil { + return resp, fmt.Errorf("%w: %w", ErrAuthentication, err) + } + + // after authentication, we execute the bundler messages. + if len(xt.BundlerPaymentMessages) != 0 { + var paymentMsgResp []*implementation.Any + bundlerPaymentGasUsed, err := k.BranchService.ExecuteWithGasLimit(ctx, xt.BundlerPaymentGasLimit, func(ctx context.Context) error { + responses, err := k.sendAnyMessages(ctx, signer, xt.BundlerPaymentMessages) + if err != nil { + return err + } + paymentMsgResp = responses + return nil + }) + resp.BundlerPaymentGasUsed = bundlerPaymentGasUsed // set independently of outcome + if err != nil { + return resp, fmt.Errorf("%w: %w", ErrBundlerPayment, err) + } + resp.BundlerPaymentResponses = paymentMsgResp + } + + // finally execute the real messages + var execResponses []*implementation.Any + execGasUsed, err := k.BranchService.ExecuteWithGasLimit(ctx, xt.ExecutionGasLimit, func(ctx context.Context) error { + responses, err := k.sendManyMessagesReturnAnys(ctx, signer, bundledTx.Messages) + if err != nil { + return err + } + execResponses = responses + return nil + }) + resp.ExecutionGasUsed = execGasUsed // set independently of outcome + if err != nil { + return resp, fmt.Errorf("%w: %w", ErrExecution, err) + } + resp.ExecutionResponses = execResponses + + return resp, nil +} + +var aaXtName = gogoproto.MessageName(&aa_interface_v1.TxExtension{}) + +func verifyAndExtractAaXtFromTx(bundledTx *txdecode.DecodedTx, currentBlock uint64, currentTime time.Time) (*aa_interface_v1.TxExtension, error) { + // some basic things: we do not allow multi addresses in the bundled tx + // rationale: the bundler could simply bundle multiple txs in the same bundle + // with other accounts. + if len(bundledTx.Signers) != 1 { + return nil, fmt.Errorf("account abstraction bundled txs can only have one signer, got: %d", len(bundledTx.Signers)) + } + // do not allow sign modes different from single + if len(bundledTx.Tx.AuthInfo.SignerInfos) != 1 { + return nil, fmt.Errorf("account abstraction tx must have one signer info") + } + + // check sign mode is valid + if bundledTx.Tx.AuthInfo.SignerInfos[0].ModeInfo.GetSingle() == nil { + return nil, fmt.Errorf("account abstraction mode info must be single") + } + + // we do not want the tx to have any fees set. + if bundledTx.Tx.AuthInfo.Fee != nil { + return nil, fmt.Errorf("account abstraction tx must not have the Fee field set") + } + + // check timeouts TODO: do not like this much since it feels like we are adding repetition of logic. + if bundledTx.Tx.Body.TimeoutTimestamp != nil && currentTime.After(bundledTx.Tx.Body.TimeoutTimestamp.AsTime()) { + return nil, fmt.Errorf("block time is after tx timeout timestamp") + } + if bundledTx.Tx.Body.TimeoutHeight != 0 && currentBlock >= bundledTx.Tx.Body.TimeoutHeight { + return nil, fmt.Errorf("block height is after tx timeout height") + } + + // extract extension + found := false + xt := new(aa_interface_v1.TxExtension) + for i, anyPb := range bundledTx.Tx.Body.ExtensionOptions { + xtName := nameFromTypeURL(anyPb.TypeUrl) + if xtName == aaXtName { + if found { + return nil, fmt.Errorf("multiple aa extensions on the same tx") + } + found = true + // unwrap + err := xt.Unmarshal(anyPb.Value) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal tx extension at index %d: %w", i, err) + } + } else { + log.Printf("name: %s, wanted: %s", xtName, aaXtName) + } + } + if !found { + return nil, fmt.Errorf("did not have AA extension %s", aaXtName) + } + + err := verifyAaXt(xt) + if err != nil { + return nil, fmt.Errorf("invalid account abstraction tx extension: %w", err) + } + + return xt, nil +} + +func verifyAaXt(_ *aa_interface_v1.TxExtension) error { + return nil +} + +func nameFromTypeURL(url string) string { + name := url + if i := strings.LastIndexByte(url, '/'); i >= 0 { + name = name[i+len("/"):] + } + return name +} diff --git a/x/accounts/keeper_account_abstraction_test.go b/x/accounts/keeper_account_abstraction_test.go new file mode 100644 index 000000000000..8f3568773603 --- /dev/null +++ b/x/accounts/keeper_account_abstraction_test.go @@ -0,0 +1,229 @@ +package accounts + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/timestamppb" + + txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1" + aa_interface_v1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1" + txdecode "cosmossdk.io/x/tx/decode" +) + +func TestVerifyAndExtractAaXtFromTx(t *testing.T) { + currentTime := time.Now() + currentBlock := uint64(1000) + + validXt := &aa_interface_v1.TxExtension{ + AuthenticationGasLimit: 100, + BundlerPaymentMessages: nil, + BundlerPaymentGasLimit: 0, + ExecutionGasLimit: 1000, + } + validXtBytes, err := validXt.Marshal() + require.NoError(t, err) + + tests := []struct { + name string + bundledTx *txdecode.DecodedTx + currentBlock uint64 + currentTime time.Time + wantExt *aa_interface_v1.TxExtension + wantErr string + }{ + { + name: "Valid transaction", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + }, + Body: &txv1beta1.TxBody{ + ExtensionOptions: []*anypb.Any{ + {TypeUrl: aaXtName, Value: validXtBytes}, + }, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: validXt, + wantErr: "", + }, + { + name: "Multiple signers", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1"), []byte("signer2")}, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "account abstraction bundled txs can only have one signer, got: 2", + }, + { + name: "Multiple signer infos", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{{}, {}}, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "account abstraction tx must have one signer info", + }, + { + name: "Invalid mode info", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{}}, + }, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "account abstraction mode info must be single", + }, + { + name: "Fee set", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + Fee: &txv1beta1.Fee{}, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "account abstraction tx must not have the Fee field set", + }, + { + name: "Timeout timestamp exceeded", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + }, + Body: &txv1beta1.TxBody{ + TimeoutTimestamp: timestamppb.New(currentTime.Add(-1 * time.Hour)), + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "block time is after tx timeout timestamp", + }, + { + name: "Timeout height exceeded", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + }, + Body: &txv1beta1.TxBody{ + TimeoutHeight: currentBlock - 1, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "block height is after tx timeout height", + }, + { + name: "Multiple AA extensions", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + }, + Body: &txv1beta1.TxBody{ + ExtensionOptions: []*anypb.Any{ + {TypeUrl: aaXtName, Value: validXtBytes}, + {TypeUrl: aaXtName, Value: validXtBytes}, + }, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "multiple aa extensions on the same tx", + }, + { + name: "Missing AA extension", + bundledTx: &txdecode.DecodedTx{ + Signers: [][]byte{[]byte("signer1")}, + Tx: &txv1beta1.Tx{ + AuthInfo: &txv1beta1.AuthInfo{ + SignerInfos: []*txv1beta1.SignerInfo{ + {ModeInfo: &txv1beta1.ModeInfo{Sum: &txv1beta1.ModeInfo_Single_{ + Single: &txv1beta1.ModeInfo_Single{Mode: 1}, + }}}, + }, + }, + Body: &txv1beta1.TxBody{ + ExtensionOptions: []*anypb.Any{}, + }, + }, + }, + currentBlock: currentBlock, + currentTime: currentTime, + wantExt: nil, + wantErr: "did not have AA extension", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotExt, err := verifyAndExtractAaXtFromTx(tt.bundledTx, tt.currentBlock, tt.currentTime) + if tt.wantErr != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.wantExt, gotExt) + } + }) + } +} diff --git a/x/accounts/msg_server.go b/x/accounts/msg_server.go index 372941cece0e..2ee9a9334a95 100644 --- a/x/accounts/msg_server.go +++ b/x/accounts/msg_server.go @@ -2,6 +2,7 @@ package accounts import ( "context" + "errors" "fmt" "cosmossdk.io/core/event" @@ -11,6 +12,8 @@ import ( var _ v1.MsgServer = msgServer{} +var ErrBundlingDisabled = errors.New("accounts: bundling is disabled") + func NewMsgServer(k Keeper) v1.MsgServer { return &msgServer{k} } @@ -85,5 +88,18 @@ func (m msgServer) Execute(ctx context.Context, execute *v1.MsgExecute) (*v1.Msg } func (m msgServer) ExecuteBundle(ctx context.Context, req *v1.MsgExecuteBundle) (*v1.MsgExecuteBundleResponse, error) { - panic("impl") + if m.k.bundlingDisabled { + return nil, ErrBundlingDisabled + } + + _, err := m.k.addressCodec.StringToBytes(req.Bundler) + if err != nil { + return nil, err + } + responses := make([]*v1.BundledTxResponse, len(req.Txs)) + for i, bundledTx := range req.Txs { + bundleRes := m.k.ExecuteBundledTx(ctx, req.Bundler, bundledTx) + responses[i] = bundleRes + } + return &v1.MsgExecuteBundleResponse{Responses: responses}, nil } diff --git a/x/accounts/msg_server_test.go b/x/accounts/msg_server_test.go index 23d4cc66c684..4a5c6fb87d2c 100644 --- a/x/accounts/msg_server_test.go +++ b/x/accounts/msg_server_test.go @@ -43,3 +43,16 @@ func TestMsgServer(t *testing.T) { require.NoError(t, err) require.NotNil(t, execResp) } + +func TestMsgServer_BundlingDisabled(t *testing.T) { + k, ctx := newKeeper(t, accountstd.AddAccount("test", NewTestAccount)) + k.DisableTxBundling() + + s := NewMsgServer(k) + + _, err := s.ExecuteBundle(ctx, &v1.MsgExecuteBundle{ + Bundler: "someone", + Txs: nil, + }) + require.ErrorIs(t, err, ErrBundlingDisabled) +} diff --git a/x/accounts/proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto b/x/accounts/proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto index d411e6c1e17f..a0d1ce4fbc16 100644 --- a/x/accounts/proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto +++ b/x/accounts/proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package cosmos.accounts.interfaces.account_abstraction.v1; +import "google/protobuf/any.proto"; import "cosmos/tx/v1beta1/tx.proto"; option go_package = "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1"; @@ -36,4 +37,29 @@ message QueryAuthenticationMethods {} message QueryAuthenticationMethodsResponse { // authentication_methods are the authentication methods that the account supports. repeated string authentication_methods = 1; +} + +// TxExtension is the extension option that AA's add to txs when they're bundled. +message TxExtension { + // authentication_gas_limit expresses the gas limit to be used for the authentication part of the + // bundled tx. + uint64 authentication_gas_limit = 1; + // bundler_payment_messages expresses a list of messages that the account + // executes to pay the bundler for submitting the bundled tx. + // It can be empty if the bundler does not need any form of payment, + // the handshake for submitting the UserOperation might have happened off-chain. + // Bundlers and accounts are free to use any form of payment, in fact the payment can + // either be empty or be expressed as: + // - NFT payment + // - IBC Token payment. + // - Payment through delegations. + repeated google.protobuf.Any bundler_payment_messages = 2; + // bundler_payment_gas_limit defines the gas limit to be used for the bundler payment. + // This ensures that, since the bundler executes a list of bundled tx and there needs to + // be minimal trust between bundler and the tx sender, the sender cannot consume + // the whole bundle gas. + uint64 bundler_payment_gas_limit = 3; + // execution_gas_limit defines the gas limit to be used for the execution of the UserOperation's + // execution messages. + uint64 execution_gas_limit = 4; } \ No newline at end of file diff --git a/x/accounts/proto/cosmos/accounts/v1/tx.proto b/x/accounts/proto/cosmos/accounts/v1/tx.proto index 1bf502fcbf27..f9f746cace11 100644 --- a/x/accounts/proto/cosmos/accounts/v1/tx.proto +++ b/x/accounts/proto/cosmos/accounts/v1/tx.proto @@ -70,8 +70,6 @@ message MsgExecuteResponse { google.protobuf.Any response = 1; } -// -------- Account Abstraction --------- - // MsgExecuteBundle defines the ExecuteBundle request type for the Msg/ExecuteBundle RPC method. message MsgExecuteBundle { option (cosmos.msg.v1.signer) = "bundler"; @@ -79,17 +77,35 @@ message MsgExecuteBundle { // to execute one or multiple UserOperations on behalf of others. string bundler = 1; // txs defines the txs to execute on behalf of other users. - repeated cosmos.tx.v1beta1.TxRaw txs = 2; + repeated bytes txs = 2; } // BundledTxResponse defines the response of a bundled tx. +// If the operation fails the error field will be populated, the used gas fields will also be +// populated depending on when the execution stopped. Bundler payment responses will be populated +// if the execution fails. message BundledTxResponse { - google.protobuf.Any exec_responses = 1; - string error = 2; + // authentication_gas_used defines the gas used for the authentication part of the UserOperation. + uint64 authentication_gas_used = 1; + // bundler_payment_gas_used defines the gas used for the bundler payment part of the UserOperation. + uint64 bundler_payment_gas_used = 2; + // bundler_payment_responses defines the responses of the bundler payment messages. + // It can be empty if the bundler does not need any form of payment. + repeated google.protobuf.Any bundler_payment_responses = 3; + // execution_gas_used defines the gas used for the execution part of the UserOperation. + uint64 execution_gas_used = 4; + // execution_responses defines the responses of the execution messages. + repeated google.protobuf.Any execution_responses = 5; + // error defines the error that occurred during the execution of the UserOperation. + // If the error is not empty, the UserOperation failed. + // Other fields might be populated even if the error is not empty, for example + // if the operation fails after the authentication step, the authentication_gas_used + // field will be populated. + string error = 6; } // MsgExecuteBundleResponse defines the ExecuteBundle response type for the Msg/ExecuteBundle RPC method. message MsgExecuteBundleResponse { - // responses is the list of responses returned by the account implementations. + // responses is the list of responses from the bundle txs. repeated BundledTxResponse responses = 1; } diff --git a/x/accounts/protoutils.go b/x/accounts/protoutils.go new file mode 100644 index 000000000000..0c337c69c56f --- /dev/null +++ b/x/accounts/protoutils.go @@ -0,0 +1,117 @@ +package accounts + +import ( + "time" + + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/timestamppb" + + txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1" + "cosmossdk.io/x/accounts/internal/implementation" + + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +func protoV2TxToProtoV1(t *txv1beta1.Tx) *tx.Tx { + if t == nil || t.Body == nil || t.AuthInfo == nil { + panic("unvalidated tx") + } + + return &tx.Tx{ + Body: &tx.TxBody{ + Messages: protoV2AnyToV1(t.Body.Messages...), + Memo: t.Body.Memo, + TimeoutHeight: t.Body.TimeoutHeight, + Unordered: t.Body.Unordered, + TimeoutTimestamp: protoV2TimestampToV1(t.Body.TimeoutTimestamp), + ExtensionOptions: protoV2AnyToV1(t.Body.ExtensionOptions...), + NonCriticalExtensionOptions: protoV2AnyToV1(t.Body.NonCriticalExtensionOptions...), + }, + AuthInfo: &tx.AuthInfo{ + SignerInfos: protoV2SignerInfoToV1(t.AuthInfo.SignerInfos), + Fee: nil, // Fee and Tip are expected + Tip: nil, // to be empty. + }, + Signatures: t.Signatures, + } +} + +func protoV2TimestampToV1(timestamp *timestamppb.Timestamp) *time.Time { + if timestamp == nil { + return nil + } + ts := timestamp.AsTime() + return &ts +} + +func protov2TxRawToProtoV1(raw *txv1beta1.TxRaw) *tx.TxRaw { + // Check if 'raw' is nil to prevent nil dereferences + if raw == nil { + panic("unvalidated raw tx") + } + return &tx.TxRaw{ + BodyBytes: raw.BodyBytes, + AuthInfoBytes: raw.AuthInfoBytes, + Signatures: raw.Signatures, + } +} + +func protoV2AnyToV1(v2s ...*anypb.Any) []*implementation.Any { + v1s := make([]*implementation.Any, len(v2s)) + for i, v2 := range v2s { + if v2 == nil { + panic("unvalidated any") + } + v1s[i] = &implementation.Any{ + TypeUrl: v2.TypeUrl, + Value: v2.Value, + } + } + return v1s +} + +func protoV2SignerInfoToV1(infos []*txv1beta1.SignerInfo) []*tx.SignerInfo { + v1s := make([]*tx.SignerInfo, len(infos)) + for i, info := range infos { + if info == nil { + // Handle nil 'info' to avoid nil dereference + panic("unvalidated signer info") + } + var publicKey *implementation.Any + if info.PublicKey != nil { + publicKeys := protoV2AnyToV1(info.PublicKey) + if len(publicKeys) > 0 && publicKeys[0] != nil { + publicKey = publicKeys[0] + } + } + v1s[i] = &tx.SignerInfo{ + PublicKey: publicKey, + ModeInfo: protoV2ModeInfoToV1(info.ModeInfo), + Sequence: info.Sequence, + } + } + return v1s +} + +func protoV2ModeInfoToV1(info *txv1beta1.ModeInfo) *tx.ModeInfo { + if info == nil || info.Sum == nil { + panic("unvalidated mode info") + } + switch v := info.Sum.(type) { + case *txv1beta1.ModeInfo_Single_: + if v.Single == nil { + panic("unvalidated single mode") + } + return &tx.ModeInfo{ + Sum: &tx.ModeInfo_Single_{ + Single: &tx.ModeInfo_Single{ + Mode: signing.SignMode(v.Single.Mode), + }, + }, + } + default: + // NOTE: we have a check that disallows modes different from single + panic("unexpected mode info") + } +} diff --git a/x/accounts/utils_test.go b/x/accounts/utils_test.go index 5e1cf037883e..abe494f69b42 100644 --- a/x/accounts/utils_test.go +++ b/x/accounts/utils_test.go @@ -75,7 +75,7 @@ func newKeeper(t *testing.T, accounts ...implementation.AccountCreatorFunc) (Kee ss := coretesting.KVStoreService(ctx, "test") env := runtime.NewEnvironment(ss, coretesting.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)) env.EventService = eventService{} - m, err := NewKeeper(codec.NewProtoCodec(ir), env, addressCodec, ir, accounts...) + m, err := NewKeeper(codec.NewProtoCodec(ir), env, addressCodec, ir, nil, accounts...) require.NoError(t, err) return m, ctx } diff --git a/x/accounts/v1/tx.pb.go b/x/accounts/v1/tx.pb.go index aa1b8a020aca..953167ab99a1 100644 --- a/x/accounts/v1/tx.pb.go +++ b/x/accounts/v1/tx.pb.go @@ -9,7 +9,7 @@ import ( github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" - tx "github.com/cosmos/cosmos-sdk/types/tx" + _ "github.com/cosmos/cosmos-sdk/types/tx" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" @@ -288,7 +288,7 @@ type MsgExecuteBundle struct { // to execute one or multiple UserOperations on behalf of others. Bundler string `protobuf:"bytes,1,opt,name=bundler,proto3" json:"bundler,omitempty"` // txs defines the txs to execute on behalf of other users. - Txs []*tx.TxRaw `protobuf:"bytes,2,rep,name=txs,proto3" json:"txs,omitempty"` + Txs [][]byte `protobuf:"bytes,2,rep,name=txs,proto3" json:"txs,omitempty"` } func (m *MsgExecuteBundle) Reset() { *m = MsgExecuteBundle{} } @@ -331,7 +331,7 @@ func (m *MsgExecuteBundle) GetBundler() string { return "" } -func (m *MsgExecuteBundle) GetTxs() []*tx.TxRaw { +func (m *MsgExecuteBundle) GetTxs() [][]byte { if m != nil { return m.Txs } @@ -339,9 +339,27 @@ func (m *MsgExecuteBundle) GetTxs() []*tx.TxRaw { } // BundledTxResponse defines the response of a bundled tx. +// If the operation fails the error field will be populated, the used gas fields will also be +// populated depending on when the execution stopped. Bundler payment responses will be populated +// if the execution fails. type BundledTxResponse struct { - ExecResponses *any.Any `protobuf:"bytes,1,opt,name=exec_responses,json=execResponses,proto3" json:"exec_responses,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // authentication_gas_used defines the gas used for the authentication part of the UserOperation. + AuthenticationGasUsed uint64 `protobuf:"varint,1,opt,name=authentication_gas_used,json=authenticationGasUsed,proto3" json:"authentication_gas_used,omitempty"` + // bundler_payment_gas_used defines the gas used for the bundler payment part of the UserOperation. + BundlerPaymentGasUsed uint64 `protobuf:"varint,2,opt,name=bundler_payment_gas_used,json=bundlerPaymentGasUsed,proto3" json:"bundler_payment_gas_used,omitempty"` + // bundler_payment_responses defines the responses of the bundler payment messages. + // It can be empty if the bundler does not need any form of payment. + BundlerPaymentResponses []*any.Any `protobuf:"bytes,3,rep,name=bundler_payment_responses,json=bundlerPaymentResponses,proto3" json:"bundler_payment_responses,omitempty"` + // execution_gas_used defines the gas used for the execution part of the UserOperation. + ExecutionGasUsed uint64 `protobuf:"varint,4,opt,name=execution_gas_used,json=executionGasUsed,proto3" json:"execution_gas_used,omitempty"` + // execution_responses defines the responses of the execution messages. + ExecutionResponses []*any.Any `protobuf:"bytes,5,rep,name=execution_responses,json=executionResponses,proto3" json:"execution_responses,omitempty"` + // error defines the error that occurred during the execution of the UserOperation. + // If the error is not empty, the UserOperation failed. + // Other fields might be populated even if the error is not empty, for example + // if the operation fails after the authentication step, the authentication_gas_used + // field will be populated. + Error string `protobuf:"bytes,6,opt,name=error,proto3" json:"error,omitempty"` } func (m *BundledTxResponse) Reset() { *m = BundledTxResponse{} } @@ -377,9 +395,37 @@ func (m *BundledTxResponse) XXX_DiscardUnknown() { var xxx_messageInfo_BundledTxResponse proto.InternalMessageInfo -func (m *BundledTxResponse) GetExecResponses() *any.Any { +func (m *BundledTxResponse) GetAuthenticationGasUsed() uint64 { if m != nil { - return m.ExecResponses + return m.AuthenticationGasUsed + } + return 0 +} + +func (m *BundledTxResponse) GetBundlerPaymentGasUsed() uint64 { + if m != nil { + return m.BundlerPaymentGasUsed + } + return 0 +} + +func (m *BundledTxResponse) GetBundlerPaymentResponses() []*any.Any { + if m != nil { + return m.BundlerPaymentResponses + } + return nil +} + +func (m *BundledTxResponse) GetExecutionGasUsed() uint64 { + if m != nil { + return m.ExecutionGasUsed + } + return 0 +} + +func (m *BundledTxResponse) GetExecutionResponses() []*any.Any { + if m != nil { + return m.ExecutionResponses } return nil } @@ -393,7 +439,7 @@ func (m *BundledTxResponse) GetError() string { // MsgExecuteBundleResponse defines the ExecuteBundle response type for the Msg/ExecuteBundle RPC method. type MsgExecuteBundleResponse struct { - // responses is the list of responses returned by the account implementations. + // responses is the list of responses from the bundle txs. Responses []*BundledTxResponse `protobuf:"bytes,1,rep,name=responses,proto3" json:"responses,omitempty"` } @@ -450,46 +496,51 @@ func init() { func init() { proto.RegisterFile("cosmos/accounts/v1/tx.proto", fileDescriptor_29c2b6d8a13d4189) } var fileDescriptor_29c2b6d8a13d4189 = []byte{ - // 609 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0x8d, 0x9b, 0xb6, 0xf9, 0x7a, 0xd3, 0x9f, 0x8f, 0x51, 0x55, 0x5c, 0x57, 0x72, 0x4b, 0xf8, - 0x8b, 0x2a, 0x18, 0x37, 0x85, 0x55, 0x59, 0xb5, 0x15, 0x08, 0x16, 0x5d, 0x60, 0x75, 0xc5, 0xa6, - 0xf2, 0xcf, 0x64, 0x88, 0xda, 0x78, 0x22, 0xdf, 0x71, 0x71, 0x76, 0x88, 0x07, 0x40, 0x3c, 0x07, - 0xab, 0x3e, 0x46, 0x97, 0x5d, 0xb2, 0x40, 0x80, 0x1a, 0xa4, 0xbe, 0x06, 0xb2, 0x3d, 0xe3, 0x04, - 0x4a, 0xa2, 0x2e, 0x59, 0x65, 0xe6, 0x9e, 0x73, 0xef, 0x9c, 0x73, 0xc6, 0x19, 0x58, 0x0b, 0x04, - 0x76, 0x05, 0x3a, 0x5e, 0x10, 0x88, 0x24, 0x92, 0xe8, 0x9c, 0xb6, 0x1c, 0x99, 0xd2, 0x5e, 0x2c, - 0xa4, 0x20, 0xa4, 0x00, 0xa9, 0x06, 0xe9, 0x69, 0xcb, 0x5a, 0xe5, 0x42, 0xf0, 0x13, 0xe6, 0xe4, - 0x0c, 0x3f, 0x69, 0x3b, 0x5e, 0xd4, 0x2f, 0xe8, 0xd6, 0x6d, 0x35, 0xab, 0x8b, 0x3c, 0x1b, 0xd3, - 0x45, 0xae, 0x00, 0x5b, 0x01, 0xbe, 0x87, 0xcc, 0x39, 0x6d, 0xf9, 0x4c, 0x7a, 0x2d, 0x27, 0x10, - 0x9d, 0x48, 0xe1, 0x96, 0xc2, 0x65, 0x5a, 0xa2, 0x5a, 0x83, 0xb5, 0xcc, 0x05, 0x17, 0xf9, 0xd2, - 0xc9, 0x56, 0x45, 0xb5, 0xf1, 0xd3, 0x80, 0xda, 0x01, 0xf2, 0x57, 0x51, 0x47, 0x92, 0x15, 0x98, - 0x45, 0x16, 0x85, 0x2c, 0x36, 0x8d, 0x0d, 0xa3, 0x39, 0xe7, 0xaa, 0x1d, 0xb9, 0x03, 0xf3, 0x4a, - 0xf8, 0x91, 0xec, 0xf7, 0x98, 0x39, 0x95, 0xa3, 0x75, 0x55, 0x3b, 0xec, 0xf7, 0x18, 0xa1, 0x50, - 0xeb, 0x32, 0x44, 0x8f, 0x33, 0xb3, 0xba, 0x61, 0x34, 0xeb, 0xdb, 0xcb, 0xb4, 0xb0, 0x47, 0xb5, - 0x3d, 0xba, 0x1b, 0xf5, 0x5d, 0x4d, 0x22, 0x1e, 0xcc, 0xb4, 0x93, 0x28, 0x44, 0x73, 0x7a, 0xa3, - 0xda, 0xac, 0x6f, 0xaf, 0x52, 0x15, 0x50, 0x66, 0x8c, 0x2a, 0xe9, 0x74, 0x5f, 0x74, 0xa2, 0xbd, - 0xad, 0xf3, 0x6f, 0xeb, 0x95, 0xcf, 0xdf, 0xd7, 0x9b, 0xbc, 0x23, 0xdf, 0x26, 0x3e, 0x0d, 0x44, - 0xd7, 0x51, 0x2e, 0x8b, 0x9f, 0xc7, 0x18, 0x1e, 0x3b, 0x99, 0x2e, 0xcc, 0x1b, 0xd0, 0x2d, 0x26, - 0xef, 0xd4, 0x3f, 0x5c, 0x9d, 0x6d, 0x2a, 0x0b, 0x8d, 0x13, 0x58, 0x52, 0x2e, 0x5d, 0x86, 0x3d, - 0x11, 0x21, 0x23, 0x0f, 0x61, 0x49, 0xbb, 0xf2, 0xc2, 0x30, 0x66, 0x88, 0xca, 0xf6, 0xa2, 0x2a, - 0xef, 0x16, 0x55, 0xb2, 0x05, 0xff, 0xc5, 0xaa, 0x29, 0xb7, 0x3e, 0xce, 0x5c, 0xc9, 0x6a, 0x7c, - 0x35, 0x00, 0x0e, 0x90, 0x3f, 0x4f, 0x59, 0x90, 0x48, 0x36, 0x36, 0xd7, 0x15, 0x98, 0x95, 0x5e, - 0xcc, 0x99, 0x54, 0x89, 0xaa, 0xdd, 0x3f, 0x1f, 0xe6, 0x0b, 0x20, 0x43, 0x77, 0x65, 0x9e, 0xa3, - 0x31, 0x19, 0x37, 0x8a, 0xa9, 0x0d, 0xff, 0x0f, 0xe7, 0xec, 0x25, 0x51, 0x78, 0xc2, 0x88, 0x09, - 0x35, 0x3f, 0x5f, 0xe9, 0xb0, 0xf4, 0x96, 0x6c, 0x42, 0x55, 0xa6, 0x68, 0x4e, 0xe5, 0x1e, 0x4d, - 0xed, 0x51, 0xa6, 0xa5, 0xc3, 0xc3, 0xd4, 0xf5, 0xde, 0xb9, 0x19, 0x69, 0x67, 0x3e, 0x93, 0xab, - 0x3b, 0x1b, 0x6d, 0xb8, 0x55, 0x4c, 0x0f, 0x0f, 0xd3, 0x52, 0xee, 0x33, 0x58, 0x64, 0x29, 0x0b, - 0x8e, 0xb4, 0x1a, 0x9c, 0x28, 0x7a, 0x21, 0xe3, 0xea, 0x5e, 0x24, 0xcb, 0x30, 0xc3, 0xe2, 0x58, - 0xc4, 0xea, 0xe2, 0x8a, 0x4d, 0xe3, 0x08, 0xcc, 0x3f, 0xfd, 0x94, 0xc7, 0xed, 0xc3, 0xdc, 0xe8, - 0x49, 0x99, 0x87, 0xfb, 0xf4, 0xfa, 0xab, 0x40, 0xaf, 0x09, 0x75, 0x87, 0x7d, 0xdb, 0x1f, 0xa7, - 0xa0, 0x7a, 0x80, 0x9c, 0xbc, 0x84, 0xe9, 0xfc, 0x0f, 0xbb, 0xf6, 0xb7, 0x09, 0xea, 0x3b, 0xb7, - 0xee, 0x4e, 0x00, 0x4b, 0x59, 0xaf, 0xa1, 0xa6, 0xbf, 0x52, 0x7b, 0x0c, 0x5f, 0xe1, 0xd6, 0x83, - 0xc9, 0x78, 0x39, 0x32, 0x80, 0x85, 0xdf, 0xaf, 0xf4, 0xde, 0xe4, 0xc6, 0x82, 0x65, 0x3d, 0xba, - 0x09, 0x4b, 0x1f, 0x62, 0xcd, 0xbc, 0xbf, 0x3a, 0xdb, 0x34, 0xf6, 0x9e, 0x9e, 0x5f, 0xda, 0xc6, - 0xc5, 0xa5, 0x6d, 0xfc, 0xb8, 0xb4, 0x8d, 0x4f, 0x03, 0xbb, 0x72, 0x31, 0xb0, 0x2b, 0x5f, 0x06, - 0x76, 0xe5, 0x8d, 0x7a, 0x09, 0x31, 0x3c, 0xa6, 0x1d, 0xe1, 0xa4, 0xa3, 0xcf, 0xb2, 0x3f, 0x9b, - 0x5f, 0xed, 0x93, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc4, 0x6a, 0x98, 0xb0, 0xb3, 0x05, 0x00, - 0x00, + // 690 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0xcb, 0x4e, 0xdb, 0x4c, + 0x14, 0x8e, 0x73, 0xfd, 0x39, 0xe1, 0x2f, 0x74, 0x4a, 0xc1, 0x18, 0xc9, 0xa4, 0xe9, 0x2d, 0x42, + 0xd4, 0x26, 0xb4, 0x6a, 0x25, 0x76, 0x80, 0xe8, 0x45, 0x2a, 0x12, 0x8d, 0xe8, 0xa6, 0x9b, 0xc8, + 0xb1, 0x07, 0x13, 0x41, 0x3c, 0x91, 0xcf, 0x18, 0x25, 0xbb, 0xaa, 0x0f, 0x50, 0xf5, 0x39, 0xba, + 0xe2, 0x31, 0x58, 0xb2, 0xec, 0xa2, 0xea, 0x05, 0x2a, 0xf1, 0x1a, 0x55, 0xc6, 0x33, 0x36, 0xe1, + 0x12, 0xb1, 0xec, 0x2a, 0x33, 0xf3, 0x9d, 0xf3, 0x9d, 0xf3, 0x7d, 0x27, 0x33, 0x86, 0x39, 0x97, + 0x61, 0x87, 0xa1, 0xed, 0xb8, 0x2e, 0x8b, 0x02, 0x8e, 0xf6, 0x41, 0xdd, 0xe6, 0x3d, 0xab, 0x1b, + 0x32, 0xce, 0x08, 0x89, 0x41, 0x4b, 0x81, 0xd6, 0x41, 0xdd, 0x98, 0xf5, 0x19, 0xf3, 0xf7, 0xa9, + 0x2d, 0x22, 0x5a, 0xd1, 0x8e, 0xed, 0x04, 0xfd, 0x38, 0xdc, 0x98, 0x91, 0x5c, 0x1d, 0xf4, 0x07, + 0x34, 0x1d, 0xf4, 0x25, 0x60, 0x4a, 0xa0, 0xe5, 0x20, 0xb5, 0x0f, 0xea, 0x2d, 0xca, 0x9d, 0xba, + 0xed, 0xb2, 0x76, 0x20, 0x71, 0x43, 0xe2, 0xbc, 0x97, 0xa0, 0xaa, 0x07, 0x63, 0xca, 0x67, 0x3e, + 0x13, 0x4b, 0x7b, 0xb0, 0x8a, 0x4f, 0xab, 0x7f, 0x34, 0x28, 0x6d, 0xa2, 0xff, 0x26, 0x68, 0x73, + 0x32, 0x0d, 0x45, 0xa4, 0x81, 0x47, 0x43, 0x5d, 0xab, 0x68, 0xb5, 0xb1, 0x86, 0xdc, 0x91, 0x7b, + 0x30, 0x2e, 0x1b, 0x6f, 0xf2, 0x7e, 0x97, 0xea, 0x59, 0x81, 0x96, 0xe5, 0xd9, 0x76, 0xbf, 0x4b, + 0x89, 0x05, 0xa5, 0x0e, 0x45, 0x74, 0x7c, 0xaa, 0xe7, 0x2a, 0x5a, 0xad, 0xbc, 0x3c, 0x65, 0xc5, + 0xf2, 0x2c, 0x25, 0xcf, 0x5a, 0x0d, 0xfa, 0x0d, 0x15, 0x44, 0x1c, 0x28, 0xec, 0x44, 0x81, 0x87, + 0x7a, 0xbe, 0x92, 0xab, 0x95, 0x97, 0x67, 0x2d, 0x69, 0xd0, 0x40, 0x98, 0x25, 0x5b, 0xb7, 0xd6, + 0x59, 0x3b, 0x58, 0x5b, 0x3a, 0xfa, 0x31, 0x9f, 0xf9, 0xfa, 0x73, 0xbe, 0xe6, 0xb7, 0xf9, 0x6e, + 0xd4, 0xb2, 0x5c, 0xd6, 0xb1, 0xa5, 0xca, 0xf8, 0xe7, 0x09, 0x7a, 0x7b, 0xf6, 0xa0, 0x2f, 0x14, + 0x09, 0xd8, 0x88, 0x99, 0x57, 0xca, 0x9f, 0xce, 0x0e, 0x17, 0xa4, 0x84, 0xea, 0x3e, 0x4c, 0x48, + 0x95, 0x0d, 0x8a, 0x5d, 0x16, 0x20, 0x25, 0x8f, 0x61, 0x42, 0xa9, 0x72, 0x3c, 0x2f, 0xa4, 0x88, + 0x52, 0xf6, 0x2d, 0x79, 0xbc, 0x1a, 0x9f, 0x92, 0x25, 0xf8, 0x2f, 0x94, 0x49, 0x42, 0xfa, 0x75, + 0xe2, 0x92, 0xa8, 0xea, 0x77, 0x0d, 0x60, 0x13, 0xfd, 0x8d, 0x1e, 0x75, 0x23, 0x4e, 0xaf, 0xf5, + 0x75, 0x1a, 0x8a, 0xdc, 0x09, 0x7d, 0xca, 0xa5, 0xa3, 0x72, 0xf7, 0xcf, 0x9b, 0xf9, 0x12, 0x48, + 0xaa, 0x2e, 0xf1, 0xf3, 0xbc, 0x4d, 0xda, 0x8d, 0x6c, 0x7a, 0x0b, 0x93, 0x29, 0xcf, 0x5a, 0x14, + 0x78, 0xfb, 0x94, 0xe8, 0x50, 0x6a, 0x89, 0x95, 0x32, 0x4b, 0x6d, 0xc9, 0x24, 0xe4, 0x78, 0x0f, + 0xf5, 0x6c, 0x25, 0x57, 0x1b, 0x6f, 0x0c, 0x96, 0x2b, 0xe3, 0x83, 0xa6, 0x14, 0x5e, 0xfd, 0x9d, + 0x85, 0xdb, 0x31, 0x89, 0xb7, 0xdd, 0x4b, 0xba, 0x7a, 0x0e, 0x33, 0x4e, 0xc4, 0x77, 0x69, 0xc0, + 0xdb, 0xae, 0xc3, 0xdb, 0x2c, 0x68, 0xfa, 0x0e, 0x36, 0x23, 0xa4, 0x9e, 0xe0, 0xcf, 0x37, 0xee, + 0x0e, 0xc3, 0xaf, 0x1c, 0x7c, 0x8f, 0xd4, 0x23, 0x2f, 0x40, 0x97, 0xc4, 0xcd, 0xae, 0xd3, 0xef, + 0xd0, 0x80, 0xa7, 0x89, 0xd9, 0x38, 0x51, 0xe2, 0x5b, 0x31, 0xac, 0x12, 0xb7, 0x60, 0xf6, 0x62, + 0xa2, 0x12, 0x8c, 0x7a, 0x4e, 0x0c, 0xe8, 0x6a, 0x5f, 0x66, 0x86, 0xf9, 0x94, 0x02, 0x24, 0x8b, + 0x40, 0xa8, 0xf0, 0x68, 0xa8, 0xfb, 0xbc, 0x68, 0x62, 0x32, 0x41, 0x54, 0xfd, 0x0d, 0xb8, 0x93, + 0x46, 0xa7, 0x95, 0x0b, 0x23, 0x2a, 0xa7, 0xf4, 0x69, 0xd1, 0x29, 0x28, 0xd0, 0x30, 0x64, 0xa1, + 0x5e, 0x14, 0x53, 0x88, 0x37, 0xd5, 0x26, 0xe8, 0x17, 0x27, 0x96, 0x38, 0xbd, 0x0e, 0x63, 0x69, + 0x39, 0x4d, 0x94, 0x7b, 0x68, 0x5d, 0x7e, 0xf7, 0xac, 0x4b, 0x33, 0x6a, 0xa4, 0x79, 0xcb, 0x9f, + 0xb3, 0x90, 0xdb, 0x44, 0x9f, 0xbc, 0x86, 0xbc, 0x78, 0x92, 0xe6, 0xae, 0x62, 0x90, 0x37, 0xd9, + 0xb8, 0x3f, 0x02, 0x4c, 0xda, 0x7a, 0x07, 0x25, 0x75, 0x0f, 0xcd, 0x6b, 0xe2, 0x25, 0x6e, 0x3c, + 0x1a, 0x8d, 0x27, 0x94, 0x2e, 0xfc, 0x3f, 0xfc, 0xa7, 0x7d, 0x30, 0x3a, 0x31, 0x8e, 0x32, 0x16, + 0x6f, 0x12, 0xa5, 0x8a, 0x18, 0x85, 0x8f, 0x67, 0x87, 0x0b, 0xda, 0xda, 0xb3, 0xa3, 0x13, 0x53, + 0x3b, 0x3e, 0x31, 0xb5, 0x5f, 0x27, 0xa6, 0xf6, 0xe5, 0xd4, 0xcc, 0x1c, 0x9f, 0x9a, 0x99, 0x6f, + 0xa7, 0x66, 0xe6, 0x83, 0x7c, 0xeb, 0xd1, 0xdb, 0xb3, 0xda, 0xcc, 0xee, 0x9d, 0xff, 0xf0, 0xb4, + 0x8a, 0x62, 0xbe, 0x4f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x15, 0x7e, 0x1c, 0x2a, 0x95, 0x06, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -878,14 +929,9 @@ func (m *MsgExecuteBundle) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = l if len(m.Txs) > 0 { for iNdEx := len(m.Txs) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Txs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } + i -= len(m.Txs[iNdEx]) + copy(dAtA[i:], m.Txs[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Txs[iNdEx]))) i-- dAtA[i] = 0x12 } @@ -925,19 +971,50 @@ func (m *BundledTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.Error) i = encodeVarintTx(dAtA, i, uint64(len(m.Error))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x32 } - if m.ExecResponses != nil { - { - size, err := m.ExecResponses.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if len(m.ExecutionResponses) > 0 { + for iNdEx := len(m.ExecutionResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExecutionResponses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a } + } + if m.ExecutionGasUsed != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ExecutionGasUsed)) i-- - dAtA[i] = 0xa + dAtA[i] = 0x20 + } + if len(m.BundlerPaymentResponses) > 0 { + for iNdEx := len(m.BundlerPaymentResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BundlerPaymentResponses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.BundlerPaymentGasUsed != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.BundlerPaymentGasUsed)) + i-- + dAtA[i] = 0x10 + } + if m.AuthenticationGasUsed != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AuthenticationGasUsed)) + i-- + dAtA[i] = 0x8 } return len(dAtA) - i, nil } @@ -1085,8 +1162,8 @@ func (m *MsgExecuteBundle) Size() (n int) { n += 1 + l + sovTx(uint64(l)) } if len(m.Txs) > 0 { - for _, e := range m.Txs { - l = e.Size() + for _, b := range m.Txs { + l = len(b) n += 1 + l + sovTx(uint64(l)) } } @@ -1099,9 +1176,26 @@ func (m *BundledTxResponse) Size() (n int) { } var l int _ = l - if m.ExecResponses != nil { - l = m.ExecResponses.Size() - n += 1 + l + sovTx(uint64(l)) + if m.AuthenticationGasUsed != 0 { + n += 1 + sovTx(uint64(m.AuthenticationGasUsed)) + } + if m.BundlerPaymentGasUsed != 0 { + n += 1 + sovTx(uint64(m.BundlerPaymentGasUsed)) + } + if len(m.BundlerPaymentResponses) > 0 { + for _, e := range m.BundlerPaymentResponses { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.ExecutionGasUsed != 0 { + n += 1 + sovTx(uint64(m.ExecutionGasUsed)) + } + if len(m.ExecutionResponses) > 0 { + for _, e := range m.ExecutionResponses { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } } l = len(m.Error) if l > 0 { @@ -1768,7 +1862,7 @@ func (m *MsgExecuteBundle) Unmarshal(dAtA []byte) error { if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Txs", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1778,25 +1872,23 @@ func (m *MsgExecuteBundle) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Txs = append(m.Txs, &tx.TxRaw{}) - if err := m.Txs[len(m.Txs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Txs = append(m.Txs, make([]byte, postIndex-iNdEx)) + copy(m.Txs[len(m.Txs)-1], dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -1849,8 +1941,46 @@ func (m *BundledTxResponse) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthenticationGasUsed", wireType) + } + m.AuthenticationGasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AuthenticationGasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BundlerPaymentGasUsed", wireType) + } + m.BundlerPaymentGasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BundlerPaymentGasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExecResponses", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BundlerPaymentResponses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1877,14 +2007,65 @@ func (m *BundledTxResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ExecResponses == nil { - m.ExecResponses = &any.Any{} + m.BundlerPaymentResponses = append(m.BundlerPaymentResponses, &any.Any{}) + if err := m.BundlerPaymentResponses[len(m.BundlerPaymentResponses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutionGasUsed", wireType) } - if err := m.ExecResponses.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ExecutionGasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExecutionGasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecutionResponses", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecutionResponses = append(m.ExecutionResponses, &any.Any{}) + if err := m.ExecutionResponses[len(m.ExecutionResponses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 2: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } diff --git a/x/auth/tx/config.go b/x/auth/tx/config.go index 24cec22034cd..9b10f645a621 100644 --- a/x/auth/tx/config.go +++ b/x/auth/tx/config.go @@ -193,8 +193,7 @@ func NewTxConfigWithOptions(protoCodec codec.Codec, configOptions ConfigOptions) dec, err := txdecode.NewDecoder(txdecode.Options{ SigningContext: configOptions.SigningContext, ProtoCodec: protoCodec, - }, - ) + }) if err != nil { return nil, err } diff --git a/x/group/go.mod b/x/group/go.mod index 057c32bca4a2..5c843444c2c1 100644 --- a/x/group/go.mod +++ b/x/group/go.mod @@ -34,8 +34,8 @@ require ( ) require ( - buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 // indirect - buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect cosmossdk.io/collections v0.4.1-0.20240802064046-23fac2f1b8ab // indirect; main cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // main cosmossdk.io/schema v0.3.1-0.20240930054013-7c6e0388a3f9 // indirect diff --git a/x/group/go.sum b/x/group/go.sum index 17bf7d55128e..d12a884c8a3a 100644 --- a/x/group/go.sum +++ b/x/group/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2 h1:90/4O5QkHb8EZdA2SAhueRzYw6u5ZHCPKtReFqshnTY= -buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.34.2-20240701160653-fedbb9acfd2f.2/go.mod h1:1+3gJj2NvZ1mTLAtHu+lMhOjGgQPiCKCeo+9MBww0Eo= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 h1:b7EEYTUHmWSBEyISHlHvXbJPqtKiHRuUignL1tsHnNQ= -buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc=