newton_cli/cli/
mod.rs

1use crate::{
2    commands::{
3        policy::PolicyCommand, policy_client::PolicyClientCommand, policy_data::PolicyDataCommand,
4        policy_files::PolicyFilesCommand, task::TaskCommand,
5    },
6    config::NewtonCliConfig,
7};
8use clap::{Parser, Subcommand};
9use newton_cli_runner::NewtonRunner;
10use newton_prover_core::config::{
11    log::{init_logger, LogFormat, LoggerConfig},
12    NewtonAvsConfigBuilder,
13};
14use std::{ffi::OsString, path::PathBuf};
15use tracing::info;
16
17/// newton protocol cli entry point interface
18#[derive(Debug, Parser)]
19#[command(author, about = "Newton protocol cli", long_about = None)]
20pub struct NewtonCli {
21    /// chain id
22    #[arg(long, value_name = "CHAIN_ID", global = true, env = "CHAIN_ID")]
23    chain_id: Option<u64>,
24
25    /// optional path to the configuration file
26    #[arg(long, value_name = "FILE", global = true)]
27    config_path: Option<PathBuf>,
28
29    /// log format
30    #[arg(
31        long,
32        value_enum,
33        default_value = "minimal",
34        global = true,
35        help = "Log format: full, compact, pretty, json, or minimal"
36    )]
37    log_format: LogFormat,
38
39    /// Suppress verbose config loading logs
40    #[arg(long, global = true)]
41    quiet: bool,
42
43    #[command(subcommand)]
44    command: Commands,
45}
46
47impl NewtonCli {
48    /// parsers only the default cli arguments
49    pub fn parse_args() -> Self {
50        Self::parse()
51    }
52
53    /// parsers only the default cli arguments from the given iterator
54    pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error>
55    where
56        I: IntoIterator<Item = T>,
57        T: Into<OsString> + Clone,
58    {
59        Self::try_parse_from(itr)
60    }
61
62    /// Try to load config, falling back to minimal config if service config loading fails
63    /// This is for commands that don't actually use the config (marked with _config)
64    /// With bundled deployment files, contract addresses should always load successfully.
65    fn load_config_or_minimal(&self, chain_id: u64) -> newton_prover_core::config::NewtonAvsConfig<NewtonCliConfig> {
66        let mut builder = NewtonAvsConfigBuilder::new(chain_id);
67        if let Some(config_path) = &self.config_path {
68            info!("Loading cli config from: {:?}", config_path);
69            builder = builder.with_service_path(config_path.clone());
70        }
71
72        // Try to load full config - should work now with bundled deployment files
73        match builder.build::<NewtonCliConfig>() {
74            Ok(config) => config,
75            Err(e) => {
76                // If service config loading fails, create minimal config with defaults
77                // Deployment files are bundled, so we can still load contract addresses
78                tracing::debug!("Service config loading failed, using defaults: {}", e);
79                let env = std::env::var("DEPLOYMENT_ENV").unwrap_or_else(|_| "stagef".to_string());
80                let service = NewtonCliConfig {
81                    eth_rpc_url: "http://127.0.0.1:8545".to_string(),
82                    newton_rpc_url: "http://127.0.0.1:8545".to_string(),
83                    signer: newton_prover_core::config::key::EcdsaKey {
84                        private_key: None,
85                        keystore_path: None,
86                        keystore_password: None,
87                    },
88                };
89                // Load contract addresses from bundled files, then construct config manually
90                use newton_prover_core::config::{contracts::*, ipfs::IpfsConfig, rpc::RpcProviderConfig};
91                // Try to load contracts from bundled files (should succeed)
92                // Clippy: use unwrap_or instead of unwrap_or_else when error isn't used
93                #[allow(clippy::unnecessary_lazy_evaluations)]
94                let contracts = ContractsConfig::load(chain_id, env.clone()).unwrap_or_else(|_| {
95                    // Shouldn't happen with bundled files, but provide fallback
96                    ContractsConfig {
97                        avs: NewtonAvsContractsConfig {
98                            newton_prover_service_manager: alloy::primitives::Address::ZERO,
99                            newton_prover_task_manager: alloy::primitives::Address::ZERO,
100                            challenge_verifier: alloy::primitives::Address::ZERO,
101                            rego_verifier: alloy::primitives::Address::ZERO,
102                            attestation_validator: alloy::primitives::Address::ZERO,
103                            operator_registry: alloy::primitives::Address::ZERO,
104                            operator_state_retriever: alloy::primitives::Address::ZERO,
105                            bls_apk_registry: alloy::primitives::Address::ZERO,
106                            index_registry: alloy::primitives::Address::ZERO,
107                            stake_registry: alloy::primitives::Address::ZERO,
108                            socket_registry: alloy::primitives::Address::ZERO,
109                            strategy: alloy::primitives::Address::ZERO,
110                        },
111                        eigenlayer: EigenlayerContractsConfig {
112                            delegation_manager: alloy::primitives::Address::ZERO,
113                            avs_directory: alloy::primitives::Address::ZERO,
114                            strategy_manager: alloy::primitives::Address::ZERO,
115                            allocation_manager: alloy::primitives::Address::ZERO,
116                            rewards_coordinator: alloy::primitives::Address::ZERO,
117                            strategy_factory: alloy::primitives::Address::ZERO,
118                            permission_controller: alloy::primitives::Address::ZERO,
119                        },
120                        policy: NewtonPolicyContractsConfig {
121                            policy_factory: alloy::primitives::Address::ZERO,
122                            policy_data_factory: alloy::primitives::Address::ZERO,
123                        },
124                    }
125                });
126                // Construct config with loaded contracts and default service config
127                newton_prover_core::config::NewtonAvsConfig {
128                    env: env.clone(),
129                    chain_id,
130                    rpc: RpcProviderConfig::load(chain_id).unwrap_or(RpcProviderConfig {
131                        http: "http://127.0.0.1:8545".to_string(),
132                        ws: "ws://127.0.0.1:8545".to_string(),
133                    }),
134                    ipfs: IpfsConfig::default(),
135                    contracts,
136                    service,
137                }
138            }
139        }
140    }
141
142    /// Load config, returning an error if it fails
143    /// This is for commands that actually need the config
144    fn load_config_required(
145        &self,
146        chain_id: u64,
147    ) -> eyre::Result<newton_prover_core::config::NewtonAvsConfig<NewtonCliConfig>> {
148        let mut builder = NewtonAvsConfigBuilder::new(chain_id);
149        if let Some(config_path) = &self.config_path {
150            info!("Loading cli config from: {:?}", config_path);
151            builder = builder.with_service_path(config_path.clone());
152        }
153        builder.build::<NewtonCliConfig>().map_err(|e| {
154            eyre::eyre!(
155                "Failed to load configuration: {}. \
156                Make sure deployment files exist for chain_id {} or set DEPLOYMENT_ENV environment variable.",
157                e,
158                chain_id
159            )
160        })
161    }
162
163    /// execute the configured cli command.
164    pub fn run(self) -> eyre::Result<()> {
165        // Build log filter - suppress verbose config loading logs by default
166        let env_filter = "warn,newton_cli=info".to_string();
167
168        // Initialize logging with specified format
169        init_logger(LoggerConfig::new(self.log_format).with_env_filter(env_filter));
170
171        let runner = NewtonRunner::default();
172
173        // Commands that don't need config at all (all subcommands use _config)
174        // These can work with a dummy config if loading fails
175        let commands_that_dont_need_config =
176            matches!(&self.command, Commands::PolicyFiles(_) | Commands::PolicyClient(_));
177
178        // Determine chain_id - optional for commands that don't need config
179        let chain_id = if commands_that_dont_need_config {
180            self.chain_id.unwrap_or(1)
181        } else {
182            self.chain_id.ok_or_else(|| eyre::eyre!("chain id is required"))?
183        };
184
185        // Load config - graceful fallback only for commands that truly don't need it
186        // Note: Policy and PolicyData have Simulate subcommands that need real config (for execute_wasm),
187        // so they require real config even though Deploy subcommands don't use it
188        // With bundled deployment files, config loading should succeed, but we allow fallback
189        // for commands that don't use config in case service config file is missing
190        let config = if commands_that_dont_need_config {
191            self.load_config_or_minimal(chain_id)
192        } else {
193            // Task, Policy, and PolicyData require real config
194            self.load_config_required(chain_id)?
195        };
196
197        match self.command {
198            Commands::Task(command) => runner.run_blocking_until_ctrl_c(Box::new(command).execute(config))?,
199            Commands::PolicyData(command) => runner.run_blocking_until_ctrl_c(Box::new(command).execute(config))?,
200            Commands::Policy(command) => runner.run_blocking_until_ctrl_c(Box::new(command).execute(config))?,
201            Commands::PolicyFiles(command) => runner.run_blocking_until_ctrl_c(Box::new(command).execute(config))?,
202            Commands::PolicyClient(command) => runner.run_blocking_until_ctrl_c(Box::new(command).execute(config))?,
203        }
204
205        Ok(())
206    }
207}
208
209/// commands to be executed
210#[derive(Debug, Subcommand)]
211pub enum Commands {
212    /// task commands
213    Task(TaskCommand),
214
215    /// policy data commands
216    PolicyData(PolicyDataCommand),
217
218    /// policy commands
219    Policy(PolicyCommand),
220
221    /// policy files commands
222    PolicyFiles(PolicyFilesCommand),
223
224    /// policy client commands
225    #[command(name = "policy-client")]
226    PolicyClient(PolicyClientCommand),
227}