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 thepublic_key
parameter in the message. - After successful signature verification,
SA decorator
will remove temporary PubKey so thatSA module
can initiate contract with this account later.
- Since the account doesn't have PubKey yet, for signature verification,
- 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 toauth
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
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
calltype 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 typeSmartAccount
, if not, rejects it. - if
recover
call success, module will update new PubKey for account then save account toauth
module.
- The
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
andSA decorator
smart account
tx will go through theSA SetPubKey
decorator instead of theauth SetPubKey
decorator. This avoiding the check for similarity of Account Address and PubKey.
- tx will be identified as signed by smart account. If true, it will be redirected to
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
- Check if activation msg is the only one in the
- 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
- Check if activation msg is the only one in the
- validateSignerAddress(
signer
,msg
)- Calculate address based on
msg
parameters - Compare new address with the
signer's
address
- Calculate address based on
- setSignerPubkey(
signer
,pubkey
)- Set pubkey to
signer's
account
- Set pubkey to
func HandleSmartAccountTx(tx):
checkAllowedMsgs(tx.msgs)
callPreExecuteMsg(tx)
- checkAllowrdMsgs(
msgs
)- Check if all msg in
msgs
are allowed for smart-account by government
- Check if all msg in
- callPreExecuteMsg(
tx
)- Call pre_execute method of smartcontract that linked with account for
tx
pre-validation
- Call pre_execute method of smartcontract that linked with account for
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
- Call after_execute method of smartcontract that linked with account for
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
- Call (pre/after)_execute method of smartcontract that linked with account for nested
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 thepre_execute
andafter_execute
methodwhitelist_code_id
: determine which code_id can be instantiated as asmart account
disable_msgs_list
: list of msgs thatsmart 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
andafter_execute
method must not consume exceedmax_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,
}
x/smartaccount
types/auranw
is deprecated, used for v0.7.0 migration only, and will be removed later.
Documentation
¶
Index ¶
- func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *typesv1.GenesisState
- func GetSmartAccountTxSigner(ctx sdk.Context, sigTx authsigning.SigVerifiableTx, saKeeper sakeeper.Keeper) (*typesv1.SmartAccount, error)
- func GetValidActivateAccountMessage(sigTx authsigning.SigVerifiableTx) (*typesv1.MsgActivateAccount, error)
- func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState typesv1.GenesisState)
- type AfterTxDecorator
- type AppModule
- func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)
- func (AppModule) ConsensusVersion() uint64
- func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate
- func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage
- func (AppModule) GenerateGenesisState(simState *module.SimulationState)
- func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate
- func (am AppModule) Name() string
- func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg
- func (AppModule) QuerierRoute() string
- func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)
- func (am AppModule) RegisterServices(cfg module.Configurator)
- func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry)
- func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation
- type AppModuleBasic
- func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage
- func (AppModuleBasic) GetQueryCmd() *cobra.Command
- func (a AppModuleBasic) GetTxCmd() *cobra.Command
- func (AppModuleBasic) Name() string
- func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino)
- func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)
- func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry)
- func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)
- func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router)
- func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error
- type PostValidateAuthzTxDecorator
- type SetPubKeyDecorator
- type SmartAccountDecorator
- type ValidateAuthzTxDecorator
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExportGenesis ¶
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 ¶
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 ¶
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 ¶
ConsensusVersion implements ConsensusVersion.
func (AppModule) EndBlock ¶
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate
EndBlock executes all ABCI EndBlock logic respective to the capability module. It returns no validator updates.
func (AppModule) ExportGenesis ¶
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) ProposalContents ¶
func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg
ProposalContents doesn't return any content functions for governance proposals
func (AppModule) QuerierRoute ¶
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 ¶
func NewPostValidateAuthzTxDecorator ¶
func NewPostValidateAuthzTxDecorator(saKeeper sakeeper.Keeper) *PostValidateAuthzTxDecorator
func (*PostValidateAuthzTxDecorator) PostHandle ¶
type SetPubKeyDecorator ¶
type SetPubKeyDecorator struct {
// contains filtered or unexported fields
}
func NewSetPubKeyDecorator ¶
func NewSetPubKeyDecorator(saKeeper sakeeper.Keeper) *SetPubKeyDecorator
func (*SetPubKeyDecorator) AnteHandle ¶
type SmartAccountDecorator ¶
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 ¶
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