newton_cli/commands/
policy_data.rs

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