Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension points to the CLI #477

Merged
merged 3 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
alpe marked this conversation as resolved.
Show resolved Hide resolved
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