diff --git a/Cargo.lock b/Cargo.lock index 9bfadb1a..065ee858 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8973,8 +8973,7 @@ dependencies = [ [[package]] name = "world-id-authenticator" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d49101e3f7645df65066d62aafcddf5e58a713aea7ac0ba8d417f90eadefa7" +source = "git+https://github.com/worldcoin/world-id-protocol?branch=wip-102#f3e6b14dfc6882cb4c20124a2178df1c93422e02" dependencies = [ "alloy", "anyhow", @@ -9008,8 +9007,7 @@ dependencies = [ [[package]] name = "world-id-core" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098dc829e61fee475eadcbf5dab9687ce4b85531985ab8fb7cc4bbb6956f23f3" +source = "git+https://github.com/worldcoin/world-id-protocol?branch=wip-102#f3e6b14dfc6882cb4c20124a2178df1c93422e02" dependencies = [ "taceo-eddsa-babyjubjub", "world-id-authenticator", @@ -9021,8 +9019,7 @@ dependencies = [ [[package]] name = "world-id-primitives" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc964ae49594d98cfd854385a9a6ee4b87ea70ffaee172d95b432e80471669b" +source = "git+https://github.com/worldcoin/world-id-protocol?branch=wip-102#f3e6b14dfc6882cb4c20124a2178df1c93422e02" dependencies = [ "alloy", "alloy-primitives", @@ -9056,8 +9053,7 @@ dependencies = [ [[package]] name = "world-id-proof" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa564fc8a89175bd0eb03e9a98095427c46ca6bae1465108b6b4bd2b3bce0c60" +source = "git+https://github.com/worldcoin/world-id-protocol?branch=wip-102#f3e6b14dfc6882cb4c20124a2178df1c93422e02" dependencies = [ "ark-bn254", "ark-ec", @@ -9090,8 +9086,7 @@ dependencies = [ [[package]] name = "world-id-registries" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464f2a67429855df77a21584629f634d31b9424d44d5d112c7c4c78bb5cb717e" +source = "git+https://github.com/worldcoin/world-id-protocol?branch=wip-102#f3e6b14dfc6882cb4c20124a2178df1c93422e02" dependencies = [ "alloy", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 904bb3a4..7bee0344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,8 +35,8 @@ alloy-core = { version = "1", default-features = false, features = [ ] } alloy-primitives = { version = "1", default-features = false } uniffi = { version = "0.31.0", features = ["tokio"] } -world-id-core = { version = "0.10.1", default-features = false } -world-id-proof = { version = "0.10.1", default-features = false } +world-id-core = { git = "https://github.com/worldcoin/world-id-protocol", branch = "wip-102", default-features = false } +world-id-proof = { git = "https://github.com/worldcoin/world-id-protocol", branch = "wip-102", default-features = false } # internal walletkit-core = { version = "0.16.0", path = "walletkit-core", default-features = false } diff --git a/walletkit-cli/src/commands/recovery_agent.rs b/walletkit-cli/src/commands/recovery_agent.rs index 05b74d71..9f984e0c 100644 --- a/walletkit-cli/src/commands/recovery_agent.rs +++ b/walletkit-cli/src/commands/recovery_agent.rs @@ -1,4 +1,4 @@ -//! `walletkit recovery-agent` subcommands — recovery agent management. +//! `walletkit recovery-agent` subcommands — recovery agent management (WIP-102). use clap::Subcommand; use eyre::WrapErr as _; @@ -9,58 +9,44 @@ use super::{init_authenticator, Cli}; #[derive(Subcommand)] pub enum RecoveryAgentCommand { - /// Initiate a time-locked recovery agent update (14-day cooldown). - Initiate { + /// Update the holder's recovery agent. Effective immediately, reversible + /// during the revert window. + Update { /// Checksummed hex address of the new recovery agent (e.g. "0x1234…"). new_recovery_agent: String, }, - /// Execute a pending recovery agent update after the cooldown has elapsed. - Execute, - /// Cancel a pending recovery agent update before the cooldown expires. - Cancel, + /// Revert an in-flight recovery agent update during the revert window. + Revert, } pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { let (authenticator, _store) = init_authenticator(cli).await?; match action { - RecoveryAgentCommand::Initiate { new_recovery_agent } => { + RecoveryAgentCommand::Update { new_recovery_agent } => { let request_id = authenticator - .initiate_recovery_agent_update(new_recovery_agent.clone()) + .update_recovery_agent(new_recovery_agent.clone()) .await - .wrap_err("initiate recovery agent update failed")?; + .wrap_err("update recovery agent failed")?; let data = serde_json::json!({ "request_id": request_id }); if cli.json { output::print_json_data(&data, true); } else { - println!("Recovery agent update initiated. Request ID: {request_id}"); + println!("Recovery agent update submitted. Request ID: {request_id}"); } } - RecoveryAgentCommand::Execute => { + RecoveryAgentCommand::Revert => { let request_id = authenticator - .execute_recovery_agent_update() + .revert_recovery_agent_update() .await - .wrap_err("execute recovery agent update failed")?; + .wrap_err("revert recovery agent update failed")?; let data = serde_json::json!({ "request_id": request_id }); if cli.json { output::print_json_data(&data, true); } else { - println!("Recovery agent update executed. Request ID: {request_id}"); - } - } - RecoveryAgentCommand::Cancel => { - let request_id = authenticator - .cancel_recovery_agent_update() - .await - .wrap_err("cancel recovery agent update failed")?; - - let data = serde_json::json!({ "request_id": request_id }); - if cli.json { - output::print_json_data(&data, true); - } else { - println!("Recovery agent update cancelled. Request ID: {request_id}"); + println!("Recovery agent update reverted. Request ID: {request_id}"); } } } diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 3c24129f..88e3f1bb 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -1,5 +1,12 @@ //! The Authenticator is the main component with which users interact with the World ID Protocol. +// `#[uniffi::export]` macro-generated FFI registrations reference the deprecated +// WIP-102 V1 wrappers below (`initiate_/execute_/cancel_recovery_agent_update`). +// Those references emit deprecation warnings at the wrapper definition sites, +// outside the impl-level lint scope. Suppress at module level — the V1 wrappers +// are intentionally retained for backward compat with existing FFI consumers. +#![allow(deprecated)] + use crate::{ defaults::DefaultConfig, error::WalletKitError, primitives::ParseFromForeignBinding, Environment, FieldElement, Region, @@ -265,7 +272,7 @@ impl Authenticator { }) } - /// Initiates a time-locked recovery agent update (14-day cooldown). + /// Initiates a time-locked recovery agent update (legacy V1, 14-day cooldown). /// /// Signs an EIP-712 `InitiateRecoveryAgentUpdate` payload and submits it to /// the gateway. Returns the gateway request ID that can be used to poll @@ -279,6 +286,10 @@ impl Authenticator { /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not /// a valid address. /// - Returns a network error if the gateway request fails. + #[deprecated( + note = "WIP-102: use `update_recovery_agent`. The legacy URL still works against a V2-upgraded gateway, but the V2 contract changes the agent immediately (with a revert window) instead of starting a cooldown." + )] + #[allow(deprecated)] pub async fn initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -294,8 +305,37 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Executes a pending recovery agent update after the 14-day cooldown has - /// elapsed. + /// Updates the holder's recovery agent (WIP-102). + /// + /// On a V2 registry the new agent becomes effective immediately, but for a + /// revert window any authenticator can call + /// [`Self::revert_recovery_agent_update`] to roll back. During that window + /// the *previous* agent remains the only valid signer for `recoverAccount`, + /// which mitigates a compromised authenticator silently swapping in an + /// attacker-controlled recovery address. + /// + /// # Arguments + /// * `new_recovery_agent` — the checksummed hex address of the new recovery + /// agent (e.g. `"0x1234…"`). + /// + /// # Errors + /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not + /// a valid address. + /// - Returns a network error if the gateway request fails. + pub async fn update_recovery_agent( + &self, + new_recovery_agent: String, + ) -> Result { + let new_recovery_agent = + Address::parse_from_ffi(&new_recovery_agent, "new_recovery_agent")?; + + let request_id = self.inner.update_recovery_agent(new_recovery_agent).await?; + + Ok(request_id.to_string()) + } + + /// Executes a pending recovery agent update after the legacy V1 cooldown + /// has elapsed. /// /// This call is **permissionless** — no signature is required. The contract /// enforces the cooldown and will revert with @@ -305,6 +345,10 @@ impl Authenticator { /// /// # Errors /// Returns a network error if the gateway request fails. + #[deprecated( + note = "WIP-102: this operation no longer exists. On a V2-upgraded gateway the call is a no-op (returns Queued without touching chain). Remove the call from your flow." + )] + #[allow(deprecated)] pub async fn execute_recovery_agent_update( &self, ) -> Result { @@ -314,7 +358,7 @@ impl Authenticator { } /// Cancels a pending time-locked recovery agent update before the cooldown - /// expires. + /// expires (legacy V1). /// /// Signs an EIP-712 `CancelRecoveryAgentUpdate` payload and submits it to /// the gateway. Returns the gateway request ID that can be used to poll @@ -322,11 +366,34 @@ impl Authenticator { /// /// # Errors /// Returns a network error if the gateway request fails. + #[deprecated( + note = "WIP-102: use `revert_recovery_agent_update`. The legacy URL still works against a V2-upgraded gateway, but the new method name reflects WIP-102 semantics: the operation can only succeed within the revert window after `update_recovery_agent`." + )] + #[allow(deprecated)] pub async fn cancel_recovery_agent_update(&self) -> Result { let request_id = self.inner.cancel_recovery_agent_update().await?; Ok(request_id.to_string()) } + + /// Reverts an in-flight recovery agent update during the revert window + /// (WIP-102). + /// + /// Must be called within the revert window after + /// [`Self::update_recovery_agent`]. During that window any authenticator + /// can revert the update; the previous recovery agent stays effective + /// until the window expires. + /// + /// Signs an EIP-712 `CancelRecoveryAgentUpdate` payload (the typehash is + /// reused on V2) and submits it to the gateway. + /// + /// # Errors + /// Returns a network error if the gateway request fails. + pub async fn revert_recovery_agent_update(&self) -> Result { + let request_id = self.inner.revert_recovery_agent_update().await?; + + Ok(request_id.to_string()) + } } #[cfg(not(feature = "storage"))]