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