Skip to content

Commit

Permalink
feat(accounts): make x/accounts more depinject friendly (#21928)
Browse files Browse the repository at this point in the history
  • Loading branch information
testinginprod authored Sep 30, 2024
1 parent 7c6e038 commit 7ce403f
Show file tree
Hide file tree
Showing 21 changed files with 251 additions and 82 deletions.
2 changes: 2 additions & 0 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"cosmossdk.io/x/accounts/accountstd"
baseaccount "cosmossdk.io/x/accounts/defaults/base"
lockup "cosmossdk.io/x/accounts/defaults/lockup"
"cosmossdk.io/x/accounts/defaults/multisig"
"cosmossdk.io/x/accounts/testing/account_abstraction"
"cosmossdk.io/x/accounts/testing/counter"
"cosmossdk.io/x/authz"
Expand Down Expand Up @@ -315,6 +316,7 @@ func NewSimApp(
accountstd.AddAccount(lockup.PERIODIC_LOCKING_ACCOUNT, lockup.NewPeriodicLockingAccount),
accountstd.AddAccount(lockup.DELAYED_LOCKING_ACCOUNT, lockup.NewDelayedLockingAccount),
accountstd.AddAccount(lockup.PERMANENT_LOCKING_ACCOUNT, lockup.NewPermanentLockingAccount),
accountstd.AddAccount("multisig", multisig.NewAccount),
// PRODUCTION: add
baseaccount.NewAccount("base", txConfig.SignModeHandler(), baseaccount.WithSecp256K1PubKey()),
)
Expand Down
24 changes: 24 additions & 0 deletions simapp/app_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
"cosmossdk.io/depinject"
"cosmossdk.io/log"
"cosmossdk.io/x/accounts"
basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject"
lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject"
multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject"
bankkeeper "cosmossdk.io/x/bank/keeper"
circuitkeeper "cosmossdk.io/x/circuit/keeper"
consensuskeeper "cosmossdk.io/x/consensus/keeper"
Expand Down Expand Up @@ -155,6 +158,27 @@ func NewSimApp(
// For providing a custom inflation function for x/mint add here your
// custom function that implements the minttypes.MintFn interface.
),
depinject.Provide(
// inject desired account types:
multisigdepinject.ProvideAccount,
basedepinject.ProvideAccount,
lockupdepinject.ProvideAllLockupAccounts,

// provide base account options
basedepinject.ProvideSecp256K1PubKey,
// if you want to provide a custom public key you
// can do it from here.
// Example:
// basedepinject.ProvideCustomPubkey[Ed25519PublicKey]()
//
// You can also provide a custom public key with a custom validation function:
//
// basedepinject.ProvideCustomPubKeyAndValidationFunc(func(pub Ed25519PublicKey) error {
// if len(pub.Key) != 64 {
// return fmt.Errorf("invalid pub key size")
// }
// })
),
)
)

Expand Down
2 changes: 1 addition & 1 deletion simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (

require (
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000
google.golang.org/grpc v1.67.0
)

Expand All @@ -62,7 +63,6 @@ require (
cloud.google.com/go/storage v1.43.0 // indirect
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/schema v0.3.0 // indirect
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
Expand Down
22 changes: 22 additions & 0 deletions simapp/v2/app_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/runtime/v2"
"cosmossdk.io/store/v2/root"
basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject"
lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject"
multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject"
upgradekeeper "cosmossdk.io/x/upgrade/keeper"

"github.com/cosmos/cosmos-sdk/client"
Expand Down Expand Up @@ -119,6 +122,25 @@ func NewSimApp[T transaction.Tx](
codec.ProvideAddressCodec,
codec.ProvideProtoCodec,
codec.ProvideLegacyAmino,
// inject desired account types:
multisigdepinject.ProvideAccount,
basedepinject.ProvideAccount,
lockupdepinject.ProvideAllLockupAccounts,

// provide base account options
basedepinject.ProvideSecp256K1PubKey,
// if you want to provide a custom public key you
// can do it from here.
// Example:
// basedepinject.ProvideCustomPubkey[Ed25519PublicKey]()
//
// You can also provide a custom public key with a custom validation function:
//
// basedepinject.ProvideCustomPubKeyAndValidationFunc(func(pub Ed25519PublicKey) error {
// if len(pub.Key) != 64 {
// return fmt.Errorf("invalid pub key size")
// }
// })
),
depinject.Invoke(
std.RegisterInterfaces,
Expand Down
9 changes: 6 additions & 3 deletions simapp/v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ require (
google.golang.org/protobuf v1.34.2
)

require (
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000
)

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
Expand All @@ -59,9 +65,6 @@ require (
cosmossdk.io/server/v2/appmanager v0.0.0-20240802110823-cffeedff643d // indirect
cosmossdk.io/server/v2/stf v0.0.0-20240708142107-25e99c54bac1 // indirect
cosmossdk.io/store v1.1.1 // indirect
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5 // indirect
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/tx v0.13.5 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
3 changes: 1 addition & 2 deletions tests/e2e/accounts/multisig/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"cosmossdk.io/core/transaction"
"cosmossdk.io/math"
"cosmossdk.io/simapp"
multisigaccount "cosmossdk.io/x/accounts/defaults/multisig"
v1 "cosmossdk.io/x/accounts/defaults/multisig/v1"
"cosmossdk.io/x/bank/testutil"

Expand Down Expand Up @@ -76,7 +75,7 @@ func (s *E2ETestSuite) initAccount(ctx context.Context, sender []byte, membersPo
members = append(members, &v1.Member{Address: addrStr, Weight: power})
}

_, accountAddr, err := s.app.AccountsKeeper.Init(ctx, multisigaccount.MULTISIG_ACCOUNT, sender,
_, accountAddr, err := s.app.AccountsKeeper.Init(ctx, "multisig", sender,
&v1.MsgInit{
Members: members,
Config: &v1.Config{
Expand Down
93 changes: 91 additions & 2 deletions x/accounts/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,95 @@
# x/accounts
# x/accounts Module

The x/accounts module provides module and facilities for writing smart cosmos-sdk accounts.
The x/accounts module enhances the Cosmos SDK by providing tools and infrastructure for creating advanced smart accounts.

# The Authentication Interface

x/accounts introduces the `Authentication` interface, allowing for flexible transaction (TX) authentication beyond traditional public key cryptography.

Chain developers can implement tailored authentication methods for their accounts. Any account that implements the `Authentication` interface can be authenticated within a transaction.

To implement the `Authentication` interface in x/accounts, an account must expose an execution handler capable of processing a specific message type.

The key message type for authentication is `MsgAuthenticate`, which is defined in the module's protocol buffer files:

[interfaces/account_abstraction/v1/interface.proto](./proto/cosmos/accounts/interfaces/account_abstraction/v1/interface.proto)

## Authentication Mechanism

### AnteHandler in the SDK

The Cosmos SDK utilizes an `AnteHandler` to verify transaction (TX) integrity. Its primary function is to ensure that the messages within a transaction are correctly signed by the purported sender.

### Authentication Flow for x/accounts Module

When the `AnteHandler` identifies that a message sender (and transaction signer) belongs to the x/accounts module, it delegates the authentication process to that module.

#### Authentication Interface Requirement

For successful authentication, the account must implement the `Authentication` interface. If an account fails to implement this interface, it's considered non-externally owned, resulting in transaction rejection.

##### Sequence Diagram

```mermaid
graph TD
A[Tx Is Received] --> B[Execute Signature Verification Ante Handler]
B --> D{Is signer an x/accounts account?}
D -->|No| E[Continue with signature verification ante handler]
D -->|Yes| F{Does account handle MsgAuthenticate?}
F -->|No| G[Fail TX: Non-externally owned account]
F -->|Yes| H[Invoke signer account MsgAuthenticate]
E --> I[End]
G --> I
H --> I
```


## Implementing the Authentication Interface

To implement the Authentication interface, an account must handle the execution of `MsgAuthenticate`. Here's an example of how to do this:

```go
package base

import (
"context"
"errors"
aa_interface_v1 "github.com/cosmos/cosmos-sdk/x/accounts/interfaces/account_abstraction/v1"
"github.com/cosmos/cosmos-sdk/x/accounts/std"
)

// Account represents a base account structure
type Account struct {
// Account fields...
}

// Authenticate implements the authentication flow for an abstracted base account.
func (a Account) Authenticate(ctx context.Context, msg *aa_interface_v1.MsgAuthenticate) (*aa_interface_v1.MsgAuthenticateResponse, error) {
if !accountstd.SenderIsAccountsModule(ctx) {
return nil, errors.New("unauthorized: only accounts module is allowed to call this")
}
// Implement your authentication logic here
// ...
return &aa_interface_v1.MsgAuthenticateResponse{}, nil
}

// RegisterExecuteHandlers registers the execution handlers for the account.
func (a Account) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
accountstd.RegisterExecuteHandler(builder, a.SwapPubKey) // Other handlers
accountstd.RegisterExecuteHandler(builder, a.Authenticate) // Implements the Authentication interface
}
```

### Key Implementation Points

1. **Sender Verification**: Always verify that the sender is the x/accounts module. This prevents unauthorized accounts from triggering authentication.
2. **Authentication Safety**: Ensure your authentication mechanism is secure:
- Prevent replay attacks by making it impossible to reuse the same action with the same signature.


#### Implementation example

Please find an example [here](./defaults/base/account.go).

# Supporting Custom Accounts in the x/auth gRPC Server

Expand Down
10 changes: 10 additions & 0 deletions x/accounts/accountstd/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ type InitBuilder = implementation.InitBuilder
// AccountCreatorFunc is the exported type of AccountCreatorFunc.
type AccountCreatorFunc = implementation.AccountCreatorFunc

func DIAccount[A Interface](name string, constructor func(deps Dependencies) (A, error)) DepinjectAccount {
return DepinjectAccount{MakeAccount: AddAccount(name, constructor)}
}

type DepinjectAccount struct {
MakeAccount AccountCreatorFunc
}

func (DepinjectAccount) IsManyPerContainerType() {}

// Dependencies is the exported type of Dependencies.
type Dependencies = implementation.Dependencies

Expand Down
2 changes: 2 additions & 0 deletions x/accounts/defaults/base/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var (

type Option func(a *Account)

func (Option) IsManyPerContainerType() {}

func NewAccount(name string, handlerMap *signing.HandlerMap, options ...Option) accountstd.AccountCreatorFunc {
return func(deps accountstd.Dependencies) (string, accountstd.Interface, error) {
acc := Account{
Expand Down
31 changes: 31 additions & 0 deletions x/accounts/defaults/base/depinject/depinject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package basedepinject

import (
"cosmossdk.io/depinject"
"cosmossdk.io/x/accounts/accountstd"
"cosmossdk.io/x/accounts/defaults/base"
"cosmossdk.io/x/tx/signing"
)

type Inputs struct {
depinject.In

SignHandlersMap *signing.HandlerMap
Options []base.Option
}

func ProvideAccount(in Inputs) accountstd.DepinjectAccount {
return accountstd.DepinjectAccount{MakeAccount: base.NewAccount("base", in.SignHandlersMap, in.Options...)}
}

func ProvideSecp256K1PubKey() base.Option {
return base.WithSecp256K1PubKey()
}

func ProvideCustomPubkey[T any, PT base.PubKeyG[T]]() base.Option {
return base.WithPubKey[T, PT]()
}

func ProvideCustomPubKeyAndValidationFunc[T any, PT base.PubKeyG[T]](validateFn func(PT) error) base.Option {
return base.WithPubKeyWithValidationFunc(validateFn)
}
10 changes: 1 addition & 9 deletions x/accounts/defaults/base/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
cosmossdk.io/api v0.7.6
cosmossdk.io/collections v0.4.0
cosmossdk.io/core v1.0.0-alpha.3
cosmossdk.io/depinject v1.0.0
cosmossdk.io/x/accounts v0.0.0-20240913065641-0064ccbce64e
cosmossdk.io/x/tx v0.13.3
github.com/cosmos/cosmos-sdk v0.53.0
Expand All @@ -19,7 +20,6 @@ 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/core/testing v0.0.0-20240923163230-04da382a9f29 // indirect
cosmossdk.io/depinject v1.0.0 // indirect
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/log v1.4.1 // indirect
cosmossdk.io/math v1.3.0 // indirect
Expand Down Expand Up @@ -174,15 +174,7 @@ replace (
cosmossdk.io/collections => ../../../../collections // TODO tag new collections ASAP
cosmossdk.io/store => ../../../../store
cosmossdk.io/x/accounts => ../../.
cosmossdk.io/x/accounts/defaults/multisig => ../multisig
cosmossdk.io/x/auth => ../../../auth
cosmossdk.io/x/bank => ../../../bank
cosmossdk.io/x/consensus => ../../../consensus
cosmossdk.io/x/distribution => ../../../distribution
cosmossdk.io/x/gov => ../../../gov
cosmossdk.io/x/mint => ../../../mint
cosmossdk.io/x/protocolpool => ../../../protocolpool
cosmossdk.io/x/slashing => ../../../slashing
cosmossdk.io/x/staking => ../../../staking
cosmossdk.io/x/tx => ../../../tx
)
31 changes: 31 additions & 0 deletions x/accounts/defaults/lockup/depinject/depinject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package lockupdepinject

import (
"cosmossdk.io/x/accounts/accountstd"
"cosmossdk.io/x/accounts/defaults/lockup"
)

func ProvideAllLockupAccounts() []accountstd.DepinjectAccount {
return []accountstd.DepinjectAccount{
ProvidePeriodicLockingAccount(),
ProvideContinuousLockingAccount(),
ProvidePermanentLockingAccount(),
ProvideDelayedLockingAccount(),
}
}

func ProvideContinuousLockingAccount() accountstd.DepinjectAccount {
return accountstd.DIAccount(lockup.CONTINUOUS_LOCKING_ACCOUNT, lockup.NewContinuousLockingAccount)
}

func ProvidePeriodicLockingAccount() accountstd.DepinjectAccount {
return accountstd.DIAccount(lockup.PERIODIC_LOCKING_ACCOUNT, lockup.NewPeriodicLockingAccount)
}

func ProvideDelayedLockingAccount() accountstd.DepinjectAccount {
return accountstd.DIAccount(lockup.DELAYED_LOCKING_ACCOUNT, lockup.NewDelayedLockingAccount)
}

func ProvidePermanentLockingAccount() accountstd.DepinjectAccount {
return accountstd.DIAccount(lockup.PERMANENT_LOCKING_ACCOUNT, lockup.NewPermanentLockingAccount)
}
7 changes: 1 addition & 6 deletions x/accounts/defaults/lockup/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,8 @@ replace (
cosmossdk.io/collections => ../../../../collections // TODO tag new collections ASAP
cosmossdk.io/store => ../../../../store
cosmossdk.io/x/accounts => ../../.
cosmossdk.io/x/accounts/defaults/multisig => ../multisig
cosmossdk.io/x/bank => ../../../bank
cosmossdk.io/x/consensus => ../../../consensus
cosmossdk.io/x/distribution => ../../../distribution
cosmossdk.io/x/gov => ../../../gov
cosmossdk.io/x/mint => ../../../mint
cosmossdk.io/x/protocolpool => ../../../protocolpool
cosmossdk.io/x/slashing => ../../../slashing
cosmossdk.io/x/staking => ../../../staking
cosmossdk.io/x/tx => ../../../tx
)
2 changes: 0 additions & 2 deletions x/accounts/defaults/lockup/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE=
cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
cosmossdk.io/schema v0.3.0 h1:01lcaM4trhzZ1HQTfTV8z6Ma1GziOZ/YmdzBN3F720c=
cosmossdk.io/schema v0.3.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g=
cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
Expand Down
2 changes: 0 additions & 2 deletions x/accounts/defaults/multisig/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
)

var MULTISIG_ACCOUNT = "multisig-account"

var (
MembersPrefix = collections.NewPrefix(0)
SequencePrefix = collections.NewPrefix(1)
Expand Down
Loading

0 comments on commit 7ce403f

Please sign in to comment.