diff --git a/cmd/blobstream/base/config.go b/cmd/blobstream/base/config.go index b2a3583c..a27a93ce 100644 --- a/cmd/blobstream/base/config.go +++ b/cmd/blobstream/base/config.go @@ -5,6 +5,10 @@ import ( "os" "strings" + ethcmn "github.com/ethereum/go-ethereum/common" + + "github.com/celestiaorg/orchestrator-relayer/evm" + "github.com/spf13/cobra" "github.com/pkg/errors" @@ -24,6 +28,19 @@ type Config struct { EVMPassphrase string } +func AddEVMPassphraseFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagEVMPassphrase, "", "the evm account passphrase (if not specified as a flag, it will be asked interactively)") +} + +func GetEVMPassphraseFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagEVMPassphrase) + val, err := cmd.Flags().GetString(FlagEVMPassphrase) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + // DefaultServicePath constructs the default Blobstream store path for // the provided service. // It tries to get the home directory from an environment variable @@ -47,20 +64,217 @@ const ( FlagP2PListenAddress = "p2p.listen-addr" FlagP2PNickname = "p2p.nickname" FlagGRPCInsecure = "grpc.insecure" + + FlagEVMAccAddress = "evm.account" + FlagEVMChainID = "evm.chain-id" + FlagEVMRPC = "evm.rpc" + FlagEVMGasLimit = "evm.gas-limit" + FlagEVMContractAddress = "evm.contract-address" + + FlagCoreGRPC = "core.grpc" + FlagCoreRPC = "core.rpc" + + FlagStartingNonce = "starting-nonce" ) +func AddStartingNonceFlag(cmd *cobra.Command) { + cmd.Flags().String( + FlagStartingNonce, + "latest", + "Specify the nonce to start the Blobstream contract from. "+ + "\"earliest\": for genesis, "+ + "\"latest\": for latest nonce, "+ + "\"nonce\": for a specific nonce.", + ) +} + +func GetStartingNonceFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagStartingNonce) + val, err := cmd.Flags().GetString(FlagStartingNonce) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + func AddP2PNicknameFlag(cmd *cobra.Command) { cmd.Flags().String(FlagP2PNickname, "", "Nickname of the p2p private key to use (if not provided, an existing one from the p2p store or a newly generated one will be used)") } +func GetP2PNicknameFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagP2PNickname) + val, err := cmd.Flags().GetString(FlagP2PNickname) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + func AddP2PListenAddressFlag(cmd *cobra.Command) { cmd.Flags().String(FlagP2PListenAddress, "/ip4/0.0.0.0/tcp/30000", "MultiAddr for the p2p peer to listen on") } +func GetP2PListenAddressFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagP2PListenAddress) + val, err := cmd.Flags().GetString(FlagP2PListenAddress) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + func AddBootstrappersFlag(cmd *cobra.Command) { cmd.Flags().String(FlagBootstrappers, "", "Comma-separated multiaddresses of p2p peers to connect to") } +func GetBootstrappersFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagBootstrappers) + val, err := cmd.Flags().GetString(FlagBootstrappers) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + func AddGRPCInsecureFlag(cmd *cobra.Command) { cmd.Flags().Bool(FlagGRPCInsecure, false, "allow gRPC over insecure channels, if not TLS the server must use TLS") } + +func GetGRPCInsecureFlag(cmd *cobra.Command) (bool, bool, error) { + changed := cmd.Flags().Changed(FlagGRPCInsecure) + val, err := cmd.Flags().GetBool(FlagGRPCInsecure) + if err != nil { + return false, changed, err + } + return val, changed, nil +} + +func AddCoreGRPCFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagCoreGRPC, "localhost:9090", "Specify the celestia app grpc address") +} + +func GetCoreGRPCFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagCoreGRPC) + val, err := cmd.Flags().GetString(FlagCoreGRPC) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func AddEVMChainIDFlag(cmd *cobra.Command) { + cmd.Flags().Uint64(FlagEVMChainID, 5, "Specify the evm chain id") +} + +func GetEVMChainIDFlag(cmd *cobra.Command) (uint64, bool, error) { + changed := cmd.Flags().Changed(FlagEVMChainID) + val, err := cmd.Flags().GetUint64(FlagEVMChainID) + if err != nil { + return 0, changed, err + } + return val, changed, nil +} + +func AddCoreRPCFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagCoreRPC, "tcp://localhost:26657", "Specify the celestia app rest rpc address") +} + +func GetCoreRPCFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagCoreRPC) + val, err := cmd.Flags().GetString(FlagCoreRPC) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func AddEVMRPCFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagEVMRPC, "http://localhost:8545", "Specify the ethereum rpc address") +} + +func GetEVMRPCFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagEVMRPC) + val, err := cmd.Flags().GetString(FlagEVMRPC) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func AddEVMContractAddressFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagEVMContractAddress, "", "Specify the contract at which the Blobstream is deployed") +} + +func GetEVMContractAddressFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagEVMContractAddress) + val, err := cmd.Flags().GetString(FlagEVMContractAddress) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func AddEVMGasLimitFlag(cmd *cobra.Command) { + cmd.Flags().Uint64(FlagEVMGasLimit, evm.DefaultEVMGasLimit, "Specify the evm gas limit") +} + +func GetEVMGasLimitFlag(cmd *cobra.Command) (uint64, bool, error) { + changed := cmd.Flags().Changed(FlagEVMGasLimit) + val, err := cmd.Flags().GetUint64(FlagEVMGasLimit) + if err != nil { + return 0, changed, err + } + return val, changed, nil +} + +func AddHomeFlag(cmd *cobra.Command, serviceName string, defaultHomeDir string) { + cmd.Flags().String(FlagHome, defaultHomeDir, fmt.Sprintf("The Blobstream %s home directory", serviceName)) +} + +func GetHomeFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagHome) + val, err := cmd.Flags().GetString(FlagHome) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func GetHomeDirectory(cmd *cobra.Command, service string) (string, error) { + homeDir, changed, err := GetHomeFlag(cmd) + if err != nil { + return "", err + } + if changed && homeDir != "" { + return homeDir, nil + } + return DefaultServicePath(service) +} + +func AddEVMAccAddressFlag(cmd *cobra.Command) { + cmd.Flags().String(FlagEVMAccAddress, "", "Specify the EVM account address to use for signing (Note: the private key should be in the keystore)") +} + +func GetEVMAccAddressFlag(cmd *cobra.Command) (string, bool, error) { + changed := cmd.Flags().Changed(FlagEVMAccAddress) + val, err := cmd.Flags().GetString(FlagEVMAccAddress) + if err != nil { + return "", changed, err + } + return val, changed, nil +} + +func ValidateEVMAddress(addr string) error { + if addr == "" { + return fmt.Errorf("the EVM address cannot be empty") + } + if !ethcmn.IsHexAddress(addr) { + return errors.New("valid EVM address is required") + } + return nil +} + +// EnsureConfigPath creates a directory configPath if it does not exist +func EnsureConfigPath(configPath string) error { + return os.MkdirAll(configPath, os.ModePerm) +} diff --git a/cmd/blobstream/deploy/config.go b/cmd/blobstream/deploy/config.go index c3ac1b52..2187dd0c 100644 --- a/cmd/blobstream/deploy/config.go +++ b/cmd/blobstream/deploy/config.go @@ -3,55 +3,37 @@ package deploy import ( "errors" "fmt" + "strings" "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" - "github.com/celestiaorg/orchestrator-relayer/evm" "github.com/spf13/cobra" ) const ( - FlagEVMAccAddress = "evm.account" - FlagEVMChainID = "evm.chain-id" - FlagEVMRPC = "evm.rpc" - FlagEVMGasLimit = "evm.gas-limit" - FlagCoreGRPCHost = "core.grpc.host" - FlagCoreGRPCPort = "core.grpc.port" - FlagCoreRPCHost = "core.rpc.host" - FlagCoreRPCPort = "core.rpc.port" - FlagStartingNonce = "starting-nonce" ServiceNameDeployer = "deployer" ) func addDeployFlags(cmd *cobra.Command) *cobra.Command { - cmd.Flags().String(FlagEVMAccAddress, "", "Specify the EVM account address to use for signing (Note: the private key should be in the keystore)") - cmd.Flags().Uint64(FlagEVMChainID, 5, "Specify the evm chain id") - cmd.Flags().String(FlagCoreGRPCHost, "localhost", "Specify the grpc address host") - cmd.Flags().Uint(FlagCoreGRPCPort, 9090, "Specify the grpc address port") - cmd.Flags().String(FlagCoreRPCHost, "localhost", "Specify the rpc address host") - cmd.Flags().Uint(FlagCoreRPCPort, 26657, "Specify the rpc address port") - cmd.Flags().String(FlagEVMRPC, "http://localhost:8545", "Specify the ethereum rpc address") - cmd.Flags().String( - FlagStartingNonce, - "latest", - "Specify the nonce to start the Blobstream contract from. "+ - "\"earliest\": for genesis, "+ - "\"latest\": for latest valset nonce, "+ - "\"nonce\": for the latest valset before the provided nonce, provided nonce included.", - ) - cmd.Flags().Uint64(FlagEVMGasLimit, evm.DefaultEVMGasLimit, "Specify the evm gas limit") + base.AddEVMAccAddressFlag(cmd) + base.AddEVMChainIDFlag(cmd) + base.AddCoreGRPCFlag(cmd) + base.AddCoreRPCFlag(cmd) + base.AddEVMRPCFlag(cmd) + base.AddStartingNonceFlag(cmd) + base.AddEVMGasLimitFlag(cmd) + base.AddEVMPassphraseFlag(cmd) homeDir, err := base.DefaultServicePath(ServiceNameDeployer) if err != nil { panic(err) } - cmd.Flags().String(base.FlagHome, homeDir, "The Blobstream deployer home directory") - cmd.Flags().String(base.FlagEVMPassphrase, "", "the evm account passphrase (if not specified as a flag, it will be asked interactively)") + base.AddHomeFlag(cmd, ServiceNameDeployer, homeDir) base.AddGRPCInsecureFlag(cmd) return cmd } type deployConfig struct { - *base.Config + base.Config evmRPC string coreRPC, coreGRPC string evmChainID uint64 @@ -62,77 +44,74 @@ type deployConfig struct { } func parseDeployFlags(cmd *cobra.Command) (deployConfig, error) { - evmAccAddr, err := cmd.Flags().GetString(FlagEVMAccAddress) + evmAccAddr, _, err := base.GetEVMAccAddressFlag(cmd) if err != nil { return deployConfig{}, err } if evmAccAddr == "" { return deployConfig{}, errors.New("the evm account address should be specified") } - evmChainID, err := cmd.Flags().GetUint64(FlagEVMChainID) - if err != nil { - return deployConfig{}, err - } - coreGRPCHost, err := cmd.Flags().GetString(FlagCoreGRPCHost) + + evmChainID, _, err := base.GetEVMChainIDFlag(cmd) if err != nil { return deployConfig{}, err } - coreGRPCPort, err := cmd.Flags().GetUint(FlagCoreGRPCPort) + + coreRPC, _, err := base.GetCoreRPCFlag(cmd) if err != nil { return deployConfig{}, err } - coreRPCHost, err := cmd.Flags().GetString(FlagCoreRPCHost) - if err != nil { - return deployConfig{}, err + if !strings.HasPrefix(coreRPC, "tcp://") { + coreRPC = fmt.Sprintf("tcp://%s", coreRPC) } - coreRPCPort, err := cmd.Flags().GetUint(FlagCoreRPCPort) + + coreGRPC, _, err := base.GetCoreGRPCFlag(cmd) if err != nil { return deployConfig{}, err } - evmRPC, err := cmd.Flags().GetString(FlagEVMRPC) + + evmRPC, _, err := base.GetEVMRPCFlag(cmd) if err != nil { return deployConfig{}, err } - startingNonce, err := cmd.Flags().GetString(FlagStartingNonce) + + startingNonce, _, err := base.GetStartingNonceFlag(cmd) if err != nil { return deployConfig{}, err } - evmGasLimit, err := cmd.Flags().GetUint64(FlagEVMGasLimit) + + evmGasLimit, _, err := base.GetEVMGasLimitFlag(cmd) if err != nil { return deployConfig{}, err } - homeDir, err := cmd.Flags().GetString(base.FlagHome) + + homeDir, _, err := base.GetHomeFlag(cmd) if err != nil { return deployConfig{}, err } - if homeDir == "" { - var err error - homeDir, err = base.DefaultServicePath(ServiceNameDeployer) - if err != nil { - return deployConfig{}, err - } - } - passphrase, err := cmd.Flags().GetString(base.FlagEVMPassphrase) + + passphrase, _, err := base.GetEVMPassphraseFlag(cmd) if err != nil { return deployConfig{}, err } - grpcInsecure, err := cmd.Flags().GetBool(base.FlagGRPCInsecure) + + grpcInsecure, _, err := base.GetGRPCInsecureFlag(cmd) if err != nil { return deployConfig{}, err } return deployConfig{ - evmAccAddress: evmAccAddr, - evmChainID: evmChainID, - coreGRPC: fmt.Sprintf("%s:%d", coreGRPCHost, coreGRPCPort), - coreRPC: fmt.Sprintf("tcp://%s:%d", coreRPCHost, coreRPCPort), - evmRPC: evmRPC, - startingNonce: startingNonce, - evmGasLimit: evmGasLimit, - Config: &base.Config{ + Config: base.Config{ Home: homeDir, EVMPassphrase: passphrase, }, - grpcInsecure: grpcInsecure, + evmRPC: evmRPC, + coreRPC: coreRPC, + coreGRPC: coreGRPC, + evmChainID: evmChainID, + evmAccAddress: evmAccAddr, + startingNonce: startingNonce, + evmGasLimit: evmGasLimit, + grpcInsecure: grpcInsecure, }, nil } diff --git a/cmd/blobstream/keys/evm/evm.go b/cmd/blobstream/keys/evm/evm.go index 38c9fdce..300d118b 100644 --- a/cmd/blobstream/keys/evm/evm.go +++ b/cmd/blobstream/keys/evm/evm.go @@ -630,6 +630,7 @@ func GetPassphrase() (string, error) { if err != nil { return "", err } + fmt.Println() return string(bzPassphrase), nil } diff --git a/cmd/blobstream/orchestrator/cmd.go b/cmd/blobstream/orchestrator/cmd.go index 091a43d0..aee75815 100644 --- a/cmd/blobstream/orchestrator/cmd.go +++ b/cmd/blobstream/orchestrator/cmd.go @@ -3,8 +3,11 @@ package orchestrator import ( "context" "os" + "path/filepath" "time" + "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" + "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/common" evm2 "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/keys/evm" "github.com/celestiaorg/orchestrator-relayer/p2p" @@ -44,20 +47,32 @@ func Start() *cobra.Command { Use: "start ", Short: "Starts the Blobstream orchestrator to sign attestations", RunE: func(cmd *cobra.Command, args []string) error { - config, err := parseOrchestratorFlags(cmd) + logger := tmlog.NewTMLogger(os.Stdout) + + homeDir, err := base.GetHomeDirectory(cmd, ServiceNameOrchestrator) if err != nil { return err } + logger.Debug("initializing orchestrator", "home", homeDir) - logger := tmlog.NewTMLogger(os.Stdout) - logger.Debug("initializing orchestrator") + fileConfig, err := LoadFileConfiguration(homeDir) + if err != nil { + return err + } + config, err := parseOrchestratorFlags(cmd, fileConfig) + if err != nil { + return err + } + if err := config.ValidateBasics(); err != nil { + return err + } ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() stopFuncs := make([]func() error, 0) - tmQuerier, appQuerier, stops, err := common.NewTmAndAppQuerier(logger, config.coreRPC, config.coreGRPC, config.grpcInsecure) + tmQuerier, appQuerier, stops, err := common.NewTmAndAppQuerier(logger, config.CoreRPC, config.CoreGRPC, config.GRPCInsecure) stopFuncs = append(stopFuncs, stops...) if err != nil { return err @@ -75,9 +90,9 @@ func Start() *cobra.Command { return err } - logger.Info("loading EVM account", "address", config.evmAccAddress) + logger.Info("loading EVM account", "address", config.EvmAccAddress) - acc, err := evm2.GetAccountFromStoreAndUnlockIt(s.EVMKeyStore, config.evmAccAddress, config.EVMPassphrase) + acc, err := evm2.GetAccountFromStoreAndUnlockIt(s.EVMKeyStore, config.EvmAccAddress, config.EVMPassphrase) stopFuncs = append(stopFuncs, func() error { return s.EVMKeyStore.Lock(acc.Address) }) if err != nil { return err @@ -86,7 +101,7 @@ func Start() *cobra.Command { // creating the data store dataStore := dssync.MutexWrap(s.DataStore) - dht, err := common.CreateDHTAndWaitForPeers(ctx, logger, s.P2PKeyStore, config.p2pNickname, config.p2pListenAddr, config.bootstrappers, dataStore) + dht, err := common.CreateDHTAndWaitForPeers(ctx, logger, s.P2PKeyStore, config.P2pNickname, config.P2PListenAddr, config.Bootstrappers, dataStore) if err != nil { return err } @@ -169,6 +184,14 @@ func Init() *cobra.Command { return err } + configPath := filepath.Join(config.home, "config") + configFilePath := filepath.Join(configPath, "config.toml") + conf := DefaultStartConfig() + err = initializeConfigFile(configFilePath, configPath, conf) + if err != nil { + return err + } + return nil }, } diff --git a/cmd/blobstream/orchestrator/config.go b/cmd/blobstream/orchestrator/config.go index 5747b940..e4f48bdb 100644 --- a/cmd/blobstream/orchestrator/config.go +++ b/cmd/blobstream/orchestrator/config.go @@ -1,8 +1,14 @@ package orchestrator import ( - "errors" + "bytes" "fmt" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/spf13/viper" "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" "github.com/cosmos/cosmos-sdk/client/flags" @@ -10,30 +16,47 @@ import ( ) const ( - FlagCoreGRPCHost = "core.grpc.host" - FlagCoreGRPCPort = "core.grpc.port" - FlagEVMAccAddress = "evm.account" - FlagCoreRPCHost = "core.rpc.host" - FlagCoreRPCPort = "core.rpc.port" ServiceNameOrchestrator = "orchestrator" ) +const DefaultConfigTemplate = `# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +############################################################################### +### RPC Configuration ### +############################################################################### + +# Specify the celestia app rest rpc address. +core-rpc = "{{ .CoreRPC }}" + +# Specify the celestia app grpc address. +core-grpc = "{{ .CoreGRPC }}" + +# allow gRPC over insecure channels, if not TLS the server must use TLS. +grpc-insecure = {{ .GRPCInsecure }} + +############################################################################### +### P2P Configuration ### +############################################################################### + +# Comma-separated multiaddresses of p2p peers to connect to. +# Example: "/ip4/127.0.0.1/tcp/30001/p2p/12D3K...,/ip4/127.0.0.1/tcp/30000/p2p/12D3K..." +bootstrappers = "{{ .Bootstrappers }}" + +# MultiAddr for the p2p peer to listen on. +listen-addr = "{{ .P2PListenAddr }}" +` + func addOrchestratorFlags(cmd *cobra.Command) *cobra.Command { - cmd.Flags().String(FlagCoreRPCHost, "localhost", "Specify the rest rpc address host") - cmd.Flags().Uint(FlagCoreRPCPort, 26657, "Specify the rest rpc address port") - cmd.Flags().String(FlagCoreGRPCHost, "localhost", "Specify the grpc address host") - cmd.Flags().Uint(FlagCoreGRPCPort, 9090, "Specify the grpc address port") - cmd.Flags().String( - FlagEVMAccAddress, - "", - "Specify the EVM account address to use for signing (Note: the private key should be in the keystore)", - ) + base.AddCoreRPCFlag(cmd) + base.AddCoreGRPCFlag(cmd) + base.AddEVMAccAddressFlag(cmd) + base.AddEVMPassphraseFlag(cmd) homeDir, err := base.DefaultServicePath(ServiceNameOrchestrator) if err != nil { panic(err) } - cmd.Flags().String(base.FlagHome, homeDir, "The Blobstream orchestrator home directory") - cmd.Flags().String(base.FlagEVMPassphrase, "", "the evm account passphrase (if not specified as a flag, it will be asked interactively)") + base.AddHomeFlag(cmd, ServiceNameOrchestrator, homeDir) base.AddP2PNicknameFlag(cmd) base.AddP2PListenAddressFlag(cmd) base.AddBootstrappersFlag(cmd) @@ -42,83 +65,104 @@ func addOrchestratorFlags(cmd *cobra.Command) *cobra.Command { } type StartConfig struct { - *base.Config - coreGRPC, coreRPC string - evmAccAddress string - bootstrappers, p2pListenAddr string - p2pNickname string - grpcInsecure bool + base.Config + CoreGRPC string `mapstructure:"core-grpc" json:"core-grpc"` + CoreRPC string `mapstructure:"core-rpc" json:"core-rpc"` + EvmAccAddress string + Bootstrappers string `mapstructure:"bootstrappers" json:"bootstrappers"` + P2PListenAddr string `mapstructure:"listen-addr" json:"listen-addr"` + P2pNickname string + GRPCInsecure bool `mapstructure:"grpc-insecure" json:"grpc-insecure"` } -func parseOrchestratorFlags(cmd *cobra.Command) (StartConfig, error) { - evmAccAddr, err := cmd.Flags().GetString(FlagEVMAccAddress) - if err != nil { - return StartConfig{}, err +func DefaultStartConfig() *StartConfig { + return &StartConfig{ + CoreRPC: "tcp://localhost:26657", + CoreGRPC: "localhost:9090", + Bootstrappers: "", + P2PListenAddr: "/ip4/0.0.0.0/tcp/30000", + GRPCInsecure: true, } - if evmAccAddr == "" { - return StartConfig{}, errors.New("the evm account address should be specified") +} + +func (cfg StartConfig) ValidateBasics() error { + if err := base.ValidateEVMAddress(cfg.EvmAccAddress); err != nil { + return fmt.Errorf("%s: flag --%s", err.Error(), base.FlagEVMAccAddress) } - coreRPCHost, err := cmd.Flags().GetString(FlagCoreRPCHost) + return nil +} + +func parseOrchestratorFlags(cmd *cobra.Command, startConf *StartConfig) (StartConfig, error) { + evmAccAddr, _, err := base.GetEVMAccAddressFlag(cmd) if err != nil { return StartConfig{}, err } - coreRPCPort, err := cmd.Flags().GetUint(FlagCoreRPCPort) + startConf.EvmAccAddress = evmAccAddr + + coreRPC, changed, err := base.GetCoreRPCFlag(cmd) if err != nil { return StartConfig{}, err } - coreGRPCHost, err := cmd.Flags().GetString(FlagCoreGRPCHost) - if err != nil { - return StartConfig{}, err + if changed { + if !strings.HasPrefix(coreRPC, "tcp://") { + coreRPC = fmt.Sprintf("tcp://%s", coreRPC) + } + startConf.CoreRPC = coreRPC } - coreGRPCPort, err := cmd.Flags().GetUint(FlagCoreGRPCPort) + + coreGRPC, changed, err := base.GetCoreGRPCFlag(cmd) if err != nil { return StartConfig{}, err } - bootstrappers, err := cmd.Flags().GetString(base.FlagBootstrappers) + if changed { + startConf.CoreGRPC = coreGRPC + } + + bootstrappers, changed, err := base.GetBootstrappersFlag(cmd) if err != nil { return StartConfig{}, err } - p2pListenAddress, err := cmd.Flags().GetString(base.FlagP2PListenAddress) + if changed { + startConf.Bootstrappers = bootstrappers + } + + p2pListenAddress, changed, err := base.GetP2PListenAddressFlag(cmd) if err != nil { return StartConfig{}, err } - p2pNickname, err := cmd.Flags().GetString(base.FlagP2PNickname) + if changed { + startConf.P2PListenAddr = p2pListenAddress + } + + p2pNickname, changed, err := base.GetP2PNicknameFlag(cmd) if err != nil { return StartConfig{}, err } - homeDir, err := cmd.Flags().GetString(base.FlagHome) + if changed { + startConf.P2pNickname = p2pNickname + } + + homeDir, _, err := base.GetHomeFlag(cmd) if err != nil { return StartConfig{}, err } - if homeDir == "" { - var err error - homeDir, err = base.DefaultServicePath(ServiceNameOrchestrator) - if err != nil { - return StartConfig{}, err - } - } - passphrase, err := cmd.Flags().GetString(base.FlagEVMPassphrase) + startConf.Home = homeDir + + passphrase, _, err := base.GetEVMPassphraseFlag(cmd) if err != nil { return StartConfig{}, err } - grpcInsecure, err := cmd.Flags().GetBool(base.FlagGRPCInsecure) + startConf.EVMPassphrase = passphrase + + grpcInsecure, changed, err := base.GetGRPCInsecureFlag(cmd) if err != nil { return StartConfig{}, err } + if changed { + startConf.GRPCInsecure = grpcInsecure + } - return StartConfig{ - evmAccAddress: evmAccAddr, - coreGRPC: fmt.Sprintf("%s:%d", coreGRPCHost, coreGRPCPort), - coreRPC: fmt.Sprintf("tcp://%s:%d", coreRPCHost, coreRPCPort), - bootstrappers: bootstrappers, - p2pNickname: p2pNickname, - p2pListenAddr: p2pListenAddress, - Config: &base.Config{ - Home: homeDir, - EVMPassphrase: passphrase, - }, - grpcInsecure: grpcInsecure, - }, nil + return *startConf, nil } func addInitFlags(cmd *cobra.Command) *cobra.Command { @@ -126,7 +170,7 @@ func addInitFlags(cmd *cobra.Command) *cobra.Command { if err != nil { panic(err) } - cmd.Flags().String(base.FlagHome, homeDir, "The Blobstream orchestrator home directory") + base.AddHomeFlag(cmd, ServiceNameOrchestrator, homeDir) return cmd } @@ -151,3 +195,72 @@ func parseInitFlags(cmd *cobra.Command) (InitConfig, error) { home: homeDir, }, nil } + +func LoadFileConfiguration(homeDir string) (*StartConfig, error) { + v := viper.New() + v.SetEnvPrefix("") + v.AutomaticEnv() + configPath := filepath.Join(homeDir, "config") + configFilePath := filepath.Join(configPath, "config.toml") + conf := DefaultStartConfig() + + // if config.toml file does not exist, we create it and write default ClientConfig values into it. + if _, err := os.Stat(configFilePath); os.IsNotExist(err) { + if err := initializeConfigFile(configFilePath, configPath, conf); err != nil { + return nil, err + } + } + + conf, err := getStartConfig(v, configPath) + if err != nil { + return nil, fmt.Errorf("couldn't get client config: %v", err) + } + return conf, nil +} + +func initializeConfigFile(configFilePath string, configPath string, conf *StartConfig) error { + if err := base.EnsureConfigPath(configPath); err != nil { + return fmt.Errorf("couldn't make orchestrator config: %v", err) + } + + if err := writeConfigToFile(configFilePath, conf); err != nil { + return fmt.Errorf("could not write orchestrator config to the file: %v", err) + } + return nil +} + +// writeConfigToFile parses DefaultConfigTemplate, renders config using the template and writes it to +// configFilePath. +func writeConfigToFile(configFilePath string, config *StartConfig) error { + var buffer bytes.Buffer + + tmpl := template.New("orchestratorConfigFileTemplate") + configTemplate, err := tmpl.Parse(DefaultConfigTemplate) + if err != nil { + return err + } + + if err := configTemplate.Execute(&buffer, config); err != nil { + return err + } + + return os.WriteFile(configFilePath, buffer.Bytes(), 0o600) +} + +// getStartConfig reads values from config.toml file and unmarshalls them into StartConfig +func getStartConfig(v *viper.Viper, configPath string) (*StartConfig, error) { + v.AddConfigPath(configPath) + v.SetConfigName("config") + v.SetConfigType("toml") + + if err := v.ReadInConfig(); err != nil { + return nil, err + } + + conf := new(StartConfig) + if err := v.Unmarshal(conf); err != nil { + return nil, err + } + + return conf, nil +} diff --git a/cmd/blobstream/query/config.go b/cmd/blobstream/query/config.go index 56a73ed9..530bab99 100644 --- a/cmd/blobstream/query/config.go +++ b/cmd/blobstream/query/config.go @@ -2,10 +2,10 @@ package query import ( "fmt" + "strings" "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" - "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/relayer" "github.com/spf13/cobra" ) @@ -15,10 +15,8 @@ const ( ) func addFlags(cmd *cobra.Command) *cobra.Command { - cmd.Flags().String(relayer.FlagCoreGRPCHost, "localhost", "Specify the grpc address host") - cmd.Flags().Uint(relayer.FlagCoreGRPCPort, 9090, "Specify the grpc address port") - cmd.Flags().String(relayer.FlagCoreRPCHost, "localhost", "Specify the rest rpc address host") - cmd.Flags().Uint(relayer.FlagCoreRPCPort, 26657, "Specify the rest rpc address") + base.AddCoreGRPCFlag(cmd) + base.AddCoreRPCFlag(cmd) cmd.Flags().String(FlagP2PNode, "", "P2P target node multiaddress (eg. /ip4/127.0.0.1/tcp/30000/p2p/12D3KooWBSMasWzRSRKXREhediFUwABNZwzJbkZcYz5rYr9Zdmfn)") cmd.Flags().String(FlagOutputFile, "", "Path to an output file path if the results need to be written to a json file. Leaving it as empty will result in printing the result to stdout") base.AddGRPCInsecureFlag(cmd) @@ -34,19 +32,14 @@ type Config struct { } func parseFlags(cmd *cobra.Command) (Config, error) { - coreRPCHost, err := cmd.Flags().GetString(relayer.FlagCoreRPCHost) + coreRPC, err := cmd.Flags().GetString(base.FlagCoreRPC) if err != nil { return Config{}, err } - coreRPCPort, err := cmd.Flags().GetUint(relayer.FlagCoreRPCPort) - if err != nil { - return Config{}, err - } - coreGRPCHost, err := cmd.Flags().GetString(relayer.FlagCoreGRPCHost) - if err != nil { - return Config{}, err + if !strings.HasPrefix(coreRPC, "tcp://") { + coreRPC = fmt.Sprintf("tcp://%s", coreRPC) } - coreGRPCPort, err := cmd.Flags().GetUint(relayer.FlagCoreGRPCPort) + coreGRPC, err := cmd.Flags().GetString(base.FlagCoreGRPC) if err != nil { return Config{}, err } @@ -63,8 +56,8 @@ func parseFlags(cmd *cobra.Command) (Config, error) { return Config{}, err } return Config{ - coreGRPC: fmt.Sprintf("%s:%d", coreGRPCHost, coreGRPCPort), - coreRPC: fmt.Sprintf("tcp://%s:%d", coreRPCHost, coreRPCPort), + coreGRPC: coreGRPC, + coreRPC: coreRPC, targetNode: targetNode, outputFile: outputFile, grpcInsecure: grpcInsecure, diff --git a/cmd/blobstream/relayer/cmd.go b/cmd/blobstream/relayer/cmd.go index e4e35c32..3b0c3991 100644 --- a/cmd/blobstream/relayer/cmd.go +++ b/cmd/blobstream/relayer/cmd.go @@ -3,8 +3,13 @@ package relayer import ( "context" "os" + "path/filepath" "time" + "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" + + ethcmn "github.com/ethereum/go-ethereum/common" + blobstreamwrapper "github.com/celestiaorg/blobstream-contracts/v4/wrappers/Blobstream.sol" evm2 "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/keys/evm" @@ -72,6 +77,14 @@ func Init() *cobra.Command { return err } + configPath := filepath.Join(config.home, "config") + configFilePath := filepath.Join(configPath, "config.toml") + conf := DefaultStartConfig() + err = initializeConfigFile(configFilePath, configPath, conf) + if err != nil { + return err + } + return nil }, } @@ -83,21 +96,32 @@ func Start() *cobra.Command { Use: "start ", Short: "Runs the Blobstream relayer to submit attestations to the target EVM chain", RunE: func(cmd *cobra.Command, args []string) error { - config, err := parseRelayerStartFlags(cmd) + logger := tmlog.NewTMLogger(os.Stdout) + + homeDir, err := base.GetHomeDirectory(cmd, ServiceNameRelayer) if err != nil { return err } + logger.Debug("initializing relayer", "home", homeDir) - // creating the logger - logger := tmlog.NewTMLogger(os.Stdout) - logger.Debug("initializing relayer") + fileConfig, err := LoadFileConfiguration(homeDir) + if err != nil { + return err + } + config, err := parseRelayerStartFlags(cmd, fileConfig) + if err != nil { + return err + } + if err := config.ValidateBasics(); err != nil { + return err + } ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() stopFuncs := make([]func() error, 0) - tmQuerier, appQuerier, stops, err := common.NewTmAndAppQuerier(logger, config.coreRPC, config.coreGRPC, config.grpcInsecure) + tmQuerier, appQuerier, stops, err := common.NewTmAndAppQuerier(logger, config.CoreRPC, config.CoreGRPC, config.GrpcInsecure) stopFuncs = append(stopFuncs, stops...) if err != nil { return err @@ -126,7 +150,7 @@ func Start() *cobra.Command { // creating the data store dataStore := dssync.MutexWrap(s.DataStore) - dht, err := common.CreateDHTAndWaitForPeers(ctx, logger, s.P2PKeyStore, config.p2pNickname, config.p2pListenAddr, config.bootstrappers, dataStore) + dht, err := common.CreateDHTAndWaitForPeers(ctx, logger, s.P2PKeyStore, config.p2pNickname, config.P2PListenAddr, config.Bootstrappers, dataStore) if err != nil { return err } @@ -146,23 +170,23 @@ func Start() *cobra.Command { }() // connecting to a Blobstream contract - ethClient, err := ethclient.Dial(config.evmRPC) + ethClient, err := ethclient.Dial(config.EvmRPC) if err != nil { return err } defer ethClient.Close() - blobStreamWrapper, err := blobstreamwrapper.NewWrappers(config.contractAddr, ethClient) + blobstreamWrapper, err := blobstreamwrapper.NewWrappers(ethcmn.HexToAddress(config.ContractAddr), ethClient) if err != nil { return err } evmClient := evm.NewClient( logger, - blobStreamWrapper, + blobstreamWrapper, s.EVMKeyStore, &acc, - config.evmRPC, - config.evmGasLimit, + config.EvmRPC, + config.EvmGasLimit, ) relay := relayer.NewRelayer( diff --git a/cmd/blobstream/relayer/config.go b/cmd/blobstream/relayer/config.go index 630a2bd7..adbca550 100644 --- a/cmd/blobstream/relayer/config.go +++ b/cmd/blobstream/relayer/config.go @@ -1,48 +1,84 @@ package relayer import ( - "errors" + "bytes" "fmt" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/celestiaorg/orchestrator-relayer/cmd/blobstream/base" - "github.com/celestiaorg/orchestrator-relayer/evm" "github.com/spf13/cobra" - - ethcmn "github.com/ethereum/go-ethereum/common" ) const ( - FlagEVMAccAddress = "evm.account" - FlagEVMChainID = "evm.chain-id" - FlagCoreGRPCHost = "core.grpc.host" - FlagCoreGRPCPort = "core.grpc.port" - FlagCoreRPCHost = "core.rpc.host" - FlagCoreRPCPort = "core.rpc.port" - FlagEVMRPC = "evm.rpc" - FlagContractAddress = "evm.contract-address" - FlagEVMGasLimit = "evm.gas-limit" - ServiceNameRelayer = "relayer" + ServiceNameRelayer = "relayer" ) +const DefaultConfigTemplate = `# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +############################################################################### +### RPC Configuration ### +############################################################################### + +# Celestia app rest rpc address. +core-rpc = "{{ .CoreRPC }}" + +# Celestia app grpc address. +core-grpc = "{{ .CoreGRPC }}" + +# Allow gRPC over insecure channels, if not TLS the server must use TLS. +grpc-insecure = {{ .GrpcInsecure }} + +############################################################################### +### P2P Configuration ### +############################################################################### + +# Comma-separated multiaddresses of p2p peers to connect to. +# Example: "/ip4/127.0.0.1/tcp/30001/p2p/12D3K...,/ip4/127.0.0.1/tcp/30000/p2p/12D3K..." +bootstrappers = "{{ .Bootstrappers }}" + +# MultiAddr for the p2p peer to listen on. +listen-addr = "{{ .P2PListenAddr }}" + +############################################################################### +### EVM Configuration ### +############################################################################### + +# Ethereum rpc address. +evm-rpc = "{{ .EvmRPC }}" + +# Evm chain id. +evm-chain-id = "{{ .EvmChainID }}" + +# Contract address at which Blobstream is deployed. +contract-address = "{{ .ContractAddr }}" + +# Evm gas limit. +gas-limit = "{{ .EvmGasLimit }}" +` + func addRelayerStartFlags(cmd *cobra.Command) *cobra.Command { - cmd.Flags().String(FlagEVMAccAddress, "", "Specify the EVM account address to use for signing (Note: the private key should be in the keystore)") - cmd.Flags().Uint64(FlagEVMChainID, 5, "Specify the evm chain id") - cmd.Flags().String(FlagCoreGRPCHost, "localhost", "Specify the grpc address host") - cmd.Flags().Uint(FlagCoreGRPCPort, 9090, "Specify the grpc address port") - cmd.Flags().String(FlagCoreRPCHost, "localhost", "Specify the rest rpc address host") - cmd.Flags().Uint(FlagCoreRPCPort, 26657, "Specify the rest rpc address port") - cmd.Flags().String(FlagEVMRPC, "http://localhost:8545", "Specify the ethereum rpc address") - cmd.Flags().String(FlagContractAddress, "", "Specify the contract at which the Blobstream is deployed") - cmd.Flags().Uint64(FlagEVMGasLimit, evm.DefaultEVMGasLimit, "Specify the evm gas limit") homeDir, err := base.DefaultServicePath(ServiceNameRelayer) if err != nil { panic(err) } - cmd.Flags().String(base.FlagHome, homeDir, "The Blobstream relayer home directory") - cmd.Flags().String(base.FlagEVMPassphrase, "", "the evm account passphrase (if not specified as a flag, it will be asked interactively)") + base.AddHomeFlag(cmd, ServiceNameRelayer, homeDir) + base.AddEVMAccAddressFlag(cmd) + base.AddEVMChainIDFlag(cmd) + base.AddCoreGRPCFlag(cmd) + base.AddCoreRPCFlag(cmd) + base.AddEVMRPCFlag(cmd) + base.AddEVMContractAddressFlag(cmd) + base.AddEVMGasLimitFlag(cmd) + base.AddEVMPassphraseFlag(cmd) base.AddP2PNicknameFlag(cmd) base.AddP2PListenAddressFlag(cmd) base.AddBootstrappersFlag(cmd) @@ -52,113 +88,144 @@ func addRelayerStartFlags(cmd *cobra.Command) *cobra.Command { } type StartConfig struct { - *base.Config - evmChainID uint64 - evmRPC, coreGRPC, coreRPC string - evmAccAddress string - contractAddr ethcmn.Address - evmGasLimit uint64 - bootstrappers, p2pListenAddr string - p2pNickname string - grpcInsecure bool + base.Config + EvmChainID uint64 `mapstructure:"evm-chain-id" json:"evm-chain-id"` + EvmRPC string `mapstructure:"evm-rpc" json:"evm-rpc"` + CoreGRPC string `mapstructure:"core-grpc" json:"core-grpc"` + CoreRPC string `mapstructure:"core-rpc" json:"core-rpc"` + evmAccAddress string + ContractAddr string `mapstructure:"contract-address" json:"contract-address"` + EvmGasLimit uint64 `mapstructure:"gas-limit" json:"gas-limit"` + Bootstrappers string `mapstructure:"bootstrappers" json:"bootstrappers"` + P2PListenAddr string `mapstructure:"listen-addr" json:"listen-addr"` + p2pNickname string + GrpcInsecure bool `mapstructure:"grpc-insecure" json:"grpc-insecure"` } -func parseRelayerStartFlags(cmd *cobra.Command) (StartConfig, error) { - evmAccAddr, err := cmd.Flags().GetString(FlagEVMAccAddress) - if err != nil { - return StartConfig{}, err +func DefaultStartConfig() *StartConfig { + return &StartConfig{ + CoreRPC: "tcp://localhost:26657", + CoreGRPC: "localhost:9090", + Bootstrappers: "", + P2PListenAddr: "/ip4/0.0.0.0/tcp/30000", + GrpcInsecure: true, + EvmChainID: 5, + EvmRPC: "http://localhost:8545", + EvmGasLimit: 2500000, } - if evmAccAddr == "" { - return StartConfig{}, errors.New("the evm account address should be specified") +} + +func (cfg StartConfig) ValidateBasics() error { + if err := base.ValidateEVMAddress(cfg.evmAccAddress); err != nil { + return fmt.Errorf("%s: flag --%s", err.Error(), base.FlagEVMAccAddress) } - evmChainID, err := cmd.Flags().GetUint64(FlagEVMChainID) - if err != nil { - return StartConfig{}, err + if err := base.ValidateEVMAddress(cfg.ContractAddr); err != nil { + return fmt.Errorf("%s: flag --%s", err.Error(), base.FlagEVMContractAddress) } - coreRPCHost, err := cmd.Flags().GetString(FlagCoreRPCHost) + return nil +} + +func parseRelayerStartFlags(cmd *cobra.Command, fileConfig *StartConfig) (StartConfig, error) { + evmAccAddr, _, err := base.GetEVMAccAddressFlag(cmd) if err != nil { return StartConfig{}, err } - coreRPCPort, err := cmd.Flags().GetUint(FlagCoreRPCPort) + fileConfig.evmAccAddress = evmAccAddr + + evmChainID, changed, err := base.GetEVMChainIDFlag(cmd) if err != nil { return StartConfig{}, err } - coreGRPCHost, err := cmd.Flags().GetString(FlagCoreGRPCHost) + if changed { + fileConfig.EvmChainID = evmChainID + } + + coreRPC, changed, err := base.GetCoreRPCFlag(cmd) if err != nil { return StartConfig{}, err } - coreGRPCPort, err := cmd.Flags().GetUint(FlagCoreGRPCPort) + if changed { + if !strings.HasPrefix(coreRPC, "tcp://") { + coreRPC = fmt.Sprintf("tcp://%s", coreRPC) + } + fileConfig.CoreRPC = coreRPC + } + + coreGRPC, changed, err := base.GetCoreGRPCFlag(cmd) if err != nil { return StartConfig{}, err } - contractAddr, err := cmd.Flags().GetString(FlagContractAddress) + if changed { + fileConfig.CoreGRPC = coreGRPC + } + + contractAddr, changed, err := base.GetEVMContractAddressFlag(cmd) if err != nil { return StartConfig{}, err } - if contractAddr == "" { - return StartConfig{}, fmt.Errorf("contract address flag is required: %s", FlagContractAddress) - } - if !ethcmn.IsHexAddress(contractAddr) { - return StartConfig{}, fmt.Errorf("valid contract address flag is required: %s", FlagContractAddress) + if changed { + fileConfig.ContractAddr = contractAddr } - address := ethcmn.HexToAddress(contractAddr) - evmRPC, err := cmd.Flags().GetString(FlagEVMRPC) + + evmRPC, changed, err := base.GetEVMRPCFlag(cmd) if err != nil { return StartConfig{}, err } - evmGasLimit, err := cmd.Flags().GetUint64(FlagEVMGasLimit) + if changed { + fileConfig.EvmRPC = evmRPC + } + + evmGasLimit, changed, err := base.GetEVMGasLimitFlag(cmd) if err != nil { return StartConfig{}, err } - bootstrappers, err := cmd.Flags().GetString(base.FlagBootstrappers) + if changed { + fileConfig.EvmGasLimit = evmGasLimit + } + + bootstrappers, changed, err := base.GetBootstrappersFlag(cmd) if err != nil { return StartConfig{}, err } - p2pListenAddress, err := cmd.Flags().GetString(base.FlagP2PListenAddress) + if changed { + fileConfig.Bootstrappers = bootstrappers + } + + p2pListenAddress, changed, err := base.GetP2PListenAddressFlag(cmd) if err != nil { return StartConfig{}, err } - p2pNickname, err := cmd.Flags().GetString(base.FlagP2PNickname) + if changed { + fileConfig.P2PListenAddr = p2pListenAddress + } + + p2pNickname, _, err := base.GetP2PNicknameFlag(cmd) if err != nil { return StartConfig{}, err } - homeDir, err := cmd.Flags().GetString(base.FlagHome) + fileConfig.p2pNickname = p2pNickname + + homeDir, _, err := base.GetHomeFlag(cmd) if err != nil { return StartConfig{}, err } - if homeDir == "" { - var err error - homeDir, err = base.DefaultServicePath(ServiceNameRelayer) - if err != nil { - return StartConfig{}, err - } - } - passphrase, err := cmd.Flags().GetString(base.FlagEVMPassphrase) + fileConfig.Home = homeDir + + passphrase, _, err := base.GetEVMPassphraseFlag(cmd) if err != nil { return StartConfig{}, err } - grpcInsecure, err := cmd.Flags().GetBool(base.FlagGRPCInsecure) + fileConfig.EVMPassphrase = passphrase + + grpcInsecure, changed, err := base.GetGRPCInsecureFlag(cmd) if err != nil { return StartConfig{}, err } + if changed { + fileConfig.GrpcInsecure = grpcInsecure + } - return StartConfig{ - evmAccAddress: evmAccAddr, - evmChainID: evmChainID, - coreGRPC: fmt.Sprintf("%s:%d", coreGRPCHost, coreGRPCPort), - coreRPC: fmt.Sprintf("tcp://%s:%d", coreRPCHost, coreRPCPort), - contractAddr: address, - evmRPC: evmRPC, - evmGasLimit: evmGasLimit, - bootstrappers: bootstrappers, - p2pListenAddr: p2pListenAddress, - p2pNickname: p2pNickname, - Config: &base.Config{ - Home: homeDir, - EVMPassphrase: passphrase, - }, - grpcInsecure: grpcInsecure, - }, nil + return *fileConfig, nil } func addInitFlags(cmd *cobra.Command) *cobra.Command { @@ -166,7 +233,7 @@ func addInitFlags(cmd *cobra.Command) *cobra.Command { if err != nil { panic(err) } - cmd.Flags().String(base.FlagHome, homeDir, "The Blobstream relayer home directory") + base.AddHomeFlag(cmd, ServiceNameRelayer, homeDir) return cmd } @@ -191,3 +258,72 @@ func parseInitFlags(cmd *cobra.Command) (InitConfig, error) { home: homeDir, }, nil } + +func LoadFileConfiguration(homeDir string) (*StartConfig, error) { + v := viper.New() + v.SetEnvPrefix("") + v.AutomaticEnv() + configPath := filepath.Join(homeDir, "config") + configFilePath := filepath.Join(configPath, "config.toml") + conf := DefaultStartConfig() + + // if config.toml file does not exist, we create it and write default ClientConfig values into it. + if _, err := os.Stat(configFilePath); os.IsNotExist(err) { + if err := initializeConfigFile(configFilePath, configPath, conf); err != nil { + return nil, err + } + } + + conf, err := getStartConfig(v, configPath) + if err != nil { + return nil, fmt.Errorf("couldn't get client config: %v", err) + } + return conf, nil +} + +func initializeConfigFile(configFilePath string, configPath string, conf *StartConfig) error { + if err := base.EnsureConfigPath(configPath); err != nil { + return fmt.Errorf("couldn't make relayer config: %v", err) + } + + if err := writeConfigToFile(configFilePath, conf); err != nil { + return fmt.Errorf("could not write relayer config to the file: %v", err) + } + return nil +} + +// writeConfigToFile parses DefaultConfigTemplate, renders config using the template and writes it to +// configFilePath. +func writeConfigToFile(configFilePath string, config *StartConfig) error { + var buffer bytes.Buffer + + tmpl := template.New("relayerConfigFileTemplate") + configTemplate, err := tmpl.Parse(DefaultConfigTemplate) + if err != nil { + return err + } + + if err := configTemplate.Execute(&buffer, config); err != nil { + return err + } + + return os.WriteFile(configFilePath, buffer.Bytes(), 0o600) +} + +// getStartConfig reads values from config.toml file and unmarshalls them into StartConfig +func getStartConfig(v *viper.Viper, configPath string) (*StartConfig, error) { + v.AddConfigPath(configPath) + v.SetConfigName("config") + v.SetConfigType("toml") + + if err := v.ReadInConfig(); err != nil { + return nil, err + } + + conf := new(StartConfig) + if err := v.Unmarshal(conf); err != nil { + return nil, err + } + + return conf, nil +} diff --git a/e2e/scripts/deploy_blobstream_contract.sh b/e2e/scripts/deploy_blobstream_contract.sh index 11c1f024..d53b068c 100644 --- a/e2e/scripts/deploy_blobstream_contract.sh +++ b/e2e/scripts/deploy_blobstream_contract.sh @@ -66,10 +66,8 @@ echo "deploying Blobstream contract..." /bin/blobstream deploy \ --evm.chain-id "${EVM_CHAIN_ID}" \ --evm.account "${EVM_ACCOUNT}" \ - --core.grpc.host "${CORE_GRPC_HOST}" \ - --core.grpc.port "${CORE_GRPC_PORT}" \ - --core.rpc.host "${CORE_RPC_HOST}" \ - --core.rpc.port "${CORE_RPC_PORT}" \ + --core.rpc="${CORE_RPC_HOST}:${CORE_RPC_PORT}" \ + --core.grpc="${CORE_GRPC_HOST}:${CORE_GRPC_PORT}" \ --grpc.insecure \ --starting-nonce "${STARTING_NONCE}" \ --evm.rpc "${EVM_ENDPOINT}" \ diff --git a/e2e/scripts/start_orchestrator_after_validator_created.sh b/e2e/scripts/start_orchestrator_after_validator_created.sh index 86d41f78..8f8fe0bb 100644 --- a/e2e/scripts/start_orchestrator_after_validator_created.sh +++ b/e2e/scripts/start_orchestrator_after_validator_created.sh @@ -49,10 +49,8 @@ then /bin/blobstream orchestrator start \ --evm.account="${EVM_ACCOUNT}" \ - --core.rpc.host="${CORE_RPC_HOST}" \ - --core.rpc.port="${CORE_RPC_PORT}" \ - --core.grpc.host="${CORE_GRPC_HOST}" \ - --core.grpc.port="${CORE_GRPC_PORT}" \ + --core.rpc="${CORE_RPC_HOST}:${CORE_RPC_PORT}" \ + --core.grpc="${CORE_GRPC_HOST}:${CORE_GRPC_PORT}" \ --grpc.insecure \ --p2p.nickname=key \ --p2p.listen-addr="${P2P_LISTEN}" \ @@ -63,10 +61,8 @@ else /bin/blobstream orchestrator start \ --evm.account="${EVM_ACCOUNT}" \ - --core.rpc.host="${CORE_RPC_HOST}" \ - --core.rpc.port="${CORE_RPC_PORT}" \ - --core.grpc.host="${CORE_GRPC_HOST}" \ - --core.grpc.port="${CORE_GRPC_PORT}" \ + --core.rpc="${CORE_RPC_HOST}:${CORE_RPC_PORT}" \ + --core.grpc="${CORE_GRPC_HOST}:${CORE_GRPC_PORT}" \ --grpc.insecure \ --p2p.listen-addr="${P2P_LISTEN}" \ --p2p.bootstrappers="${P2P_BOOTSTRAPPERS}" \ diff --git a/e2e/scripts/start_relayer.sh b/e2e/scripts/start_relayer.sh index 0f756641..9df35082 100644 --- a/e2e/scripts/start_relayer.sh +++ b/e2e/scripts/start_relayer.sh @@ -55,10 +55,8 @@ BLOBSTREAM_CONTRACT=$(cat /opt/blobstream_address.txt) sleep 5s /bin/blobstream relayer start \ --evm.account="${EVM_ACCOUNT}" \ - --core.rpc.host="${CORE_RPC_HOST}" \ - --core.rpc.port="${CORE_RPC_PORT}" \ - --core.grpc.host="${CORE_GRPC_HOST}" \ - --core.grpc.port="${CORE_GRPC_PORT}" \ + --core.rpc="${CORE_RPC_HOST}:${CORE_RPC_PORT}" \ + --core.grpc="${CORE_GRPC_HOST}:${CORE_GRPC_PORT}" \ --grpc.insecure \ --evm.chain-id="${EVM_CHAIN_ID}" \ --evm.rpc="${EVM_ENDPOINT}" \ diff --git a/go.mod b/go.mod index 5e3eea16..404d79b3 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/libp2p/go-libp2p-kad-dht v0.25.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-multiaddr v0.12.0 + github.com/spf13/viper v1.14.0 github.com/tendermint/tendermint v0.34.28 ) @@ -251,7 +252,6 @@ require ( github.com/spf13/afero v1.9.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.14.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/supranational/blst v0.3.11 // indirect