From 3febe76fea22c2afa994c113a790d57a92597db4 Mon Sep 17 00:00:00 2001 From: artpav <19916123+artemijspavlovs@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:54:03 +0300 Subject: [PATCH] feat: add commands to export and update sequencer metadata (#847) --- cmd/rollapp/rollapp.go | 2 + .../sequencer/metadata/export/export.go | 126 ++++++++++++++++++ cmd/rollapp/sequencer/metadata/metadata.go | 20 +++ .../sequencer/metadata/update/update.go | 82 ++++++++++++ cmd/rollapp/sequencer/sequencer.go | 18 +++ cmd/services/services.go | 2 +- utils/sequencer/sequencer.go | 30 ++++- utils/sequencer/types.go | 31 ++--- utils/structs/struct.go | 71 ++++++++++ 9 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 cmd/rollapp/sequencer/metadata/export/export.go create mode 100644 cmd/rollapp/sequencer/metadata/metadata.go create mode 100644 cmd/rollapp/sequencer/metadata/update/update.go create mode 100644 cmd/rollapp/sequencer/sequencer.go create mode 100644 utils/structs/struct.go diff --git a/cmd/rollapp/rollapp.go b/cmd/rollapp/rollapp.go index b3453f60..4cb5d960 100644 --- a/cmd/rollapp/rollapp.go +++ b/cmd/rollapp/rollapp.go @@ -5,6 +5,7 @@ import ( initrollapp "github.com/dymensionxyz/roller/cmd/rollapp/init" "github.com/dymensionxyz/roller/cmd/rollapp/run" + "github.com/dymensionxyz/roller/cmd/rollapp/sequencer" "github.com/dymensionxyz/roller/cmd/rollapp/start" "github.com/dymensionxyz/roller/cmd/rollapp/status" "github.com/dymensionxyz/roller/cmd/services" @@ -23,6 +24,7 @@ func Cmd() *cobra.Command { cmd.AddCommand(start.Cmd()) // cmd.AddCommand(config.Cmd()) cmd.AddCommand(run.Cmd()) + cmd.AddCommand(sequencer.Cmd()) cmd.AddCommand(services.Cmd(loadservices.RollappCmd(), startservices.RollappCmd())) return cmd diff --git a/cmd/rollapp/sequencer/metadata/export/export.go b/cmd/rollapp/sequencer/metadata/export/export.go new file mode 100644 index 00000000..43320c6e --- /dev/null +++ b/cmd/rollapp/sequencer/metadata/export/export.go @@ -0,0 +1,126 @@ +package export + +import ( + "path/filepath" + "strings" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + initconfig "github.com/dymensionxyz/roller/cmd/config/init" + "github.com/dymensionxyz/roller/cmd/consts" + "github.com/dymensionxyz/roller/cmd/utils" + globalutils "github.com/dymensionxyz/roller/utils" + "github.com/dymensionxyz/roller/utils/config/tomlconfig" + "github.com/dymensionxyz/roller/utils/errorhandling" + "github.com/dymensionxyz/roller/utils/sequencer" + sequencerutils "github.com/dymensionxyz/roller/utils/sequencer" + "github.com/dymensionxyz/roller/utils/structs" +) + +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "export", + Short: "Exports the current sequencer metadata into a .json file", + Run: func(cmd *cobra.Command, args []string) { + err := initconfig.AddFlags(cmd) + if err != nil { + pterm.Error.Println("failed to add flags") + return + } + + home, err := globalutils.ExpandHomePath(cmd.Flag(utils.FlagNames.Home).Value.String()) + if err != nil { + pterm.Error.Println("failed to expand home directory") + return + } + + rollerData, err := tomlconfig.LoadRollerConfig(home) + if err != nil { + pterm.Error.Println("failed to load roller config file", err) + return + } + + // redundant + hd, err := tomlconfig.LoadHubData(home) + if err != nil { + pterm.Error.Println("failed to load hub data from roller.toml") + } + + rollappConfig, err := tomlconfig.LoadRollappMetadataFromChain( + home, + rollerData.RollappID, + &hd, + ) + errorhandling.PrettifyErrorIfExists(err) + + hubSeqKC := utils.KeyConfig{ + Dir: filepath.Join(rollappConfig.Home, consts.ConfigDirName.HubKeys), + ID: consts.KeysIds.HubSequencer, + ChainBinary: consts.Executables.Dymension, + Type: consts.SDK_ROLLAPP, + } + + seqAddrInfo, err := utils.GetAddressInfoBinary(hubSeqKC, hubSeqKC.ChainBinary) + if err != nil { + pterm.Error.Println("failed to get address info: ", err) + return + } + seqAddrInfo.Address = strings.TrimSpace(seqAddrInfo.Address) + + seq, err := sequencerutils.GetRegisteredSequencers(rollappConfig.RollappID, hd) + if err != nil { + pterm.Error.Println("failed to retrieve registered sequencers: ", err) + } + + ok := sequencer.IsRegisteredAsSequencer(seq.Sequencers, seqAddrInfo.Address) + if !ok { + pterm.Error.Printf( + "%s is not registered as a sequencer for %s\n", + seqAddrInfo.Address, + rollappConfig.RollappID, + ) + return + } + + pterm.Info.Printf( + "%s is registered as a sequencer for %s\n", + seqAddrInfo.Address, + rollappConfig.RollappID, + ) + pterm.Info.Println( + "retrieving existing metadata", + ) + + metadata, err := sequencer.GetMetadata(seqAddrInfo.Address, hd) + if err != nil { + pterm.Error.Println("failed to retrieve metadata, ", err) + return + } + + metadataFilePath := filepath.Join( + home, consts.ConfigDirName.Rollapp, "init", + "sequencer-metadata.json", + ) + err = structs.ExportStructToFile( + *metadata, + metadataFilePath, + ) + if err != nil { + pterm.Error.Println("failed to export metadata", err) + return + } + + pterm.Info.Printf("metadata successfully exported to %s\n", metadataFilePath) + pterm.Info.Println("next steps:") + pterm.Info.Println("update the metadata file") + pterm.Info.Printf( + "run %s to submit a transaction to update the sequencer metadata\n", + pterm.DefaultBasicText.WithStyle(pterm.FgYellow.ToStyle()). + Sprintf("roller rollapp sequencer metadata update"), + ) + }, + } + + return cmd +} diff --git a/cmd/rollapp/sequencer/metadata/metadata.go b/cmd/rollapp/sequencer/metadata/metadata.go new file mode 100644 index 00000000..8b63e18f --- /dev/null +++ b/cmd/rollapp/sequencer/metadata/metadata.go @@ -0,0 +1,20 @@ +package metadata + +import ( + "github.com/spf13/cobra" + + "github.com/dymensionxyz/roller/cmd/rollapp/sequencer/metadata/export" + "github.com/dymensionxyz/roller/cmd/rollapp/sequencer/metadata/update" +) + +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "metadata [command]", + Short: "Commands to manage sequencer metadata", + } + + cmd.AddCommand(export.Cmd()) + cmd.AddCommand(update.Cmd()) + + return cmd +} diff --git a/cmd/rollapp/sequencer/metadata/update/update.go b/cmd/rollapp/sequencer/metadata/update/update.go new file mode 100644 index 00000000..8a0fba05 --- /dev/null +++ b/cmd/rollapp/sequencer/metadata/update/update.go @@ -0,0 +1,82 @@ +package update + +import ( + "fmt" + "os/exec" + "path/filepath" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + initconfig "github.com/dymensionxyz/roller/cmd/config/init" + "github.com/dymensionxyz/roller/cmd/consts" + "github.com/dymensionxyz/roller/cmd/utils" + globalutils "github.com/dymensionxyz/roller/utils" + "github.com/dymensionxyz/roller/utils/bash" + "github.com/dymensionxyz/roller/utils/config/tomlconfig" + "github.com/dymensionxyz/roller/utils/tx" +) + +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "update [metadata-file.json]", + Short: "Update the sequencer metadata", + Run: func(cmd *cobra.Command, args []string) { + err := initconfig.AddFlags(cmd) + if err != nil { + pterm.Error.Println("failed to add flags") + return + } + + home, err := globalutils.ExpandHomePath(cmd.Flag(utils.FlagNames.Home).Value.String()) + if err != nil { + pterm.Error.Println("failed to expand home directory") + return + } + + rollerData, err := tomlconfig.LoadRollerConfig(home) + if err != nil { + pterm.Error.Println("failed to load roller config file", err) + return + } + + metadataFilePath := filepath.Join( + home, consts.ConfigDirName.Rollapp, "init", + "sequencer-metadata.json", + ) + + updateSeqCmd := exec.Command( + consts.Executables.Dymension, + "tx", + "sequencer", + "update-sequencer", + rollerData.RollappID, + metadataFilePath, + "--from", + consts.KeysIds.HubSequencer, + "--keyring-backend", + "test", + "--fees", + fmt.Sprintf("%d%s", consts.DefaultFee, consts.Denoms.Hub), + "--gas-adjustment", + "1.3", + "--keyring-dir", + filepath.Join(utils.GetRollerRootDir(), consts.ConfigDirName.HubKeys), + ) + + txHash, err := bash.ExecCommandWithInput(updateSeqCmd) + if err != nil { + pterm.Error.Println("failed to update sequencer metadata", err) + return + } + + err = tx.MonitorTransaction(rollerData.HubData.RPC_URL, txHash) + if err != nil { + pterm.Error.Println("transaction failed", err) + return + } + }, + } + + return cmd +} diff --git a/cmd/rollapp/sequencer/sequencer.go b/cmd/rollapp/sequencer/sequencer.go new file mode 100644 index 00000000..6e0f09f5 --- /dev/null +++ b/cmd/rollapp/sequencer/sequencer.go @@ -0,0 +1,18 @@ +package sequencer + +import ( + "github.com/spf13/cobra" + + "github.com/dymensionxyz/roller/cmd/rollapp/sequencer/metadata" +) + +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sequencer [command]", + Short: "Commands to manage sequencer instance", + } + + cmd.AddCommand(metadata.Cmd()) + + return cmd +} diff --git a/cmd/services/services.go b/cmd/services/services.go index 96beb42f..17becf90 100644 --- a/cmd/services/services.go +++ b/cmd/services/services.go @@ -4,7 +4,7 @@ import "github.com/spf13/cobra" func Cmd(loadCmd, startCmd *cobra.Command) *cobra.Command { cmd := &cobra.Command{ - Use: "services", + Use: "services [command]", Short: "Commands for managing systemd services.", } cmd.AddCommand(loadCmd) diff --git a/utils/sequencer/sequencer.go b/utils/sequencer/sequencer.go index edb21740..5ee79b00 100644 --- a/utils/sequencer/sequencer.go +++ b/utils/sequencer/sequencer.go @@ -43,7 +43,6 @@ func Register(raCfg config.RollappConfig) error { return err } - // TODO: handle raw_log cmd := exec.Command( consts.Executables.Dymension, "tx", @@ -174,7 +173,7 @@ func GetRegisteredSequencers( raID string, hd consts.HubData, ) (*Sequencers, error) { var seq Sequencers - cmd := GetShowSequencerByRollappCmd(raID, hd) + cmd := getShowSequencerByRollappCmd(raID, hd) out, err := bash.ExecCommandWithStdout(cmd) if err != nil { @@ -189,7 +188,32 @@ func GetRegisteredSequencers( return &seq, nil } -func GetShowSequencerByRollappCmd(raID string, hd consts.HubData) *exec.Cmd { +func GetMetadata( + addr string, + hd consts.HubData, +) (*Metadata, error) { + var seqinfo ShowSequencerResponse + + cmd := exec.Command( + consts.Executables.Dymension, + "q", "sequencer", "show-sequencer", addr, + "--node", hd.RPC_URL, "-o", "json", + ) + + out, err := bash.ExecCommandWithStdout(cmd) + if err != nil { + return nil, err + } + + err = json.Unmarshal(out.Bytes(), &seqinfo) + if err != nil { + return nil, err + } + + return &seqinfo.Sequencer.Metadata, nil +} + +func getShowSequencerByRollappCmd(raID string, hd consts.HubData) *exec.Cmd { return exec.Command( consts.Executables.Dymension, "q", "sequencer", "show-sequencers-by-rollapp", diff --git a/utils/sequencer/types.go b/utils/sequencer/types.go index d8a9c992..4d9df012 100644 --- a/utils/sequencer/types.go +++ b/utils/sequencer/types.go @@ -13,6 +13,10 @@ type Sequencers struct { Sequencers []Info `json:"sequencers,omitempty"` } +type ShowSequencerResponse struct { + Sequencer Info `json:"sequencer,omitempty"` +} + type Info struct { // address is the bech32-encoded address of the sequencer account which is the account that the message was sent from. Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` @@ -38,34 +42,31 @@ type Info struct { type Metadata struct { // moniker defines a human-readable name for the sequencer. - Moniker string `protobuf:"bytes,1,opt,name=moniker,proto3" json:"moniker,omitempty"` + Moniker string `json:"moniker"` // details define other optional details. - Details string `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` + Details string `json:"details"` // bootstrap nodes list - P2PSeeds []string `protobuf:"bytes,6,rep,name=p2p_seeds,json=p2pSeeds,proto3" json:"p2p_seeds,omitempty"` + P2PSeeds []string `json:"p2p_seeds"` // RPCs list - Rpcs []string `protobuf:"bytes,7,rep,name=rpcs,proto3" json:"rpcs,omitempty"` + Rpcs []string `json:"rpcs"` // evm RPCs list - EvmRpcs []string `protobuf:"bytes,8,rep,name=evm_rpcs,json=evmRpcs,proto3" json:"evm_rpcs,omitempty"` + EvmRpcs []string `json:"evm_rpcs"` // REST API URLs - RestApiUrls []string `protobuf:"bytes,9,rep,name=rest_api_urls,json=restApiUrls,proto3" json:"rest_api_urls,omitempty"` + RestApiUrls []string `json:"rest_api_urls"` // block explorer URL - ExplorerUrl string `protobuf:"bytes,10,opt,name=explorer_url,json=explorerUrl,proto3" json:"explorer_url,omitempty"` + ExplorerUrl string `json:"explorer_url"` // genesis URLs - GenesisUrls []string `protobuf:"bytes,11,rep,name=genesis_urls,json=genesisUrls,proto3" json:"genesis_urls,omitempty"` + GenesisUrls []string `json:"genesis_urls"` // contact details // nolint:govet,staticcheck - ContactDetails *dymensionseqtypes.ContactDetails `protobuf:"bytes,12,opt,name=contact_details, -json=contactDetails, -proto3" json:"contact_details,omitempty"` + ContactDetails *dymensionseqtypes.ContactDetails `json:"contact_details"` // json dump the sequencer can add (limited by size) - ExtraData []byte `protobuf:"bytes,13,opt,name=extra_data,json=extraData,proto3" json:"extra_data,omitempty"` + ExtraData []byte `json:"extra_data"` // snapshots of the sequencer - Snapshots []*SnapshotInfo `protobuf:"bytes,14,rep,name=snapshots,proto3" json:"snapshots,omitempty"` + Snapshots []*SnapshotInfo `json:"snapshots"` // gas_price defines the value for each gas unit // nolint:govet,staticcheck - GasPrice *cosmossdkmath.Int `protobuf:"bytes,15,opt,name=gas_price,json=gasPrice,proto3, -customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_price,omitempty"` + GasPrice *cosmossdkmath.Int `json:"gas_price"` } type SnapshotInfo struct { diff --git a/utils/structs/struct.go b/utils/structs/struct.go new file mode 100644 index 00000000..1c473846 --- /dev/null +++ b/utils/structs/struct.go @@ -0,0 +1,71 @@ +package structs + +import ( + "encoding/json" + "fmt" + "os" + + cosmossdkmath "cosmossdk.io/math" + dymensionseqtypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" + + sequencerutils "github.com/dymensionxyz/roller/utils/sequencer" +) + +func InitializeMetadata(m sequencerutils.Metadata) { + if m.Moniker == "" { + m.Moniker = "" + } + if m.Details == "" { + m.Details = "" + } + if m.P2PSeeds == nil { + m.P2PSeeds = []string{} + } + if m.Rpcs == nil { + m.Rpcs = []string{} + } + if m.EvmRpcs == nil { + m.EvmRpcs = []string{} + } + if m.RestApiUrls == nil { + m.RestApiUrls = []string{} + } + if m.ExplorerUrl == "" { + m.ExplorerUrl = "" + } + if m.GenesisUrls == nil { + m.GenesisUrls = []string{} + } + if m.ContactDetails == nil { + m.ContactDetails = &dymensionseqtypes.ContactDetails{} + } + if m.ExtraData == nil { + m.ExtraData = []byte{} + } + if m.Snapshots == nil { + m.Snapshots = []*sequencerutils.SnapshotInfo{} + } + if m.GasPrice == nil { + zero := cosmossdkmath.NewInt(0) + m.GasPrice = &zero + } +} + +func ExportStructToFile(data sequencerutils.Metadata, filename string) error { + // Initialize the struct with default values + InitializeMetadata(data) + + // Marshal the struct to JSON + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + return fmt.Errorf("error marshaling to JSON: %v", err) + } + + // Write to file + err = os.WriteFile(filename, jsonData, 0o644) + if err != nil { + return fmt.Errorf("error writing to file: %v", err) + } + + return nil +}