smartaccount

package
v0.7.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 29, 2024 License: Apache-2.0 Imports: 27 Imported by: 0

README

SMART-ACCOUNT

a smart account solution for CosmWasm-enabled chains.

In our context, smart account is a contract account associated with a public key, so it can be considered a programmable EOA. The difference is that unlike an EOA account where Address and PubKey must be the same, the PubKey of a smart account can be any key that the account owner set for it.

Our new account will have SmartAccount type instead of BaseAccount or other existing types.


Activation Account

Like EOA, users can create a local smart account and decide when to actually use it. This achived by using cosmwasm Instantiate2 method which will generate an account with predictable address.

  • Instantiate2 method params
    • sender: actor that signerd the messages.
    • admin: optional address that can execute migrations.
    • code_id: reference to the stored WASM code.
    • label: optional metadata to be stored with a contract instance.
    • msg: json encoded message to be passed to the contract on instantiation.
    • funds: coins that are transferred to the contract on instantiation.
    • salt: an arbitrary value provided by the sender. Size can be 1 to 64.
    • fix_msg: include the msg value into the hash for the predictable address. Default is false.

Query generate-account

Allows users to generate smart account addresses locally based on optional configuration

type QueryGenerateAccount struct{
    // reference to the stored WASM code, must be in whitelist
    code_id    uint64

    // an arbitrary value provided by the sender. Size can be 1 to 64.
    salt       []byte

    // json encoded message to be passed to the contract on instantiation
    init_msg   []byte

    // public key of this account, must be cosmos supported schemas
    public_key Any
}

Internally a address is built by Instantiate2 containing:

(len(checksum) | checksum | len(sender_address) | sender_address | len(salt) | salt| len(initMsg) | initMsg)

When create a new EOA, users can generate their private key locally and claim their account without sending any transactions. In our smart account case, using public_key as sender_address, a smart account can be claimed to be owned by the user who has configured the parameters to generate the account address.


Message activate-account

Allows users to activate their smart account. This message will take an account with type BaseAccount and convert it to SmartAccount type with preconfigured public key. Account address must be generated by QueryGenerateAccount query

type MessageActivateAccount struct {
    // AccountAddress is the actor who signs the message
    account_address string

    // reference to the stored WASM code, must be in whitelist
    code_id         uint64

    // an arbitrary value provided by the sender. Size can be 1 to 64.
    salt            []byte

    // json encoded message to be passed to the contract on instantiation
    init_msg        []byte

    // public key of this account, must be cosmos supported schemas
    public_key      Any
}

This message is signed with the user's private key and the signer is a pre-generated account. The module will calculate the expected address based on the input parameters then compare with the signer's address, so all configuration needs to match that used for the QueryGenerateAccount query.


To illustrate this in a graph:

             tx
              ↓
  ┌───── Antehandler ─────┐
  │   ┌───────────────┐   │
  │   │  decorator 0  │   │
  │   └───────────────┘   │
  │   ┌───────────────┐   │ Set temporary PubKey      ┌───────────────┐
  │   │  SA SetPubKey │---│-------------------------->|  auth module  |
  │   └───────────────┘   │                           └───────────────┘
  |      ...........      |
  │   ┌───────────────┐   │
  │   │SigVerification│   │
  │   └───────────────┘   │
  |      ...........      |
  │   ┌───────────────┐   │ Remove temporary Pubkey   ┌───────────────┐
  │   │  SA decorator │---│-------------------------->|  auth module  |
  │   └───────────────┘   │                           └───────────────┘
  │   ┌───────────────┐   │
  │   │  decorator 2  │   │
  │   └───────────────┘   │
  └───────────────────────┘
              ↓
      ┌────────────────┐
      │  msg activate  │ 
      └────────────────┘
              ↓
  ┌───────  module ───────┐
  │   ┌───────────────┐   │   instantiate contract   ┌───────────────┐
  │   │   SA module   │---│------------------------->|  wasmd module |
  │   └───────────────┘   │                          └───────────────┘
  └───────────────────────┘
              ↓
            done
  • AnteHandler
    • Since the account doesn't have PubKey yet, for signature verification, SA SetPubKey will set a temporary PubKey for this account using the public_key parameter in the message.
    • After successful signature verification, SA decorator will remove temporary PubKey so that SA module can initiate contract with this account later.
  • SA module
    • if the message meets all the checks, the module initiates a contract based on its parameters. The new contract will be linked to the pre-generated account (contract address will be same as account address). The module will then convert account to type SmartAccount and set PubKey for it. Finnaly, save it to auth module.

Required

  • valid parameters.
  • The account must have received the funds, so it can exist on-chain as BaseAccount type with an account number, sequence and empty public key before activated.
  • The account address was not used to initiate any smart contract before.
  • In some cases, we also allow reactivation of activated accounts that are not linked to any smart contracts.
  • code_id must be in whitelist.
  • actiavte message is the only message in tx.

Recover Account

We provide a smart account recovery way in case the user forgets the account's private key or wants to change the new one. Recovery is simply changing the PubKey of an account of type SmartAccount with the new setting. This is not a required function so users can choose whether their account is recoverable or not.


Message recover

The caller specifies the address to recover, the public key and provides credentials to prove they have the authority to perform the recovery.

type MsgRecover struct{
  // Sender is the actor who signs the message
  creator     string

  // smart-account address that want to recover
  address     string

  // New PubKey using for signature verification of this account, 
  // must be cosmos supported schemas
  public_key  Any

  // Credentials
  credentials string
}

The module makes a call to the recover method of contract that linked to smart account. If the message parameters meet the verification logic implemented in the contract, the smart account will be updated with the new PubKey.

  • recover call
    type RecoverTx struct {
        Caller      string 
        PubKey      []byte
        Credentials []byte
    }
    

To illustrate this in a graph:

             tx
              ↓
  ┌───── Antehandler ─────┐
  │   ┌───────────────┐   │
  │   │  decorator 0  │   │
  │   └───────────────┘   │
  │   ┌───────────────┐   │
  │   │  decorator 1  │   │
  │   └───────────────┘   │ 
  │   ┌───────────────┐   │
  │   │  decorator 2  │   │
  │   └───────────────┘   │
  └───────────────────────┘
              ↓
      ┌────────────────┐
      │  msg recover   │ 
      └────────────────┘
              ↓
  ┌───────  module ───────┐
  │   ┌───────────────┐   │   `recover` sudo      ┌───────────────┐
  │   │   SA module   │---│---------------------->|  wasmd module |
  │   └───────────────┘   │                       └───────────────┘
  └───────────────────────┘
              ↓
            done
  • SA Module
    • The SA module checks if the requested account is of type SmartAccount, if not, rejects it.
    • if recover call success, module will update new PubKey for account then save account to auth module.

Required

  • valid parameters.
  • Account with address must exists on-chain and has type SmartAccount.
  • Account enables recovery function by implementing recover method in sudo entry point of linked smart contract.

Smart account Tx

Users can use smart accounts to sign and broadcast tx just like EOA.

Firstly, for pre-validation tx, the AnteHandler will trigger the smartcontract's pre_execute method, which is linked to the account. The user won't be charged for gas if it fails because the tx won't be allowed to join the mempool. Finnaly, following the execution of tx, the after_execute method will be used to ascertain the success or failure of tx.

The user won't be charged for the pre_execute or after execute calls because their consuming gas won't count to total gas used. However, they are limited by the MaxGasExecute param.


To illustrate this in a graph:

             tx
              ↓
  ┌───── Antehandler ─────┐
  │   ┌───────────────┐   │
  │   │ SA SetPubKey  │   │
  │   └───────────────┘   │
  │   ┌───────────────┐   │   `pre_execute` execute   ┌───────────────┐
  │   │ SA decorator  │---│-------------------------->|  wasmd module |
  │   └───────────────┘   │                           └───────────────┘
  └───────────────────────┘
              ↓
      ┌────────────────┐
      │      msg 0     │
      └────────────────┘
          ...........
      ┌────────────────┐
      │      msg 1     │
      └────────────────┘
              ↓
  ┌───────  module ───────┐
  │   ┌───────────────┐   │
  │   │    module 1   │   │
  │   └───────────────┘   │
  │   ┌───────────────┐   │
  │   │    module 2   │   │
  │   └───────────────┘   │
  └───────────────────────┘
              ↓
  ┌───── Posthandler ─────┐
  │   ┌───────────────┐   │   `after_execute` execute ┌───────────────┐
  │   │ SA decorator  │---│-------------------------->|  wasmd module |
  │   └───────────────┘   │                           └───────────────┘
  └───────────────────────┘
            done
  • Antehandler
    • tx will be identified as signed by smart account. If true, it will be redirected to SA SetPubKey and SA decorator
    • smart account tx will go through the SA SetPubKey decorator instead of the auth SetPubKey decorator. This avoiding the check for similarity of Account Address and PubKey.

Required

  • Signer is account with type SmartAccount

Ante/Post Handler

SetPubKey Decorator

Alternative for auth's SetPubkey Decorator

AnteHandler

func (d SetPubKeyDecorator) AnteHandle():
    if isActivationMsg:
        validateActivationMsg(tx.msgs)

        setTemporaryPubkey(tx.signer, tx.msgs[0].pubkey)
    else:
        if isSmartAccountTx:
            checkNilPubkey(tx.signer)
        else:
            return authante.SetPubkeyDecorator() // if is normal tx, run auth's SetPubkey Decorator

    return next() // run next ante handler
  • validateActivationMsg(msgs)
    • Check if activation msg is the only one in the msgs
  • setTemporaryPubkey(signer, pubkey)
    • Set pubkey for signer
  • checkNilPubkey(signer)
    • Ensure signer account already has pubkey
SmartAccount Decorator

Run after auth's SigVerification Decorator for handling smart-account's tx

AnteHandler

func (d SmartAccountDecorator) AnteHandle():
    if isActivationMsg:
        HandleActivationMsg(tx) // run HandleActivationMsg func
    elif isSmartAccountTx:
        HandleSmartAccountTx(tx) // run HandleSmartAccountTx func

    return next() // run next ante handler
func HandleActivationMsg(tx):
    validateActivationMsg(tx.msgs)

    validateSignerAddress(tx.signer, tx.msgs[0])

    setSignerPubkey(tx.signer, tx.msgs.pubkey)
  • validateActivationMsg(msgs)
    • Check if activation msg is the only one in the msgs
  • validateSignerAddress(signer, msg)
    • Calculate address based on msg parameters
    • Compare new address with the signer's address
  • setSignerPubkey(signer, pubkey)
    • Set pubkey to signer's account
func HandleSmartAccountTx(tx):

    checkAllowedMsgs(tx.msgs)

    callPreExecuteMsg(tx)
  • checkAllowrdMsgs(msgs)
    • Check if all msg in msgs are allowed for smart-account by government
  • callPreExecuteMsg(tx)
    • Call pre_execute method of smartcontract that linked with account for tx pre-validation

PostHandler

func (d AfterTxDecorator) PostHandle():
    callAfterExecuteMsg(tx)

    return next() // run next post handler
  • callAfterExecuteMsg(tx)
    • Call after_execute method of smartcontract that linked with account for tx after-validation
ValidateAuthzTx Decorator

Run after SmartAccountTx Decorator for handling authz nested smart-account's msgs

(Ante/Post)Handler

func (d ValidateAuthzDecorator) (Ante/Post)Handle():
    for msg in tx.msgs:
        if msg is authz.MsgExec:
            validateNestedSmartAccountMsgs(msg.GetMessages())
    
    return next() // run next ante handler

func validateNestedSmartAccountMsgs(msgs):
    for msg in msgs:
        if msg.signer is typeOf(SmartAccount):
            call(Pre/After)ExecuteNestedMsg(msg)

        if msg is authz.MsgExec:
            validateNestedSmartAccountMsgs(msg.GetMessages())
  • callPreExecuteNestedMsg(msg)
    • Call (pre/after)_execute method of smartcontract that linked with account for nested msg (pre/after)-validation

Params

Parameters are updatable by the module's authority, typically set to the gov module account.

  • max_gas_execute: limit how much gas can be consumed by the pre_execute and after_execute method
  • whitelist_code_id: determine which code_id can be instantiated as a smart account
  • disable_msgs_list: list of msgs that smart account not allowed to execute

WASM

To be considered as smart account, smart contract linked with account must implement sudo methods, after_execute and pre_execute:

// sudo method
struct AfterExecute {
    //list of messages in transaction 
    pub msgs: Vec<Any>,
    // fee information of transaction
    pub call_info: CallInfo,
    // Is tx executed throught authz msg
    pub is_authz: bool
}

// sudo method
struct PreExecute { 
    //list of messages in transaction 
    pub msgs: Vec<Any>,
    // fee information of transaction
    pub call_info: CallInfo,
    // Is tx executed throught authz msg
    pub is_authz: bool
}
  • Any: is json encoded message
    struct MsgData {
        pub type_url: String, // url type of message
        pub value:    Binary, // value of message
        // etc.
        //  MsgData {
        //      type_url: "/cosmos.bank.v1beta1.MsgSend",
        //      value: [...]
        //  }
    }
    
  • CallInfo: is tx's fee information
    pub struct CallInfo {
        pub fee: Vec<Coin>,
        pub gas: u64,
        pub fee_payer: String,
        pub fee_granter: String,
    }
    
  • pre_execute and after_execute method must not consume exceed max_gas_execute gas

Optional sudo method recover that activate the smart account recovery function

// sudo method
struct Recover {
    pub caller: String,
    pub pub_key: Binary,
    pub credentials: Binary,
}

smart account samples

x/smartaccount

  • types/auranw is deprecated, used for v0.7.0 migration only, and will be removed later.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportGenesis

func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *typesv1.GenesisState

ExportGenesis returns the capability module's exported genesis.

func GetSmartAccountTxSigner

func GetSmartAccountTxSigner(ctx sdk.Context, sigTx authsigning.SigVerifiableTx, saKeeper sakeeper.Keeper) (*typesv1.SmartAccount, error)

Return tx' signer as SmartAccount, if not return nil

func GetValidActivateAccountMessage

func GetValidActivateAccountMessage(sigTx authsigning.SigVerifiableTx) (*typesv1.MsgActivateAccount, error)

func InitGenesis

func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState typesv1.GenesisState)

InitGenesis initializes the capability module's state from a provided genesis state.

Types

type AfterTxDecorator

type AfterTxDecorator struct {
	// contains filtered or unexported fields
}

------------------------- AfterTx Decorator ------------------------- \\

func NewAfterTxDecorator

func NewAfterTxDecorator(saKeeper sakeeper.Keeper) *AfterTxDecorator

func (AfterTxDecorator) PostHandle

func (d AfterTxDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error)

type AppModule

type AppModule struct {
	AppModuleBasic
	// contains filtered or unexported fields
}

AppModule implements the AppModule interface for the capability module.

func NewAppModule

func NewAppModule(
	cdc codec.Codec,
	keeper keeper.Keeper,
	contractKeeper *wasmkeeper.PermissionedKeeper,
	accountKeeper types.AccountKeeper,
) AppModule

func (AppModule) BeginBlock

func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)

BeginBlock executes all ABCI BeginBlock logic respective to the capability module.

func (AppModule) ConsensusVersion

func (AppModule) ConsensusVersion() uint64

ConsensusVersion implements ConsensusVersion.

func (AppModule) EndBlock

EndBlock executes all ABCI EndBlock logic respective to the capability module. It returns no validator updates.

func (AppModule) ExportGenesis

func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage

ExportGenesis returns the capability module's exported genesis state as raw JSON bytes.

func (AppModule) GenerateGenesisState

func (AppModule) GenerateGenesisState(simState *module.SimulationState)

GenerateGenesisState creates a randomized GenState of the module

func (AppModule) InitGenesis

func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate

InitGenesis performs the capability module's genesis initialization It returns no validator updates.

func (AppModule) Name

func (am AppModule) Name() string

Name returns the capability module's name.

func (AppModule) ProposalContents

ProposalContents doesn't return any content functions for governance proposals

func (AppModule) QuerierRoute

func (AppModule) QuerierRoute() string

QuerierRoute returns the capability module's query routing key.

func (AppModule) RegisterInvariants

func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)

RegisterInvariants registers the capability module's invariants.

func (AppModule) RegisterServices

func (am AppModule) RegisterServices(cfg module.Configurator)

RegisterServices registers a GRPC query service to respond to the module-specific GRPC queries.

func (AppModule) RegisterStoreDecoder

func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry)

RegisterStoreDecoder registers a decoder

func (AppModule) WeightedOperations

func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation

WeightedOperations returns the all the gov module operations with their respective weights.

type AppModuleBasic

type AppModuleBasic struct {
	// contains filtered or unexported fields
}

AppModuleBasic implements the AppModuleBasic interface for the capability module.

func NewAppModuleBasic

func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic

func (AppModuleBasic) DefaultGenesis

func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage

DefaultGenesis returns the capability module's default genesis state.

func (AppModuleBasic) GetQueryCmd

func (AppModuleBasic) GetQueryCmd() *cobra.Command

GetQueryCmd returns the capability module's root query command.

func (AppModuleBasic) GetTxCmd

func (a AppModuleBasic) GetTxCmd() *cobra.Command

GetTxCmd returns the capability module's root tx command.

func (AppModuleBasic) Name

func (AppModuleBasic) Name() string

Name returns the capability module's name.

func (AppModuleBasic) RegisterCodec

func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino)

func (AppModuleBasic) RegisterGRPCGatewayRoutes

func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)

RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.

func (AppModuleBasic) RegisterInterfaces

func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry)

RegisterInterfaces registers the module's interface types

func (AppModuleBasic) RegisterLegacyAminoCodec

func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)

func (AppModuleBasic) RegisterRESTRoutes

func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router)

RegisterRESTRoutes registers the capability module's REST service handlers.

func (AppModuleBasic) ValidateGenesis

func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error

ValidateGenesis performs genesis state validation for the capability module.

type PostValidateAuthzTxDecorator

type PostValidateAuthzTxDecorator struct {
	SaKeeper sakeeper.Keeper
}

func NewPostValidateAuthzTxDecorator

func NewPostValidateAuthzTxDecorator(saKeeper sakeeper.Keeper) *PostValidateAuthzTxDecorator

func (*PostValidateAuthzTxDecorator) PostHandle

func (d *PostValidateAuthzTxDecorator) PostHandle(
	ctx sdk.Context,
	tx sdk.Tx,
	simulate bool,
	success bool,
	next sdk.PostHandler,
) (newCtx sdk.Context, err error)

type SetPubKeyDecorator

type SetPubKeyDecorator struct {
	// contains filtered or unexported fields
}

func NewSetPubKeyDecorator

func NewSetPubKeyDecorator(saKeeper sakeeper.Keeper) *SetPubKeyDecorator

func (*SetPubKeyDecorator) AnteHandle

func (d *SetPubKeyDecorator) AnteHandle(
	ctx sdk.Context,
	tx sdk.Tx,
	simulate bool,
	next sdk.AnteHandler,
) (newCtx sdk.Context, err error)

type SmartAccountDecorator

type SmartAccountDecorator struct {
	SaKeeper sakeeper.Keeper
}

func NewSmartAccountDecorator

func NewSmartAccountDecorator(saKeeper sakeeper.Keeper) *SmartAccountDecorator

func (*SmartAccountDecorator) AnteHandle

func (d *SmartAccountDecorator) AnteHandle(
	ctx sdk.Context,
	tx sdk.Tx,
	simulate bool,
	next sdk.AnteHandler,
) (newCtx sdk.Context, err error)

AnteHandle is used for performing basic validity checks on a transaction such that it can be thrown out of the mempool.

type ValidateAuthzTxDecorator

type ValidateAuthzTxDecorator struct {
	SaKeeper sakeeper.Keeper
}

func NewValidateAuthzTxDecorator

func NewValidateAuthzTxDecorator(saKeeper sakeeper.Keeper) *ValidateAuthzTxDecorator

func (*ValidateAuthzTxDecorator) AnteHandle

func (d *ValidateAuthzTxDecorator) AnteHandle(
	ctx sdk.Context,
	tx sdk.Tx,
	simulate bool,
	next sdk.AnteHandler,
) (newCtx sdk.Context, err error)

If smartaccount messages is executed throught MsgAuthzExec, SmartAccountDecorator cannot detect and validate these messages using AuthzTxDecorator AnteHandler to provide validation for nested smartaccount msgs

Directories

Path Synopsis
client
cli
v1beta1
Package v1beta1 is a reverse proxy.
Package v1beta1 is a reverse proxy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL