github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/client/cli/genesis_msg.go (about) 1 package cli 2 3 import ( 4 "bufio" 5 "bytes" 6 "crypto/sha256" 7 "encoding/json" 8 "errors" 9 "fmt" 10 11 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys" 13 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 14 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/genutil" 17 genutiltypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/genutil/types" 18 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 19 "github.com/fibonacci-chain/fbc/x/wasm/client/utils" 20 "github.com/fibonacci-chain/fbc/x/wasm/keeper" 21 "github.com/fibonacci-chain/fbc/x/wasm/types" 22 "github.com/spf13/cobra" 23 ) 24 25 // GenesisReader reads genesis data. Extension point for custom genesis state readers. 26 type GenesisReader interface { 27 ReadWasmGenesis(cmd *cobra.Command) (*GenesisData, error) 28 } 29 30 // GenesisMutator extension point to modify the wasm module genesis state. 31 // This gives flexibility to customize the data structure in the genesis file a bit. 32 type GenesisMutator interface { 33 // AlterWasmModuleState loads the genesis from the default or set home dir, 34 // unmarshalls the wasm module section into the object representation 35 // calls the callback function to modify it 36 // and marshals the modified state back into the genesis file 37 AlterWasmModuleState(cmd *cobra.Command, callback func(state *types.GenesisState, appState map[string]json.RawMessage) error) error 38 } 39 40 // GenesisStoreCodeCmd cli command to add a `MsgStoreCode` to the wasm section of the genesis 41 // that is executed on block 0. 42 func GenesisStoreCodeCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command { 43 cmd := &cobra.Command{ 44 Use: "store [wasm file] --run-as [owner_address_or_key_name]\",", 45 Short: "Upload a wasm binary", 46 Args: cobra.ExactArgs(1), 47 RunE: func(cmd *cobra.Command, args []string) error { 48 senderAddr, err := getActorAddress(cmd) 49 if err != nil { 50 return err 51 } 52 53 msg, err := parseStoreCodeArgs(args[0], senderAddr, cmd.Flags()) 54 if err != nil { 55 return err 56 } 57 if err = msg.ValidateBasic(); err != nil { 58 return err 59 } 60 61 return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, _ map[string]json.RawMessage) error { 62 state.GenMsgs = append(state.GenMsgs, types.GenesisState_GenMsgs{ 63 Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: &msg}, 64 }) 65 return nil 66 }) 67 }, 68 } 69 cmd.Flags().String(flagRunAs, "", "The address that is stored as code creator") 70 cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional") 71 cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional") 72 cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional") 73 74 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 75 cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") 76 flags.AddQueryFlagsToCmd(cmd) 77 return cmd 78 } 79 80 // GenesisInstantiateContractCmd cli command to add a `MsgInstantiateContract` to the wasm section of the genesis 81 // that is executed on block 0. 82 func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command { 83 cmd := &cobra.Command{ 84 Use: "instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --run-as [address] --admin [address,optional] --amount [coins,optional]", 85 Short: "Instantiate a wasm contract", 86 Args: cobra.ExactArgs(2), 87 RunE: func(cmd *cobra.Command, args []string) error { 88 senderAddr, err := getActorAddress(cmd) 89 if err != nil { 90 return err 91 } 92 93 msg, err := parseInstantiateArgs(args[0], args[1], senderAddr, cmd.Flags()) 94 if err != nil { 95 return err 96 } 97 if err = msg.ValidateBasic(); err != nil { 98 return err 99 } 100 101 return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error { 102 // simple sanity check that sender has some balance although it may be consumed by appState previous message already 103 switch ok, err := hasAccountBalance(cmd, appState, senderAddr, sdk.CoinAdaptersToCoins(msg.Funds)); { 104 case err != nil: 105 return err 106 case !ok: 107 return errors.New("sender has not enough account balance") 108 } 109 110 // does code id exists? 111 codeInfos, err := GetAllCodes(state) 112 if err != nil { 113 return err 114 } 115 var codeInfo *CodeMeta 116 for i := range codeInfos { 117 if codeInfos[i].CodeID == msg.CodeID { 118 codeInfo = &codeInfos[i] 119 break 120 } 121 } 122 if codeInfo == nil { 123 return fmt.Errorf("unknown code id: %d", msg.CodeID) 124 } 125 // permissions correct? 126 if !codeInfo.Info.InstantiateConfig.Allowed(senderAddr) { 127 return fmt.Errorf("permissions were not granted for %s", senderAddr) 128 } 129 state.GenMsgs = append(state.GenMsgs, types.GenesisState_GenMsgs{ 130 Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &msg}, 131 }) 132 return nil 133 }) 134 }, 135 } 136 cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation") 137 cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists") 138 cmd.Flags().String(flagAdmin, "", "Address of an admin") 139 cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin") 140 cmd.Flags().String(flagRunAs, "", "The address that pays the init funds. It is the creator of the contract.") 141 142 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 143 cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") 144 flags.AddQueryFlagsToCmd(cmd) 145 return cmd 146 } 147 148 // GenesisExecuteContractCmd cli command to add a `MsgExecuteContract` to the wasm section of the genesis 149 // that is executed on block 0. 150 func GenesisExecuteContractCmd(defaultNodeHome string, genesisMutator GenesisMutator) *cobra.Command { 151 cmd := &cobra.Command{ 152 Use: "execute [contract_addr_bech32] [json_encoded_send_args] --run-as [address] --amount [coins,optional]", 153 Short: "Execute a command on a wasm contract", 154 Args: cobra.ExactArgs(2), 155 RunE: func(cmd *cobra.Command, args []string) error { 156 senderAddr, err := getActorAddress(cmd) 157 if err != nil { 158 return err 159 } 160 161 msg, err := parseExecuteArgs(args[0], args[1], senderAddr, cmd.Flags()) 162 if err != nil { 163 return err 164 } 165 if err = msg.ValidateBasic(); err != nil { 166 return err 167 } 168 169 return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error { 170 // simple sanity check that sender has some balance although it may be consumed by appState previous message already 171 switch ok, err := hasAccountBalance(cmd, appState, senderAddr, sdk.CoinAdaptersToCoins(msg.Funds)); { 172 case err != nil: 173 return err 174 case !ok: 175 return errors.New("sender has not enough account balance") 176 } 177 178 // - does contract address exists? 179 if !hasContract(state, msg.Contract) { 180 return fmt.Errorf("unknown contract: %state", msg.Contract) 181 } 182 state.GenMsgs = append(state.GenMsgs, types.GenesisState_GenMsgs{ 183 Sum: &types.GenesisState_GenMsgs_ExecuteContract{ExecuteContract: &msg}, 184 }) 185 return nil 186 }) 187 }, 188 } 189 cmd.Flags().String(flagAmount, "", "Coins to send to the contract along with command") 190 cmd.Flags().String(flagRunAs, "", "The address that pays the funds.") 191 192 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 193 cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") 194 flags.AddQueryFlagsToCmd(cmd) 195 return cmd 196 } 197 198 // GenesisListCodesCmd cli command to list all codes stored in the genesis wasm.code section 199 // as well as from messages that are queued in the wasm.genMsgs section. 200 func GenesisListCodesCmd(defaultNodeHome string, genReader GenesisReader) *cobra.Command { 201 cmd := &cobra.Command{ 202 Use: "list-codes ", 203 Short: "Lists all codes from genesis code dump and queued messages", 204 Args: cobra.ExactArgs(0), 205 RunE: func(cmd *cobra.Command, args []string) error { 206 g, err := genReader.ReadWasmGenesis(cmd) 207 if err != nil { 208 return err 209 } 210 all, err := GetAllCodes(g.WasmModuleState) 211 if err != nil { 212 return err 213 } 214 return printJSONOutput(cmd, all) 215 }, 216 } 217 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 218 flags.AddQueryFlagsToCmd(cmd) 219 return cmd 220 } 221 222 // GenesisListContractsCmd cli command to list all contracts stored in the genesis wasm.contract section 223 // as well as from messages that are queued in the wasm.genMsgs section. 224 func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *cobra.Command { 225 cmd := &cobra.Command{ 226 Use: "list-contracts ", 227 Short: "Lists all contracts from genesis contract dump and queued messages", 228 Args: cobra.ExactArgs(0), 229 RunE: func(cmd *cobra.Command, args []string) error { 230 g, err := genReader.ReadWasmGenesis(cmd) 231 if err != nil { 232 return err 233 } 234 state := g.WasmModuleState 235 all := GetAllContracts(state) 236 return printJSONOutput(cmd, all) 237 }, 238 } 239 cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 240 flags.AddQueryFlagsToCmd(cmd) 241 return cmd 242 } 243 244 // clientCtx marshaller works only with proto or bytes so we marshal the output ourself 245 func printJSONOutput(cmd *cobra.Command, obj interface{}) error { 246 clientCtx := utils.GetClientContextFromCmd(cmd) 247 bz, err := json.MarshalIndent(obj, "", " ") 248 if err != nil { 249 return err 250 } 251 return clientCtx.PrintOutput(string(bz)) 252 } 253 254 type CodeMeta struct { 255 CodeID uint64 `json:"code_id"` 256 Info types.CodeInfo `json:"info"` 257 } 258 259 func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) { 260 all := make([]CodeMeta, len(state.Codes)) 261 for i, c := range state.Codes { 262 all[i] = CodeMeta{ 263 CodeID: c.CodeID, 264 Info: c.CodeInfo, 265 } 266 } 267 // add inflight 268 seq := codeSeqValue(state) 269 for _, m := range state.GenMsgs { 270 if msg := m.GetStoreCode(); msg != nil { 271 var accessConfig types.AccessConfig 272 if msg.InstantiatePermission != nil { 273 accessConfig = *msg.InstantiatePermission 274 } else { 275 // default 276 creator, err := sdk.AccAddressFromBech32(msg.Sender) 277 if err != nil { 278 return nil, fmt.Errorf("sender: %s", err) 279 } 280 accessConfig = state.Params.InstantiateDefaultPermission.With(creator) 281 } 282 hash := sha256.Sum256(msg.WASMByteCode) 283 all = append(all, CodeMeta{ 284 CodeID: seq, 285 Info: types.CodeInfo{ 286 CodeHash: hash[:], 287 Creator: msg.Sender, 288 InstantiateConfig: accessConfig, 289 }, 290 }) 291 seq++ 292 } 293 } 294 return all, nil 295 } 296 297 type ContractMeta struct { 298 ContractAddress string `json:"contract_address"` 299 Info types.ContractInfo `json:"info"` 300 } 301 302 func GetAllContracts(state *types.GenesisState) []ContractMeta { 303 all := make([]ContractMeta, len(state.Contracts)) 304 for i, c := range state.Contracts { 305 all[i] = ContractMeta{ 306 ContractAddress: c.ContractAddress, 307 Info: c.ContractInfo, 308 } 309 } 310 // add inflight 311 seq := contractSeqValue(state) 312 for _, m := range state.GenMsgs { 313 if msg := m.GetInstantiateContract(); msg != nil { 314 all = append(all, ContractMeta{ 315 ContractAddress: keeper.BuildContractAddress(msg.CodeID, seq).String(), 316 Info: types.ContractInfo{ 317 CodeID: msg.CodeID, 318 Creator: msg.Sender, 319 Admin: msg.Admin, 320 Label: msg.Label, 321 }, 322 }) 323 seq++ 324 } 325 } 326 return all 327 } 328 329 func hasAccountBalance(cmd *cobra.Command, appState map[string]json.RawMessage, sender sdk.AccAddress, coins sdk.Coins) (bool, error) { 330 // no coins needed, no account needed 331 if coins.IsZero() { 332 return true, nil 333 } 334 clientCtx, err := utils.GetClientQueryContext(cmd) 335 if err != nil { 336 return false, err 337 } 338 cdc := clientCtx.Codec 339 var genBalIterator auth.GenesisAccountIterator 340 err = genutil.ValidateAccountInGenesis(appState, genBalIterator, sender, coins, cdc) 341 if err != nil { 342 return false, err 343 } 344 return true, nil 345 } 346 347 func hasContract(state *types.GenesisState, contractAddr string) bool { 348 for _, c := range state.Contracts { 349 if c.ContractAddress == contractAddr { 350 return true 351 } 352 } 353 seq := contractSeqValue(state) 354 for _, m := range state.GenMsgs { 355 if msg := m.GetInstantiateContract(); msg != nil { 356 if keeper.BuildContractAddress(msg.CodeID, seq).String() == contractAddr { 357 return true 358 } 359 seq++ 360 } 361 } 362 return false 363 } 364 365 // GenesisData contains raw and unmarshalled data from the genesis file 366 type GenesisData struct { 367 GenesisFile string 368 GenDoc *tmtypes.GenesisDoc 369 AppState map[string]json.RawMessage 370 WasmModuleState *types.GenesisState 371 } 372 373 func NewGenesisData(genesisFile string, genDoc *tmtypes.GenesisDoc, appState map[string]json.RawMessage, wasmModuleState *types.GenesisState) *GenesisData { 374 return &GenesisData{GenesisFile: genesisFile, GenDoc: genDoc, AppState: appState, WasmModuleState: wasmModuleState} 375 } 376 377 type DefaultGenesisReader struct{} 378 379 func (d DefaultGenesisReader) ReadWasmGenesis(cmd *cobra.Command) (*GenesisData, error) { 380 clientCtx := utils.GetClientContextFromCmd(cmd) 381 serverCtx := utils.GetServerContextFromCmd(cmd) 382 config := serverCtx.Config 383 config.SetRoot(clientCtx.HomeDir) 384 385 genFile := config.GenesisFile() 386 appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(clientCtx.Codec, genFile) 387 if err != nil { 388 return nil, fmt.Errorf("failed to unmarshal genesis state: %w", err) 389 } 390 var wasmGenesisState types.GenesisState 391 if appState[types.ModuleName] != nil { 392 clientCtx := utils.GetClientContextFromCmd(cmd) 393 clientCtx.CodecProy.GetProtocMarshal().MustUnmarshalJSON(appState[types.ModuleName], &wasmGenesisState) 394 } 395 396 return NewGenesisData( 397 genFile, 398 genDoc, 399 appState, 400 &wasmGenesisState, 401 ), nil 402 } 403 404 var ( 405 _ GenesisReader = DefaultGenesisIO{} 406 _ GenesisMutator = DefaultGenesisIO{} 407 ) 408 409 // DefaultGenesisIO implements both interfaces to read and modify the genesis state for this module. 410 // This implementation uses the default data structure that is used by the module.go genesis import/ export. 411 type DefaultGenesisIO struct { 412 DefaultGenesisReader 413 } 414 415 // NewDefaultGenesisIO constructor to create a new instance 416 func NewDefaultGenesisIO() *DefaultGenesisIO { 417 return &DefaultGenesisIO{DefaultGenesisReader: DefaultGenesisReader{}} 418 } 419 420 // AlterWasmModuleState loads the genesis from the default or set home dir, 421 // unmarshalls the wasm module section into the object representation 422 // calls the callback function to modify it 423 // and marshals the modified state back into the genesis file 424 func (x DefaultGenesisIO) AlterWasmModuleState(cmd *cobra.Command, callback func(state *types.GenesisState, appState map[string]json.RawMessage) error) error { 425 g, err := x.ReadWasmGenesis(cmd) 426 if err != nil { 427 return err 428 } 429 if err := callback(g.WasmModuleState, g.AppState); err != nil { 430 return err 431 } 432 // and store update 433 if err := g.WasmModuleState.ValidateBasic(); err != nil { 434 return err 435 } 436 clientCtx := utils.GetClientContextFromCmd(cmd) 437 wasmGenStateBz, err := clientCtx.CodecProy.GetProtocMarshal().MarshalJSON(g.WasmModuleState) 438 if err != nil { 439 return sdkerrors.Wrap(err, "marshal wasm genesis state") 440 } 441 442 g.AppState[types.ModuleName] = wasmGenStateBz 443 appStateJSON, err := json.Marshal(g.AppState) 444 if err != nil { 445 return sdkerrors.Wrap(err, "marshal application genesis state") 446 } 447 448 g.GenDoc.AppState = appStateJSON 449 return genutil.ExportGenesisFile(g.GenDoc, g.GenesisFile) 450 } 451 452 // contractSeqValue reads the contract sequence from the genesis or 453 // returns default start value used in the keeper 454 func contractSeqValue(state *types.GenesisState) uint64 { 455 var seq uint64 = 1 456 for _, s := range state.Sequences { 457 if bytes.Equal(s.IDKey, types.KeyLastInstanceID) { 458 seq = s.Value 459 break 460 } 461 } 462 return seq 463 } 464 465 // codeSeqValue reads the code sequence from the genesis or 466 // returns default start value used in the keeper 467 func codeSeqValue(state *types.GenesisState) uint64 { 468 var seq uint64 = 1 469 for _, s := range state.Sequences { 470 if bytes.Equal(s.IDKey, types.KeyLastCodeID) { 471 seq = s.Value 472 break 473 } 474 } 475 return seq 476 } 477 478 // getActorAddress returns the account address for the `--run-as` flag. 479 // The flag value can either be an address already or a key name where the 480 // address is read from the keyring instead. 481 func getActorAddress(cmd *cobra.Command) (sdk.AccAddress, error) { 482 actorArg, err := cmd.Flags().GetString(flagRunAs) 483 if err != nil { 484 return nil, fmt.Errorf("run-as: %s", err.Error()) 485 } 486 if len(actorArg) == 0 { 487 return nil, errors.New("run-as address is required") 488 } 489 490 actorAddr, err := sdk.AccAddressFromBech32(actorArg) 491 if err == nil { 492 return actorAddr, nil 493 } 494 inBuf := bufio.NewReader(cmd.InOrStdin()) 495 keyringBackend, err := cmd.Flags().GetString(flags.FlagKeyringBackend) 496 if err != nil { 497 return nil, err 498 } 499 500 homeDir := utils.GetClientContextFromCmd(cmd).HomeDir 501 // attempt to lookup address from Keybase if no address was provided 502 kb, err := keys.NewKeyring(sdk.KeyringServiceName(), keyringBackend, homeDir, inBuf) 503 if err != nil { 504 return nil, err 505 } 506 507 info, err := kb.Get(actorArg) 508 if err != nil { 509 return nil, fmt.Errorf("failed to get address from Keybase: %w", err) 510 } 511 return info.GetAddress(), nil 512 }