Skip to content

Commit

Permalink
Add extension points to the CLI (#477)
Browse files Browse the repository at this point in the history
* Extract interfaces for genesis state in CLI

* Add more godoc

* Review feedback
  • Loading branch information
alpe authored Mar 31, 2021
1 parent d9ca5d8 commit 8109bba
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 43 deletions.
11 changes: 6 additions & 5 deletions cmd/wasmd/genwasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ func AddGenesisWasmMsgCmd(defaultNodeHome string) *cobra.Command {
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
genesisIO := wasmcli.NewDefaultGenesisIO()
txCmd.AddCommand(
wasmcli.GenesisStoreCodeCmd(defaultNodeHome),
wasmcli.GenesisInstantiateContractCmd(defaultNodeHome),
wasmcli.GenesisExecuteContractCmd(defaultNodeHome),
wasmcli.GenesisListContractsCmd(defaultNodeHome),
wasmcli.GenesisListCodesCmd(defaultNodeHome),
wasmcli.GenesisStoreCodeCmd(defaultNodeHome, genesisIO),
wasmcli.GenesisInstantiateContractCmd(defaultNodeHome, genesisIO),
wasmcli.GenesisExecuteContractCmd(defaultNodeHome, genesisIO),
wasmcli.GenesisListContractsCmd(defaultNodeHome, genesisIO),
wasmcli.GenesisListCodesCmd(defaultNodeHome, genesisIO),
)
return txCmd

Expand Down
105 changes: 70 additions & 35 deletions x/wasm/client/cli/genesis_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,24 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
)

// GenesisReader reads genesis data. Extension point for custom genesis state readers.
type GenesisReader interface {
ReadWasmGenesis(cmd *cobra.Command) (*GenesisData, error)
}

// GenesisMutator extension point to modify the wasm module genesis state.
// This gives flexibility to customize the data structure in the genesis file a bit.
type GenesisMutator interface {
// AlterModuleState loads the genesis from the default or set home dir,
// unmarshalls the wasm module section into the object representation
// calls the callback function to modify it
// and marshals the modified state back into the genesis file
AlterWasmModuleState(cmd *cobra.Command, callback func(state *types.GenesisState, appState map[string]json.RawMessage) error) error
}

// GenesisStoreCodeCmd cli command to add a `MsgStoreCode` to the wasm section of the genesis
// that is executed on block 0.
func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command {
func GenesisStoreCodeCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command {
cmd := &cobra.Command{
Use: "store [wasm file] --source [source] --builder [builder] --run-as [owner_address_or_key_name]\",",
Short: "Upload a wasm binary",
Expand All @@ -45,7 +60,7 @@ func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command {
return err
}

return alterModuleState(cmd, func(state *types.GenesisState, _ map[string]json.RawMessage) error {
return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, _ map[string]json.RawMessage) error {
state.GenMsgs = append(state.GenMsgs, types.GenesisState_GenMsgs{
Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: &msg},
})
Expand All @@ -67,7 +82,7 @@ func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command {

// GenesisInstantiateContractCmd cli command to add a `MsgInstantiateContract` to the wasm section of the genesis
// that is executed on block 0.
func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command {
func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command {
cmd := &cobra.Command{
Use: "instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --run-as [address] --admin [address,optional] --amount [coins,optional]",
Short: "Instantiate a wasm contract",
Expand All @@ -86,7 +101,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command {
return err
}

return alterModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
// simple sanity check that sender has some balance although it may be consumed by appState previous message already
switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); {
case err != nil:
Expand Down Expand Up @@ -134,7 +149,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command {

// GenesisInstantiateContractCmd cli command to add a `MsgExecuteContract` to the wasm section of the genesis
// that is executed on block 0.
func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command {
func GenesisExecuteContractCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command {
cmd := &cobra.Command{
Use: "execute [contract_addr_bech32] [json_encoded_send_args] --run-as [address] --amount [coins,optional]",
Short: "Execute a command on a wasm contract",
Expand All @@ -153,7 +168,7 @@ func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command {
return err
}

return alterModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error {
// simple sanity check that sender has some balance although it may be consumed by appState previous message already
switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); {
case err != nil:
Expand Down Expand Up @@ -184,17 +199,17 @@ func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command {

// GenesisListCodesCmd cli command to list all codes stored in the genesis wasm.code section
// as well as from messages that are queued in the wasm.genMsgs section.
func GenesisListCodesCmd(defaultNodeHome string) *cobra.Command {
func GenesisListCodesCmd(defaultNodeHome string, genReader GenesisReader) *cobra.Command {
cmd := &cobra.Command{
Use: "list-codes ",
Short: "Lists all codes from genesis code dump and queued messages",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
g, err := readGenesis(cmd)
g, err := genReader.ReadWasmGenesis(cmd)
if err != nil {
return err
}
all, err := getAllCodes(g.wasmModuleState)
all, err := getAllCodes(g.WasmModuleState)
if err != nil {
return err
}
Expand All @@ -209,17 +224,17 @@ func GenesisListCodesCmd(defaultNodeHome string) *cobra.Command {

// GenesisListContractsCmd cli command to list all contracts stored in the genesis wasm.contract section
// as well as from messages that are queued in the wasm.genMsgs section.
func GenesisListContractsCmd(defaultNodeHome string) *cobra.Command {
func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *cobra.Command {
cmd := &cobra.Command{
Use: "list-contracts ",
Short: "Lists all contracts from genesis contract dump and queued messages",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
g, err := readGenesis(cmd)
g, err := genReader.ReadWasmGenesis(cmd)
if err != nil {
return err
}
state := g.wasmModuleState
state := g.WasmModuleState
all := getAllContracts(state)
return printJsonOutput(cmd, all)
},
Expand Down Expand Up @@ -352,15 +367,21 @@ func hasContract(state *types.GenesisState, contractAddr string) bool {
return false
}

// genesisData contains raw and unmarshalled data from the genesis file
type genesisData struct {
genesisFile string
genDoc *tmtypes.GenesisDoc
appState map[string]json.RawMessage
wasmModuleState *types.GenesisState
// GenesisData contains raw and unmarshalled data from the genesis file
type GenesisData struct {
GenesisFile string
GenDoc *tmtypes.GenesisDoc
AppState map[string]json.RawMessage
WasmModuleState *types.GenesisState
}

func NewGenesisData(genesisFile string, genDoc *tmtypes.GenesisDoc, appState map[string]json.RawMessage, wasmModuleState *types.GenesisState) *GenesisData {
return &GenesisData{GenesisFile: genesisFile, GenDoc: genDoc, AppState: appState, WasmModuleState: wasmModuleState}
}

func readGenesis(cmd *cobra.Command) (*genesisData, error) {
type DefaultGenesisReader struct{}

func (d DefaultGenesisReader) ReadWasmGenesis(cmd *cobra.Command) (*GenesisData, error) {
clientCtx := client.GetClientContextFromCmd(cmd)
serverCtx := server.GetServerContextFromCmd(cmd)
config := serverCtx.Config
Expand All @@ -377,44 +398,58 @@ func readGenesis(cmd *cobra.Command) (*genesisData, error) {
clientCtx.JSONMarshaler.MustUnmarshalJSON(appState[types.ModuleName], &wasmGenesisState)
}

return &genesisData{
genesisFile: genFile,
genDoc: genDoc,
appState: appState,
wasmModuleState: &wasmGenesisState,
}, nil
return NewGenesisData(
genFile,
genDoc,
appState,
&wasmGenesisState,
), nil
}

var _ GenesisReader = DefaultGenesisIO{}
var _ GenesisMutator = DefaultGenesisIO{}

// DefaultGenesisIO implements both interfaces to read and modify the genesis state for this module.
// This implementation uses the default data structure that is used by the module.go genesis import/ export.
type DefaultGenesisIO struct {
DefaultGenesisReader
}

// NewDefaultGenesisIO constructor to create a new instance
func NewDefaultGenesisIO() *DefaultGenesisIO {
return &DefaultGenesisIO{DefaultGenesisReader: DefaultGenesisReader{}}
}

// alterModuleState loads the genesis from the default or set home dir,
// AlterModuleState loads the genesis from the default or set home dir,
// unmarshalls the wasm module section into the object representation
// calls the callback function to modify it
// and marshals the modified state back into the genesis file
func alterModuleState(cmd *cobra.Command, callback func(state *types.GenesisState, appState map[string]json.RawMessage) error) error {
g, err := readGenesis(cmd)
func (x DefaultGenesisIO) AlterWasmModuleState(cmd *cobra.Command, callback func(state *types.GenesisState, appState map[string]json.RawMessage) error) error {
g, err := x.ReadWasmGenesis(cmd)
if err != nil {
return err
}
if err := callback(g.wasmModuleState, g.appState); err != nil {
if err := callback(g.WasmModuleState, g.AppState); err != nil {
return err
}
// and store update
if err := g.wasmModuleState.ValidateBasic(); err != nil {
if err := g.WasmModuleState.ValidateBasic(); err != nil {
return err
}
clientCtx := client.GetClientContextFromCmd(cmd)
wasmGenStateBz, err := clientCtx.JSONMarshaler.MarshalJSON(g.wasmModuleState)
wasmGenStateBz, err := clientCtx.JSONMarshaler.MarshalJSON(g.WasmModuleState)
if err != nil {
return sdkerrors.Wrap(err, "marshal wasm genesis state")
}

g.appState[types.ModuleName] = wasmGenStateBz
appStateJSON, err := json.Marshal(g.appState)
g.AppState[types.ModuleName] = wasmGenStateBz
appStateJSON, err := json.Marshal(g.AppState)
if err != nil {
return sdkerrors.Wrap(err, "marshal application genesis state")
}

g.genDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(g.genDoc, g.genesisFile)
g.GenDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(g.GenDoc, g.GenesisFile)
}

// contractSeqValue reads the contract sequence from the genesis or
Expand Down
6 changes: 3 additions & 3 deletions x/wasm/client/cli/genesis_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func TestGenesisStoreCodeCmd(t *testing.T) {
homeDir := setupGenesis(t, spec.srcGenesis)

// when
cmd := GenesisStoreCodeCmd(homeDir)
cmd := GenesisStoreCodeCmd(homeDir, NewDefaultGenesisIO())
spec.mutator(cmd)
err := executeCmdWithContext(t, homeDir, cmd)
if spec.expError {
Expand Down Expand Up @@ -299,7 +299,7 @@ func TestInstantiateContractCmd(t *testing.T) {
homeDir := setupGenesis(t, spec.srcGenesis)

// when
cmd := GenesisInstantiateContractCmd(homeDir)
cmd := GenesisInstantiateContractCmd(homeDir, NewDefaultGenesisIO())
spec.mutator(cmd)
err := executeCmdWithContext(t, homeDir, cmd)
if spec.expError {
Expand Down Expand Up @@ -498,7 +498,7 @@ func TestExecuteContractCmd(t *testing.T) {
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
homeDir := setupGenesis(t, spec.srcGenesis)
cmd := GenesisExecuteContractCmd(homeDir)
cmd := GenesisExecuteContractCmd(homeDir, NewDefaultGenesisIO())
spec.mutator(cmd)

// when
Expand Down

0 comments on commit 8109bba

Please sign in to comment.