Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions walletkit-cli/src/commands/recovery_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use clap::Subcommand;

use super::{init_authenticator, Cli};
use crate::output;
use walletkit_core::issuers::RecoveryBindingManager;
use walletkit_core::Environment;

Expand All @@ -15,6 +16,7 @@ pub enum RecoveryBindingCommand {
UnregisterBindings {
sub: String,
},
GetBinding,
}

pub async fn run(
Expand Down Expand Up @@ -44,6 +46,25 @@ pub async fn run(
.unbind_recovery_agent(&authenticator, sub.clone())
.await?;
}
RecoveryBindingCommand::GetBinding => {
let recovery_binding_manager =
RecoveryBindingManager::new(environment).unwrap();
let recovery_binding = recovery_binding_manager
.get_recovery_binding(authenticator.leaf_index())
.await?;
if cli.json {
output::print_json_data(
&serde_json::json!({
"recovery_agent": recovery_binding.recovery_agent,
"pending_recovery_agent": recovery_binding.pending_recovery_agent,
"execute_after": recovery_binding.execute_after,
}),
true,
);
} else {
println!("Recovery binding: {recovery_binding:?}");
}
}
}
Ok(())
}
68 changes: 66 additions & 2 deletions walletkit-core/src/issuers/pop_backend_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ struct ChallengeResponse {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, uniffi::Record)]
pub struct RecoveryBindingResponse {
#[serde(rename = "recoveryAgent")]
pub recovery_agent: String,
pub recovery_agent: Option<String>,
#[serde(rename = "pendingRecoveryAgent")]
pub pending_recovery_agent: Option<String>,
#[serde(rename = "executeAfter")]
pub execute_after: Option<String>,
}

/// Low-level HTTP client for the Proof-of-Personhood (`PoP`) backend API.
Expand Down Expand Up @@ -424,7 +428,10 @@ mod tests {
let result = pop_api_client.get_recovery_binding(42).await;
assert!(result.is_ok(), "Expected success but got error: {result:?}");
mock.assert_async().await;
assert_eq!(result.unwrap().recovery_agent, "0x1234567890abcdef");
assert_eq!(
result.unwrap().recovery_agent,
Some("0x1234567890abcdef".to_string())
);
drop(server);
}

Expand Down Expand Up @@ -452,4 +459,61 @@ mod tests {
mock.assert_async().await;
drop(server);
}

#[tokio::test]
async fn test_get_recovery_binding_no_pending_recovery_agent() {
let mut server = mockito::Server::new_async().await;
let url = server.url();

let mock = server
.mock("GET", "/api/v1/recovery-binding?leafIndex=42")
.with_status(200)
.with_body("{\"recoveryAgent\": \"0x1234567890abcdef\"}")
.create_async()
.await;

let pop_api_client = PopBackendClient::new(url.clone());

let result = pop_api_client.get_recovery_binding(42).await;
assert!(result.is_ok(), "Expected success but got error: {result:?}");
mock.assert_async().await;
let recovery_binding = result.unwrap();
assert_eq!(recovery_binding.pending_recovery_agent, None);
assert_eq!(recovery_binding.execute_after, None);
assert_eq!(
recovery_binding.recovery_agent,
Some("0x1234567890abcdef".to_string())
);
drop(server);
}

#[tokio::test]
async fn test_get_recovery_binding_with_pending_recovery_agent() {
let mut server = mockito::Server::new_async().await;
let url = server.url();

let mock = server
.mock("GET", "/api/v1/recovery-binding?leafIndex=42")
.with_status(200)
.with_body("{\"recoveryAgent\": \"0x0000000000000000000000000000000000000001\", \"pendingRecoveryAgent\": \"0x0000000000000000000000000000000000000000\", \"executeAfter\": \"0x01\"}")
.create_async()
.await;

let pop_api_client = PopBackendClient::new(url.clone());

let result = pop_api_client.get_recovery_binding(42).await;
assert!(result.is_ok(), "Expected success but got error: {result:?}");
mock.assert_async().await;
let recovery_binding = result.unwrap();
assert_eq!(
recovery_binding.pending_recovery_agent,
Some("0x0000000000000000000000000000000000000000".to_string())
);
assert_eq!(recovery_binding.execute_after, Some("0x01".to_string()));
assert_eq!(
recovery_binding.recovery_agent,
Some("0x0000000000000000000000000000000000000001".to_string())
);
drop(server);
}
}
25 changes: 23 additions & 2 deletions walletkit-core/src/issuers/recovery_bindings_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,32 @@
use crate::authenticator::Authenticator;
use crate::error::WalletKitError;
use crate::issuers::pop_backend_client::ManageRecoveryBindingRequest;
use crate::issuers::pop_backend_client::RecoveryBindingResponse;
use crate::issuers::PopBackendClient;
use crate::Environment;
use alloy_primitives::keccak256;
use alloy_primitives::Address;
use std::string::String;
/// Represents a recovery binding.
#[derive(Debug, PartialEq, Eq, uniffi::Record)]
pub struct RecoveryBinding {
/// The hex address of the recovery agent (e.g. `"0x1234…"`).
pub recovery_agent: Option<String>,
/// The hex address of the pending recovery agent (e.g. `"0x1234…"`).
pub pending_recovery_agent: Option<String>,
/// The timestamp of the recovery agent update in seconds since the Unix epoch.
pub execute_after: Option<String>,
}

impl From<RecoveryBindingResponse> for RecoveryBinding {
fn from(response: RecoveryBindingResponse) -> Self {
Self {
recovery_agent: response.recovery_agent,
pending_recovery_agent: response.pending_recovery_agent,
execute_after: response.execute_after,
}
}
}

/// Client for registering and unregistering recovery agents with the `PoP` backend.
///
Expand Down Expand Up @@ -156,12 +177,12 @@ impl RecoveryBindingManager {
pub async fn get_recovery_binding(
&self,
leaf_index: u64,
) -> Result<String, WalletKitError> {
) -> Result<RecoveryBinding, WalletKitError> {
let recovery_binding = self
.pop_backend_client
.get_recovery_binding(leaf_index)
.await?;
Ok(recovery_binding.recovery_agent)
Ok(recovery_binding.into())
}
}

Expand Down
Loading