feat(accounts): re-introduce bundler (backport #21562) (#22365)

Co-authored-by: testinginprod <98415576+testinginprod@users.noreply.github.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2024-10-25 17:20:59 +02:00 committed by GitHub
parent 3564de3eab
commit b5e9e56d18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 1925 additions and 278 deletions

View File

@ -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.

View File

@ -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),

View File

@ -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

View File

@ -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=

View File

@ -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
}

View File

@ -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

View File

@ -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=

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -539,4 +539,131 @@ For example, given the following `genesis.json` file:
}
```
The accounts module will run the lockup account initialization message.
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
```

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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=

View File

@ -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 {

13
x/accounts/errors.go Normal file
View File

@ -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")
)

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)
}
})
}
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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;
}

View File

@ -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;
}

117
x/accounts/protoutils.go Normal file
View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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{}
}
if err := m.ExecResponses.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
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 2:
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ExecutionGasUsed", wireType)
}
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 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType)
}

View File

@ -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
}

View File

@ -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

View File

@ -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=