newton_cli/commands/
policy_data.rs

1use alloy::primitives::{Address, FixedBytes};
2use clap::{Parser, Subcommand};
3use eyre::{Context, Result};
4use newton_prover_chainio::policy_data::PolicyDataController;
5use newton_prover_core::{
6    common::chain::get_block_time_ms, config::NewtonAvsConfig, newton_policy_data::INewtonPolicyData,
7};
8use serde_json::Value;
9use std::path::PathBuf;
10use tracing::{self, info};
11
12use crate::{commands::utils, config::NewtonCliConfig};
13
14/// Policy data commands
15#[derive(Debug, Parser)]
16#[command(name = "policy-data")]
17pub struct PolicyDataCommand {
18    #[command(subcommand)]
19    pub subcommand: PolicyDataSubcommand,
20}
21
22#[derive(Debug, Subcommand)]
23pub enum PolicyDataSubcommand {
24    /// Deploy policy data
25    Deploy(DeployCommand),
26    /// Simulate WASM execution
27    Simulate(SimulateCommand),
28}
29
30/// Simulate WASM execution command
31#[derive(Debug, Parser)]
32pub struct SimulateCommand {
33    /// Path to the WASM component file
34    #[arg(long)]
35    wasm_file: PathBuf,
36
37    /// Input JSON string
38    #[arg(long)]
39    input_json: String,
40}
41
42/// Deploy policy data command
43#[derive(Debug, Parser)]
44pub struct DeployCommand {
45    #[arg(long, env = "PRIVATE_KEY")]
46    private_key: Option<String>,
47
48    #[arg(long, env = "RPC_URL")]
49    rpc_url: Option<String>,
50
51    #[arg(long, default_value = "policy-files/policy_cids.json")]
52    policy_cids: PathBuf,
53}
54
55impl DeployCommand {
56    /// Convert seconds to blocks based on chain's block time
57    async fn seconds_to_blocks(rpc_url: &str, seconds: u32) -> Result<u32> {
58        let block_time_ms = get_block_time_ms(rpc_url).await?;
59        let block_time_seconds = block_time_ms / 1000;
60
61        if block_time_seconds == 0 {
62            return Err(eyre::eyre!("Block time is zero, cannot convert seconds to blocks"));
63        }
64
65        let blocks = (seconds as u64 / block_time_seconds) as u32;
66        tracing::info!(
67            "Converting {} seconds to blocks: block_time={}ms ({}s), result={} blocks",
68            seconds,
69            block_time_ms,
70            block_time_seconds,
71            blocks
72        );
73
74        Ok(blocks)
75    }
76
77    /// Get task generator addresses
78    ///
79    /// Returns all 12 task generator addresses for all networks.
80    async fn get_task_generator_addresses(_rpc_url: &str) -> Result<Vec<Address>> {
81        let addresses = vec![
82            "0x4883282094755C01cd0d15dFE74753c9E189d194",
83            "0x51eBfB4c0441b0D8898d968975073c24B9190227",
84            "0x19F9dEC9928417B6Ea6aD52291acCa9deef59E3F",
85            "0x9Ccd55A0Fb8aBD14919bb5B2BCDD39C1Dd40E883",
86            "0xb3785B3CA4d6b175518c9a2A51AE836f2dE3016B",
87            "0xC6aA2638873e90eCaF0306cbCC4370C6AC893E68",
88            "0x64E33a6b2874129f2C6FB61832b763F65C8342fb",
89            "0xA21d5CCa5771DAB5a049b1FAA524c41ae911e185",
90            "0x22748005349aD3e261Fc6B1C83956781d6d661B4",
91            "0x3F15Da29D0de8c45bf625299AA20e43970153622",
92            "0x494221AaA9B9B273EB171512Ea000bEaE868d332",
93            "0xD45062003a4626a532F30A4596aB253c45AE0647",
94        ];
95
96        addresses
97            .iter()
98            .map(|addr| {
99                addr.parse()
100                    .map_err(|e| eyre::eyre!("Failed to parse task generator address {}: {}", addr, e))
101            })
102            .collect()
103    }
104
105    /// Deploy policy data contract
106    ///
107    /// This function deploys a policy data contract using the Rust chainio functions.
108    /// It returns the deployed policy data address.
109    /// Note: expire_after_blocks should be in blocks, not seconds.
110    async fn deploy_policy_data(
111        private_key: &str,
112        rpc_url: &str,
113        wasm_cid: &str,
114        wasm_args_cid: &str,
115        policy_data_metadata_cid: &str,
116        expire_after_blocks: u32,
117    ) -> Result<Address> {
118        let controller = PolicyDataController::new(private_key.to_string(), rpc_url.to_string());
119
120        tracing::info!(
121            "Deploying policy data:\n  wasmCid: {}\n  wasmArgs: {}\n  metadataCid: {}\n  expireAfter: {} blocks \n",
122            wasm_cid,
123            wasm_args_cid,
124            policy_data_metadata_cid,
125            expire_after_blocks
126        );
127
128        let (_receipt, policy_data_address) = controller
129            .deploy_policy_data(
130                wasm_cid.to_string(),
131                wasm_args_cid.to_string(),
132                expire_after_blocks,
133                policy_data_metadata_cid.to_string(),
134            )
135            .await
136            .map_err(|e| eyre::eyre!("Failed to deploy policy data: {}", e))?;
137
138        tracing::info!("Policy data deployed successfully at address: {}", policy_data_address);
139
140        Ok(policy_data_address)
141    }
142
143    /// Set attestation info on deployed policy data
144    ///
145    /// This function sets the attestation info on a deployed policy data contract.
146    /// It creates an AttestationInfo struct with:
147    /// - attestationType: ECDSA (0)
148    /// - verifier: address(0)
149    /// - verificationKey: bytes32(0)
150    async fn set_attestation_info(
151        private_key: &str,
152        rpc_url: &str,
153        policy_data_address: Address,
154        attester_address: Address,
155    ) -> Result<()> {
156        let controller = PolicyDataController::new(private_key.to_string(), rpc_url.to_string());
157
158        // Get task generator addresses based on chain ID
159        let task_generator_addresses = Self::get_task_generator_addresses(rpc_url).await?;
160
161        // Create attesters array: [attester, ...task_generator_addresses]
162        let mut attesters = vec![attester_address];
163        attesters.extend(task_generator_addresses);
164
165        // Create AttestationInfo struct
166        let attestation_info = INewtonPolicyData::AttestationInfo {
167            attesters,
168            attestationType: 0u8, // ECDSA
169            verifier: Address::ZERO,
170            verificationKey: FixedBytes::ZERO,
171        };
172
173        tracing::info!(
174            "Setting attestation info on policy data {} with attesters: {:?}",
175            policy_data_address,
176            attestation_info.attesters
177        );
178
179        controller
180            .set_attestation_info(policy_data_address, attestation_info)
181            .await
182            .map_err(|e| eyre::eyre!("Failed to set attestation info: {}", e))?;
183
184        tracing::info!(
185            "Attestation info set successfully on policy data: {}",
186            policy_data_address
187        );
188
189        Ok(())
190    }
191
192    /// Execute the deploy command
193    pub async fn execute(self: Box<Self>, _config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
194        // Get values from args or env, with error if still missing
195        let private_key = self
196            .private_key
197            .ok_or_else(|| eyre::eyre!("private_key is required (use --private-key or PRIVATE_KEY env var)"))?;
198
199        let rpc_url = self
200            .rpc_url
201            .ok_or_else(|| eyre::eyre!("rpc_url is required (use --rpc-url or RPC_URL env var)"))?;
202
203        // Read from policy_cids.json file
204        let json_content = std::fs::read_to_string(&self.policy_cids)
205            .with_context(|| format!("Failed to read policy_cids.json: {:?}", self.policy_cids))?;
206        let json: Value = serde_json::from_str(&json_content)
207            .with_context(|| format!("Failed to parse policy_cids.json: {:?}", self.policy_cids))?;
208
209        // Extract values from policy_cids.json
210        let wasm_cid = json.get("wasmCid").and_then(|v| v.as_str()).unwrap_or("");
211        let wasm_args_cid = json.get("wasmArgs").and_then(|v| v.as_str()).unwrap_or("");
212        let policy_data_metadata_cid = json.get("policyDataMetadataCid").and_then(|v| v.as_str()).unwrap_or("");
213        let attester = json.get("attester").and_then(|v| v.as_str()).unwrap_or("");
214        let attester_address: Address = attester.parse().unwrap_or(Address::ZERO);
215
216        // Convert 300 seconds to blocks (matching the shell script's uint32(300 seconds))
217        // The contract expects expire_after in blocks, not seconds
218        let expire_after_blocks = Self::seconds_to_blocks(&rpc_url, 300).await?;
219
220        // Deploy policy data
221        let policy_data_address = Self::deploy_policy_data(
222            &private_key,
223            &rpc_url,
224            wasm_cid,
225            wasm_args_cid,
226            policy_data_metadata_cid,
227            expire_after_blocks,
228        )
229        .await?;
230
231        tracing::info!("Policy data deployment completed! Setting attestation info...");
232        // Set attestation info on policy data
233        Self::set_attestation_info(&private_key, &rpc_url, policy_data_address, attester_address).await?;
234
235        tracing::info!("Successfully deployed policy data and set attestation");
236        tracing::info!("Policy Data Address: {}", policy_data_address);
237
238        Ok(())
239    }
240}
241
242impl SimulateCommand {
243    /// Execute the simulate command
244    pub async fn execute(self: Box<Self>, config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
245        info!("Executing WASM simulation...");
246        let output = utils::execute_wasm(self.wasm_file, self.input_json, config).await?;
247        info!("WASM simulation output: {}", output);
248        Ok(())
249    }
250}
251
252impl PolicyDataCommand {
253    /// Execute the policy-data command
254    pub async fn execute(self: Box<Self>, config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
255        match self.subcommand {
256            PolicyDataSubcommand::Deploy(command) => {
257                Box::new(command).execute(config).await?;
258            }
259            PolicyDataSubcommand::Simulate(command) => {
260                Box::new(command).execute(config).await?;
261            }
262        }
263        Ok(())
264    }
265}