From 31162f3096bb2d1f07c637c4e7e8e50a3a14f3a4 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 2 Feb 2024 17:04:10 -0500 Subject: [PATCH] fetch key packages --- Cargo.lock | 5 +- gateway-types/src/lib.rs | 6 +- registry/src/error.rs | 2 + registry/src/lib.rs | 34 ++++-- xps-gateway/src/rpc/api.rs | 7 +- xps-gateway/src/rpc/methods.rs | 12 +- xps-gateway/tests/integration_test.rs | 138 +++++++++++++++++++--- xps-gateway/tests/integration_util/mod.rs | 30 ++++- 8 files changed, 194 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4dd611..8ef58b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1731,9 +1731,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown", @@ -2060,6 +2060,7 @@ dependencies = [ "chrono", "ethers", "hex", + "indexmap", "jsonrpsee", "log", "peg", diff --git a/gateway-types/src/lib.rs b/gateway-types/src/lib.rs index 91ec4c1..6da5370 100644 --- a/gateway-types/src/lib.rs +++ b/gateway-types/src/lib.rs @@ -39,14 +39,14 @@ pub type Bytes = Vec; /// * `transaction` - A `String` representing the unique identifier of the transaction on the /// blockchain. This can be used to track the transaction in a blockchain explorer. /// -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct GrantInstallationResult { pub status: Status, pub message: String, pub transaction: String, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct KeyPackageResult { /// Status of the operation pub status: Status, @@ -56,7 +56,7 @@ pub struct KeyPackageResult { pub key_packages: Vec, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum Status { Completed, Failed, diff --git a/registry/src/error.rs b/registry/src/error.rs index bfa461a..b643878 100644 --- a/registry/src/error.rs +++ b/registry/src/error.rs @@ -22,4 +22,6 @@ pub enum ContactOperationError { ResolutionError(lib_didethresolver::error::ResolverError, String), #[error("The DID has been deactivated, and no longer valid")] DIDDeactivated, + #[error("Type failed to convert")] + Type(#[from] lib_didethresolver::error::TypeError), } diff --git a/registry/src/lib.rs b/registry/src/lib.rs index f6e01a7..64a0d68 100644 --- a/registry/src/lib.rs +++ b/registry/src/lib.rs @@ -8,10 +8,7 @@ use ethers::{core::types::Signature, providers::Middleware, types::Address}; use gateway_types::{GrantInstallationResult, KeyPackageResult, Status}; use lib_didethresolver::types::VerificationMethodProperties; use lib_didethresolver::Resolver; -use lib_didethresolver::{ - did_registry::DIDRegistry, - types::{Attribute, XmtpAttribute}, -}; +use lib_didethresolver::{did_registry::DIDRegistry, types::XmtpAttribute}; pub struct ContactOperations { registry: DIDRegistry, @@ -52,17 +49,30 @@ where } let document = resolution.document; - /* + let properties = document .verification_method - .iter() - .filter_map(|method| method.id.fragment().filter(|f| f.starts_with("xmtp-"))) - .collect::>(); - */ + .into_iter() + .filter(|method| { + method + .id + .fragment() + .map(|f| f.starts_with("xmtp-")) + .unwrap_or(false) + && method + .id + .contains_query("meta".into(), "installation".into()) + }) + .filter_map(|method| method.verification_properties) + .collect::>(); + Ok(KeyPackageResult { status: Status::Completed, message: "Key packages retrieved".to_string(), - key_packages: Vec::new(), + key_packages: properties + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, }) } @@ -75,7 +85,7 @@ where validity: U256, ) -> Result> { let address = self.resolve_did_address(did)?; - let attribute: [u8; 32] = Attribute::from(name).into(); + let attribute: [u8; 32] = name.into(); log::debug!( "setting attribute {:#?}", String::from_utf8_lossy(&attribute) @@ -110,7 +120,7 @@ where signature: Signature, ) -> Result<(), ContactOperationError> { let address = self.resolve_did_address(did)?; - let attribute: [u8; 32] = Attribute::from(name).into(); + let attribute: [u8; 32] = name.into(); log::debug!( "Revoking attribute {:#?}", String::from_utf8_lossy(&attribute) diff --git a/xps-gateway/src/rpc/api.rs b/xps-gateway/src/rpc/api.rs index 4b180ce..2ec242d 100644 --- a/xps-gateway/src/rpc/api.rs +++ b/xps-gateway/src/rpc/api.rs @@ -4,8 +4,8 @@ use ethers::core::types::Signature; use ethers::prelude::*; use jsonrpsee::{proc_macros::rpc, types::ErrorObjectOwned}; -use gateway_types::GrantInstallationResult; use gateway_types::Message; +use gateway_types::{GrantInstallationResult, KeyPackageResult}; use lib_didethresolver::types::XmtpAttribute; /// XPS JSON-RPC Interface Methods @@ -222,6 +222,9 @@ pub trait Xps { signature: Signature, ) -> Result<(), ErrorObjectOwned>; + #[method(name = "fetchKeyPackages")] + async fn fetch_key_packages(&self, did: String) -> Result; + /// # Documentation for JSON RPC Endpoint: `status` /// ## Overview @@ -304,7 +307,7 @@ pub trait Xps { /// $ $ curl -H "Content-Type: application/json" -d '{"id":7000, "jsonrpc":"2.0", "method":"xps_status"}' http:///localhost:34695 /// {"jsonrpc":"2.0","result":"OK","id":7000} /// ``` - + /// /// ### Notes /// - The system should have proper error handling to deal with invalid requests, unauthorized access, and other potential issues. #[method(name = "status")] diff --git a/xps-gateway/src/rpc/methods.rs b/xps-gateway/src/rpc/methods.rs index 262eb46..fe747b1 100644 --- a/xps-gateway/src/rpc/methods.rs +++ b/xps-gateway/src/rpc/methods.rs @@ -8,7 +8,7 @@ use jsonrpsee::types::error::ErrorCode; use async_trait::async_trait; use ethers::prelude::*; use ethers::{core::types::Signature, providers::Middleware}; -use gateway_types::GrantInstallationResult; +use gateway_types::{GrantInstallationResult, KeyPackageResult}; use jsonrpsee::types::ErrorObjectOwned; use lib_didethresolver::types::XmtpAttribute; use rand::{rngs::StdRng, SeedableRng}; @@ -88,6 +88,16 @@ impl XpsServer for XpsMethods

{ async fn wallet_address(&self) -> Result { Ok(self.wallet.address()) } + + async fn fetch_key_packages(&self, did: String) -> Result { + log::debug!("xps_fetchKeyPackages called"); + let result = self + .contact_operations + .fetch_key_packages(did) + .await + .map_err(RpcError::from)?; + Ok(result) + } } /// Error types for DID Registry JSON-RPC diff --git a/xps-gateway/tests/integration_test.rs b/xps-gateway/tests/integration_test.rs index c144f2f..de580e6 100644 --- a/xps-gateway/tests/integration_test.rs +++ b/xps-gateway/tests/integration_test.rs @@ -2,7 +2,8 @@ mod integration_util; use anyhow::Error; -use ethers::signers::LocalWallet; +use ethers::{signers::LocalWallet, signers::Signer}; + use lib_didethresolver::{ did_registry::RegistrySignerExt, types::{DidUrl, KeyEncoding, XmtpAttribute, XmtpKeyPurpose}, @@ -11,7 +12,7 @@ use xps_gateway::rpc::XpsClient; use ethers::middleware::Middleware; use ethers::types::{Address, U256, U64}; -use gateway_types::Message; +use gateway_types::{Message, Status}; use integration_util::*; @@ -118,25 +119,11 @@ async fn test_grant_installation() -> Result<(), Error> { #[tokio::test] async fn test_revoke_installation() -> Result<(), Error> { with_xps_client(None, |client, context, resolver, anvil| async move { - let wallet: LocalWallet = anvil.keys()[3].clone().into(); - let me = get_user(&anvil, 3).await; + let me: LocalWallet = anvil.keys()[3].clone().into(); let name = *b"xmtp/installation/hex "; let value = b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"; - let validity = U256::from(604_800); - let signature = wallet - .sign_attribute(&context.registry, name, value.to_vec(), validity) - .await?; - let attr = context.registry.set_attribute_signed( - me.address(), - signature.v.try_into().unwrap(), - signature.r.into(), - signature.s.into(), - name, - value.into(), - validity, - ); - attr.send().await?.await?; + set_attribute(name, value.to_vec(), &me, &context.registry).await?; let doc = resolver .resolve_did(me.address(), None) @@ -152,7 +139,7 @@ async fn test_revoke_installation() -> Result<(), Error> { .unwrap() ); - let signature = wallet + let signature = me .sign_revoke_attribute(&context.registry, name, value.to_vec()) .await?; @@ -192,3 +179,116 @@ async fn test_revoke_installation() -> Result<(), Error> { }) .await } + +#[tokio::test] +async fn test_fetch_key_packages() -> Result<(), Error> { + with_xps_client(None, |client, context, _, anvil| async move { + let me: LocalWallet = anvil.keys()[3].clone().into(); + let name = *b"xmtp/installation/hex "; + let value = b"000000000000000000000000000000000000000000000000000000000000000000"; + set_attribute(name, value.to_vec(), &me, &context.registry).await?; + + let value = b"111111111111111111111111111111111111111111111111111111111111111111"; + set_attribute(name, value.to_vec(), &me, &context.registry).await?; + + let res = client + .fetch_key_packages(format!("0x{}", hex::encode(me.address()))) + .await?; + + assert_eq!(res.status, Status::Completed); + assert_eq!(&res.message, "Key packages retrieved"); + assert_eq!( + res.key_packages, + vec![ + b"000000000000000000000000000000000000000000000000000000000000000000", + b"111111111111111111111111111111111111111111111111111111111111111111" + ] + ); + + Ok(()) + }) + .await +} + +#[tokio::test] +async fn test_fetch_key_packages_revoke() -> Result<(), Error> { + with_xps_client(None, |client, context, _, anvil| async move { + let me: LocalWallet = anvil.keys()[3].clone().into(); + let name = *b"xmtp/installation/hex "; + let value = b"000000000000000000000000000000000000000000000000000000000000000000"; + set_attribute(name, value.to_vec(), &me, &context.registry).await?; + + let value = b"111111111111111111111111111111111111111111111111111111111111111111"; + set_attribute(name, value.to_vec(), &me, &context.registry).await?; + + client + .revoke_installation( + format!("0x{}", hex::encode(me.address())), + XmtpAttribute { + purpose: XmtpKeyPurpose::Installation, + encoding: KeyEncoding::Hex, + }, + value.to_vec(), + me.sign_revoke_attribute(&context.registry, name, value.to_vec()) + .await?, + ) + .await?; + + let res = client + .fetch_key_packages(format!("0x{}", hex::encode(me.address()))) + .await?; + + assert_eq!(res.status, Status::Completed); + assert_eq!(&res.message, "Key packages retrieved"); + assert_eq!( + res.key_packages, + vec![hex::decode( + b"000000000000000000000000000000000000000000000000000000000000000000" + ) + .unwrap()] + ); + + Ok(()) + }) + .await +} + +#[tokio::test] +async fn test_fetch_key_packages_client() -> Result<(), Error> { + with_xps_client(None, |client, context, _, anvil| async move { + let me: LocalWallet = anvil.keys()[3].clone().into(); + let attribute = XmtpAttribute { + purpose: XmtpKeyPurpose::Installation, + encoding: KeyEncoding::Hex, + }; + let value = b"000000000000000000000000000000000000000000000000000000000000000000"; + + client + .grant_installation( + format!("0x{}", hex::encode(me.address())), + attribute.clone(), + value.to_vec(), + me.sign_attribute( + &context.registry, + attribute.into(), + value.to_vec(), + U256::from(604_800), + ) + .await?, + ) + .await?; + let res = client + .fetch_key_packages(format!("0x{}", hex::encode(me.address()))) + .await?; + + assert_eq!(res.status, Status::Completed); + assert_eq!(&res.message, "Key packages retrieved"); + assert_eq!( + res.key_packages, + vec![b"000000000000000000000000000000000000000000000000000000000000000000"] + ); + + Ok(()) + }) + .await +} diff --git a/xps-gateway/tests/integration_util/mod.rs b/xps-gateway/tests/integration_util/mod.rs index 492be3f..25a6232 100644 --- a/xps-gateway/tests/integration_util/mod.rs +++ b/xps-gateway/tests/integration_util/mod.rs @@ -11,10 +11,14 @@ use ethers::{ middleware::SignerMiddleware, providers::{Provider, Ws}, signers::{LocalWallet, Signer as _}, + types::U256, utils::AnvilInstance, }; use futures::future::FutureExt; -use lib_didethresolver::{did_registry::DIDRegistry, Resolver}; +use lib_didethresolver::{ + did_registry::{DIDRegistry, RegistrySignerExt}, + Resolver, +}; use std::{ future::Future, sync::{Arc, Once}, @@ -145,3 +149,27 @@ fn init_test_logging() { .init() }) } + +pub async fn set_attribute( + name: [u8; 32], + value: Vec, + wallet: &LocalWallet, + registry: &DIDRegistry>>, +) -> Result<(), Error> { + let validity = U256::from(604_800); + let signature = wallet + .sign_attribute(registry, name, value.to_vec(), validity) + .await?; + + let attr = registry.set_attribute_signed( + wallet.address(), + signature.v.try_into().unwrap(), + signature.r.into(), + signature.s.into(), + name, + value.into(), + validity, + ); + attr.send().await?.await?; + Ok(()) +}