github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/client/cli/query.go (about)

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"strconv"
    12  	"strings"
    13  
    14  	wasmvm "github.com/CosmWasm/wasmvm"
    15  
    16  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    17  
    18  	"github.com/fibonacci-chain/fbc/x/wasm/keeper"
    19  
    20  	"github.com/spf13/cobra"
    21  	flag "github.com/spf13/pflag"
    22  
    23  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client"
    24  	clientCtx "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    25  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags"
    26  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    27  	codectypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types"
    28  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    29  )
    30  
    31  // NewQueryCmd returns the query commands for wasm
    32  func NewQueryCmd(cdc *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    33  	queryCmd := &cobra.Command{
    34  		Use:                        types.ModuleName,
    35  		Short:                      "Querying commands for the wasm module",
    36  		DisableFlagParsing:         true,
    37  		SuggestionsMinimumDistance: 2,
    38  		RunE:                       client.ValidateCmd,
    39  	}
    40  
    41  	queryCmd.AddCommand(
    42  		NewCmdListCode(cdc, reg),
    43  		NewCmdListContractByCode(cdc, reg),
    44  		NewCmdQueryCode(cdc, reg),
    45  		NewCmdQueryCodeInfo(cdc, reg),
    46  		NewCmdGetContractInfo(cdc, reg),
    47  		NewCmdGetContractHistory(cdc, reg),
    48  		NewCmdGetContractState(cdc, reg),
    49  		NewCmdListPinnedCode(cdc, reg),
    50  		NewCmdLibVersion(cdc, reg),
    51  		NewCmdListContractBlockedMethod(cdc),
    52  		NewCmdGetParams(cdc, reg),
    53  		NewCmdGetAddressWhitelist(cdc, reg),
    54  	)
    55  
    56  	return queryCmd
    57  }
    58  
    59  // NewCmdLibVersion gets current libwasmvm version.
    60  func NewCmdLibVersion(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    61  	cmd := &cobra.Command{
    62  		Use:     "libwasmvm-version",
    63  		Short:   "Get libwasmvm version",
    64  		Long:    "Get libwasmvm version",
    65  		Aliases: []string{"lib-version"},
    66  		Args:    cobra.ExactArgs(0),
    67  		RunE: func(cmd *cobra.Command, args []string) error {
    68  			version, err := wasmvm.LibwasmvmVersion()
    69  			if err != nil {
    70  				return fmt.Errorf("error retrieving libwasmvm version: %w", err)
    71  			}
    72  			fmt.Println(version)
    73  			return nil
    74  		},
    75  	}
    76  	return cmd
    77  }
    78  
    79  // NewCmdListCode lists all wasm code uploaded
    80  func NewCmdListCode(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    81  	cmd := &cobra.Command{
    82  		Use:     "list-code",
    83  		Short:   "List all wasm bytecode on the chain",
    84  		Long:    "List all wasm bytecode on the chain",
    85  		Aliases: []string{"list-codes", "codes", "lco"},
    86  		Args:    cobra.ExactArgs(0),
    87  		RunE: func(cmd *cobra.Command, args []string) error {
    88  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
    89  			queryClient := types.NewQueryClient(clientCtx)
    90  
    91  			pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
    92  			if err != nil {
    93  				return err
    94  			}
    95  			res, err := queryClient.Codes(
    96  				context.Background(),
    97  				&types.QueryCodesRequest{
    98  					Pagination: pageReq,
    99  				},
   100  			)
   101  			if err != nil {
   102  				return err
   103  			}
   104  			return clientCtx.PrintProto(res)
   105  		},
   106  	}
   107  	flags.AddQueryFlagsToCmd(cmd)
   108  	flags.AddPaginationFlagsToCmd(cmd, "list codes")
   109  	return cmd
   110  }
   111  
   112  func NewCmdGetParams(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   113  	cmd := &cobra.Command{
   114  		Use:     "get-params",
   115  		Short:   "Get wasm parameters on the chain",
   116  		Long:    "Get wasm parameters on the chain",
   117  		Aliases: []string{"get-params", "params"},
   118  		Args:    cobra.ExactArgs(0),
   119  		RunE: func(cmd *cobra.Command, args []string) error {
   120  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   121  			route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryParams)
   122  
   123  			res, _, err := clientCtx.Query(route)
   124  			if err != nil {
   125  				return err
   126  			}
   127  
   128  			var params types.Params
   129  			m.GetCdc().MustUnmarshalJSON(res, &params)
   130  			return clientCtx.PrintOutput(params)
   131  		},
   132  	}
   133  	return cmd
   134  }
   135  
   136  func NewCmdGetAddressWhitelist(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   137  	cmd := &cobra.Command{
   138  		Use:     "get-address-whitelist",
   139  		Short:   "Get wasm address whitelist on the chain",
   140  		Long:    "Get wasm address whitelist on the chain",
   141  		Aliases: []string{"whitelist", "gawl"},
   142  		Args:    cobra.ExactArgs(0),
   143  		RunE: func(cmd *cobra.Command, args []string) error {
   144  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   145  			route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryParams)
   146  
   147  			res, _, err := clientCtx.Query(route)
   148  			if err != nil {
   149  				return err
   150  			}
   151  
   152  			var params types.Params
   153  			m.GetCdc().MustUnmarshalJSON(res, &params)
   154  			var whitelist []string
   155  			whitelist = strings.Split(params.CodeUploadAccess.Address, ",")
   156  			if len(whitelist) == 1 && whitelist[0] == "" {
   157  				whitelist = []string{}
   158  			}
   159  			response := types.NewQueryAddressWhitelistResponse(whitelist)
   160  			return clientCtx.PrintOutput(response)
   161  		},
   162  	}
   163  	return cmd
   164  }
   165  
   166  // NewCmdListContractByCode lists all wasm code uploaded for given code id
   167  func NewCmdListContractByCode(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   168  	cmd := &cobra.Command{
   169  		Use:     "list-contract-by-code [code_id]",
   170  		Short:   "List wasm all bytecode on the chain for given code id",
   171  		Long:    "List wasm all bytecode on the chain for given code id",
   172  		Aliases: []string{"list-contracts-by-code", "list-contracts", "contracts", "lca"},
   173  		Args:    cobra.ExactArgs(1),
   174  		RunE: func(cmd *cobra.Command, args []string) error {
   175  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   176  			queryClient := types.NewQueryClient(clientCtx)
   177  
   178  			codeID, err := strconv.ParseUint(args[0], 10, 64)
   179  			if err != nil {
   180  				return err
   181  			}
   182  			pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
   183  			if err != nil {
   184  				return err
   185  			}
   186  			res, err := queryClient.ContractsByCode(
   187  				context.Background(),
   188  				&types.QueryContractsByCodeRequest{
   189  					CodeId:     codeID,
   190  					Pagination: pageReq,
   191  				},
   192  			)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			return clientCtx.PrintProto(res)
   197  		},
   198  	}
   199  	flags.AddQueryFlagsToCmd(cmd)
   200  	flags.AddPaginationFlagsToCmd(cmd, "list contracts by code")
   201  	return cmd
   202  }
   203  
   204  // NewCmdQueryCode returns the bytecode for a given contract
   205  func NewCmdQueryCode(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   206  	cmd := &cobra.Command{
   207  		Use:     "code [code_id] [output filename]",
   208  		Short:   "Downloads wasm bytecode for given code id",
   209  		Long:    "Downloads wasm bytecode for given code id",
   210  		Aliases: []string{"source-code", "source"},
   211  		Args:    cobra.ExactArgs(2),
   212  		RunE: func(cmd *cobra.Command, args []string) error {
   213  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   214  			queryClient := types.NewQueryClient(clientCtx)
   215  
   216  			codeID, err := strconv.ParseUint(args[0], 10, 64)
   217  			if err != nil {
   218  				return err
   219  			}
   220  			res, err := queryClient.Code(
   221  				context.Background(),
   222  				&types.QueryCodeRequest{
   223  					CodeId: codeID,
   224  				},
   225  			)
   226  			if err != nil {
   227  				return err
   228  			}
   229  			if len(res.Data) == 0 {
   230  				return fmt.Errorf("contract not found")
   231  			}
   232  
   233  			fmt.Printf("Downloading wasm code to %s\n", args[1])
   234  			return ioutil.WriteFile(args[1], res.Data, 0o600)
   235  		},
   236  	}
   237  	flags.AddQueryFlagsToCmd(cmd)
   238  	return cmd
   239  }
   240  
   241  // NewCmdQueryCodeInfo returns the code info for a given code id
   242  func NewCmdQueryCodeInfo(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   243  	cmd := &cobra.Command{
   244  		Use:   "code-info [code_id]",
   245  		Short: "Prints out metadata of a code id",
   246  		Long:  "Prints out metadata of a code id",
   247  		Args:  cobra.ExactArgs(1),
   248  		RunE: func(cmd *cobra.Command, args []string) error {
   249  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   250  
   251  			codeID, err := strconv.ParseUint(args[0], 10, 64)
   252  			if err != nil {
   253  				return err
   254  			}
   255  
   256  			queryClient := types.NewQueryClient(clientCtx)
   257  			res, err := queryClient.Code(
   258  				context.Background(),
   259  				&types.QueryCodeRequest{
   260  					CodeId: codeID,
   261  				},
   262  			)
   263  			if err != nil {
   264  				return err
   265  			}
   266  			if res.CodeInfoResponse == nil {
   267  				return fmt.Errorf("contract not found")
   268  			}
   269  
   270  			return clientCtx.PrintProto(res.CodeInfoResponse)
   271  		},
   272  	}
   273  	flags.AddQueryFlagsToCmd(cmd)
   274  	return cmd
   275  }
   276  
   277  // NewCmdGetContractInfo gets details about a given contract
   278  func NewCmdGetContractInfo(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   279  	cmd := &cobra.Command{
   280  		Use:     "contract [bech32_address]",
   281  		Short:   "Prints out metadata of a contract given its address",
   282  		Long:    "Prints out metadata of a contract given its address",
   283  		Aliases: []string{"meta", "c"},
   284  		Args:    cobra.ExactArgs(1),
   285  		RunE: func(cmd *cobra.Command, args []string) error {
   286  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   287  			queryClient := types.NewQueryClient(clientCtx)
   288  
   289  			_, err := sdk.AccAddressFromBech32(args[0])
   290  			if err != nil {
   291  				return err
   292  			}
   293  			res, err := queryClient.ContractInfo(
   294  				context.Background(),
   295  				&types.QueryContractInfoRequest{
   296  					Address: args[0],
   297  				},
   298  			)
   299  			if err != nil {
   300  				return err
   301  			}
   302  			return clientCtx.PrintProto(res)
   303  		},
   304  	}
   305  	flags.AddQueryFlagsToCmd(cmd)
   306  	return cmd
   307  }
   308  
   309  func NewCmdListContractBlockedMethod(m *codec.CodecProxy) *cobra.Command {
   310  	cmd := &cobra.Command{
   311  		Use:     "list-contract-blocked-method [bech32_address]",
   312  		Short:   "List blocked methods of a contract given its address",
   313  		Long:    "List blocked methods of a contract given its address",
   314  		Aliases: []string{"lcbm"},
   315  		Args:    cobra.ExactArgs(1),
   316  		RunE: func(cmd *cobra.Command, args []string) error {
   317  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc())
   318  
   319  			_, err := sdk.AccAddressFromBech32(args[0])
   320  			if err != nil {
   321  				return err
   322  			}
   323  			res, _, err := clientCtx.Query(fmt.Sprintf("custom/wasm/list-contract-blocked-method/%s", args[0]))
   324  			if err != nil {
   325  				return err
   326  			}
   327  			return clientCtx.PrintOutput(res)
   328  		},
   329  	}
   330  	flags.AddQueryFlagsToCmd(cmd)
   331  	return cmd
   332  }
   333  
   334  // NewCmdGetContractHistory prints the code history for a given contract
   335  func NewCmdGetContractHistory(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   336  	cmd := &cobra.Command{
   337  		Use:     "contract-history [bech32_address]",
   338  		Short:   "Prints out the code history for a contract given its address",
   339  		Long:    "Prints out the code history for a contract given its address",
   340  		Aliases: []string{"history", "hist", "ch"},
   341  		Args:    cobra.ExactArgs(1),
   342  		RunE: func(cmd *cobra.Command, args []string) error {
   343  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   344  			queryClient := types.NewQueryClient(clientCtx)
   345  
   346  			_, err := sdk.AccAddressFromBech32(args[0])
   347  			if err != nil {
   348  				return err
   349  			}
   350  			pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
   351  			if err != nil {
   352  				return err
   353  			}
   354  			res, err := queryClient.ContractHistory(
   355  				context.Background(),
   356  				&types.QueryContractHistoryRequest{
   357  					Address:    args[0],
   358  					Pagination: pageReq,
   359  				},
   360  			)
   361  			if err != nil {
   362  				return err
   363  			}
   364  
   365  			return clientCtx.PrintProto(res)
   366  		},
   367  	}
   368  
   369  	flags.AddQueryFlagsToCmd(cmd)
   370  	flags.AddPaginationFlagsToCmd(cmd, "contract history")
   371  	return cmd
   372  }
   373  
   374  // NewCmdGetContractState dumps full internal state of a given contract
   375  func NewCmdGetContractState(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   376  	cmd := &cobra.Command{
   377  		Use:                        "contract-state",
   378  		Short:                      "Querying commands for the wasm module",
   379  		Aliases:                    []string{"state", "cs", "s"},
   380  		DisableFlagParsing:         true,
   381  		SuggestionsMinimumDistance: 2,
   382  		RunE:                       client.ValidateCmd,
   383  	}
   384  	cmd.AddCommand(
   385  		newCmdGetContractStateAll(m, reg),
   386  		newCmdGetContractStateRaw(m, reg),
   387  		newCmdGetContractStateSmart(m, reg),
   388  	)
   389  	return cmd
   390  }
   391  
   392  func newCmdGetContractStateAll(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   393  	cmd := &cobra.Command{
   394  		Use:   "all [bech32_address]",
   395  		Short: "Prints out all internal state of a contract given its address",
   396  		Long:  "Prints out all internal state of a contract given its address",
   397  		Args:  cobra.ExactArgs(1),
   398  		RunE: func(cmd *cobra.Command, args []string) error {
   399  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   400  			queryClient := types.NewQueryClient(clientCtx)
   401  
   402  			_, err := sdk.AccAddressFromBech32(args[0])
   403  			if err != nil {
   404  				return err
   405  			}
   406  			pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
   407  			if err != nil {
   408  				return err
   409  			}
   410  			res, err := queryClient.AllContractState(
   411  				context.Background(),
   412  				&types.QueryAllContractStateRequest{
   413  					Address:    args[0],
   414  					Pagination: pageReq,
   415  				},
   416  			)
   417  			if err != nil {
   418  				return err
   419  			}
   420  			return clientCtx.PrintProto(res)
   421  		},
   422  	}
   423  	flags.AddQueryFlagsToCmd(cmd)
   424  	flags.AddPaginationFlagsToCmd(cmd, "contract state")
   425  	return cmd
   426  }
   427  
   428  func newCmdGetContractStateRaw(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   429  	decoder := newArgDecoder(hex.DecodeString)
   430  	cmd := &cobra.Command{
   431  		Use:   "raw [bech32_address] [key]",
   432  		Short: "Prints out internal state for key of a contract given its address",
   433  		Long:  "Prints out internal state for of a contract given its address",
   434  		Args:  cobra.ExactArgs(2),
   435  		RunE: func(cmd *cobra.Command, args []string) error {
   436  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   437  			queryClient := types.NewQueryClient(clientCtx)
   438  
   439  			_, err := sdk.AccAddressFromBech32(args[0])
   440  			if err != nil {
   441  				return err
   442  			}
   443  			queryData, err := decoder.DecodeString(args[1])
   444  			if err != nil {
   445  				return err
   446  			}
   447  
   448  			res, err := queryClient.RawContractState(
   449  				context.Background(),
   450  				&types.QueryRawContractStateRequest{
   451  					Address:   args[0],
   452  					QueryData: queryData,
   453  				},
   454  			)
   455  			if err != nil {
   456  				return err
   457  			}
   458  			return clientCtx.PrintProto(res)
   459  		},
   460  	}
   461  	decoder.RegisterFlags(cmd.PersistentFlags(), "key argument")
   462  	flags.AddQueryFlagsToCmd(cmd)
   463  	return cmd
   464  }
   465  
   466  func newCmdGetContractStateSmart(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   467  	decoder := newArgDecoder(asciiDecodeString)
   468  	cmd := &cobra.Command{
   469  		Use:   "smart [bech32_address] [query]",
   470  		Short: "Calls contract with given address with query data and prints the returned result",
   471  		Long:  "Calls contract with given address with query data and prints the returned result",
   472  		Args:  cobra.ExactArgs(2),
   473  		RunE: func(cmd *cobra.Command, args []string) error {
   474  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   475  			queryClient := types.NewQueryClient(clientCtx)
   476  
   477  			_, err := sdk.AccAddressFromBech32(args[0])
   478  			if err != nil {
   479  				return err
   480  			}
   481  			if args[1] == "" {
   482  				return errors.New("query data must not be empty")
   483  			}
   484  			queryData, err := decoder.DecodeString(args[1])
   485  			if err != nil {
   486  				return fmt.Errorf("decode query: %s", err)
   487  			}
   488  			if !json.Valid(queryData) {
   489  				return errors.New("query data must be json")
   490  			}
   491  
   492  			res, err := queryClient.SmartContractState(
   493  				context.Background(),
   494  				&types.QuerySmartContractStateRequest{
   495  					Address:   args[0],
   496  					QueryData: queryData,
   497  				},
   498  			)
   499  			if err != nil {
   500  				return err
   501  			}
   502  			return clientCtx.PrintProto(res)
   503  		},
   504  	}
   505  	decoder.RegisterFlags(cmd.PersistentFlags(), "query argument")
   506  	flags.AddQueryFlagsToCmd(cmd)
   507  	return cmd
   508  }
   509  
   510  // NewCmdListPinnedCode lists all wasm code ids that are pinned
   511  func NewCmdListPinnedCode(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   512  	cmd := &cobra.Command{
   513  		Use:   "pinned",
   514  		Short: "List all pinned code ids",
   515  		Long:  "\t\tLong:    List all pinned code ids,\n",
   516  		Args:  cobra.ExactArgs(0),
   517  		RunE: func(cmd *cobra.Command, args []string) error {
   518  			clientCtx := clientCtx.NewCLIContext().WithProxy(m).WithInterfaceRegistry(reg)
   519  			queryClient := types.NewQueryClient(clientCtx)
   520  
   521  			pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
   522  			if err != nil {
   523  				return err
   524  			}
   525  			res, err := queryClient.PinnedCodes(
   526  				context.Background(),
   527  				&types.QueryPinnedCodesRequest{
   528  					Pagination: pageReq,
   529  				},
   530  			)
   531  			if err != nil {
   532  				return err
   533  			}
   534  			return clientCtx.PrintProto(res)
   535  		},
   536  	}
   537  	flags.AddQueryFlagsToCmd(cmd)
   538  	flags.AddPaginationFlagsToCmd(cmd, "list codes")
   539  	return cmd
   540  }
   541  
   542  type argumentDecoder struct {
   543  	// dec is the default decoder
   544  	dec                func(string) ([]byte, error)
   545  	asciiF, hexF, b64F bool
   546  }
   547  
   548  func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
   549  	return &argumentDecoder{dec: def}
   550  }
   551  
   552  func (a *argumentDecoder) RegisterFlags(f *flag.FlagSet, argName string) {
   553  	f.BoolVar(&a.asciiF, "ascii", false, "ascii encoded "+argName)
   554  	f.BoolVar(&a.hexF, "hex", false, "hex encoded  "+argName)
   555  	f.BoolVar(&a.b64F, "b64", false, "base64 encoded "+argName)
   556  }
   557  
   558  func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
   559  	found := -1
   560  	for i, v := range []*bool{&a.asciiF, &a.hexF, &a.b64F} {
   561  		if !*v {
   562  			continue
   563  		}
   564  		if found != -1 {
   565  			return nil, errors.New("multiple decoding flags used")
   566  		}
   567  		found = i
   568  	}
   569  	switch found {
   570  	case 0:
   571  		return asciiDecodeString(s)
   572  	case 1:
   573  		return hex.DecodeString(s)
   574  	case 2:
   575  		return base64.StdEncoding.DecodeString(s)
   576  	default:
   577  		return a.dec(s)
   578  	}
   579  }
   580  
   581  func asciiDecodeString(s string) ([]byte, error) {
   582  	return []byte(s), nil
   583  }
   584  
   585  // sdk ReadPageRequest expects binary but we encoded to base64 in our marshaller
   586  func withPageKeyDecoded(flagSet *flag.FlagSet) *flag.FlagSet {
   587  	encoded, err := flagSet.GetString(flags.FlagPageKey)
   588  	if err != nil {
   589  		panic(err.Error())
   590  	}
   591  	raw, err := base64.StdEncoding.DecodeString(encoded)
   592  	if err != nil {
   593  		panic(err.Error())
   594  	}
   595  	err = flagSet.Set(flags.FlagPageKey, string(raw))
   596  	if err != nil {
   597  		panic(err.Error())
   598  	}
   599  	return flagSet
   600  }