diff --git a/src/ethereum/identity.rs b/src/ethereum/identity.rs index 5db515a..3049d11 100644 --- a/src/ethereum/identity.rs +++ b/src/ethereum/identity.rs @@ -7,6 +7,7 @@ use serde::{ }; use crate::errors::ValidationError; +use crate::models::profiles::currencies::Currency; use super::signatures::recover_address; use super::utils::address_to_string; @@ -22,14 +23,10 @@ pub struct DidPkh { } impl DidPkh { - pub fn from_ethereum_address(address: &str) -> Self { - // Ethereum mainnet - // https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-3.md - Self { - network_id: "eip155".to_string(), - chain_id: "1".to_string(), - address: address.to_lowercase(), - } + pub fn from_address(currency: &Currency, address: &str) -> Self { + let (network_id, chain_id) = currency.caip2(); + let address = currency.normalize_address(address); + Self { network_id, chain_id, address } } } @@ -133,10 +130,12 @@ mod tests { use crate::ethereum::utils::address_to_string; use super::*; + const ETHEREUM: Currency = Currency::Ethereum; + #[test] fn test_did_string_conversion() { let address = "0xB9C5714089478a327F09197987f16f9E5d936E8a"; - let did = DidPkh::from_ethereum_address(address); + let did = DidPkh::from_address(ÐEREUM, address); assert_eq!(did.address, address.to_lowercase()); let did_str = did.to_string(); @@ -156,7 +155,7 @@ mod tests { let secret_key_ref = SecretKeyRef::new(&secret_key); let secret_key_str = secret_key.display_secret().to_string(); let address = address_to_string(secret_key_ref.address()); - let did = DidPkh::from_ethereum_address(&address); + let did = DidPkh::from_address(ÐEREUM, &address); let message = create_identity_claim(actor_id, &did).unwrap(); let signature = sign_message(&secret_key_str, message.as_bytes()).unwrap().to_string(); let result = verify_identity_proof(actor_id, &did, &signature); diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index c7a6804..eacdaf9 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -227,7 +227,7 @@ async fn get_identity_claim( let actor_id = current_user.profile.actor_id(&config.instance_url()); let wallet_address = current_user.wallet_address.as_ref() .ok_or(HttpError::PermissionError)?; - let did = DidPkh::from_ethereum_address(wallet_address); + let did = DidPkh::from_address(&config.default_currency(), wallet_address); let claim = create_identity_claim(&actor_id, &did) .map_err(|_| HttpError::InternalError)?; let response = IdentityClaim { claim }; @@ -246,7 +246,7 @@ async fn create_identity_proof( let actor_id = current_user.profile.actor_id(&config.instance_url()); let wallet_address = current_user.wallet_address.as_ref() .ok_or(HttpError::PermissionError)?; - let did = DidPkh::from_ethereum_address(wallet_address); + let did = DidPkh::from_address(&config.default_currency(), wallet_address); verify_identity_proof( &actor_id, &did, diff --git a/src/models/profiles/currencies.rs b/src/models/profiles/currencies.rs index 604c469..088b766 100644 --- a/src/models/profiles/currencies.rs +++ b/src/models/profiles/currencies.rs @@ -10,6 +10,21 @@ impl Currency { Self::Ethereum => "ETH", }.to_string() } + + /// Network ID and chain ID according to CAIP-2 standard + /// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md + pub fn caip2(&self) -> (String, String) { + let (network_id, chain_id) = match self { + Self::Ethereum => ("eip155", "1"), + }; + (network_id.to_string(), chain_id.to_string()) + } + + pub fn normalize_address(&self, address: &str) -> String { + match self { + Self::Ethereum => address.to_lowercase(), + } + } } pub fn get_currency_field_name(currency: &Currency) -> String { diff --git a/src/models/profiles/queries.rs b/src/models/profiles/queries.rs index d910dc8..75ae83b 100644 --- a/src/models/profiles/queries.rs +++ b/src/models/profiles/queries.rs @@ -3,6 +3,7 @@ use uuid::Uuid; use crate::database::catch_unique_violation; use crate::errors::DatabaseError; +use crate::ethereum::identity::DidPkh; use crate::models::cleanup::{ find_orphaned_files, find_orphaned_ipfs_objects, @@ -374,6 +375,7 @@ pub async fn search_profile_by_wallet_address( wallet_address: &str, ) -> Result, DatabaseError> { let field_name = get_currency_field_name(currency); + let did_str = DidPkh::from_address(currency, wallet_address).to_string(); // If currency is Ethereum, // search over extra fields must be case insensitive let rows = db_client.query( @@ -382,6 +384,11 @@ pub async fn search_profile_by_wallet_address( FROM actor_profile LEFT JOIN user_account USING (id) WHERE user_account.wallet_address = $2 + OR EXISTS ( + SELECT 1 + FROM jsonb_array_elements(actor_profile.identity_proofs) AS proof + WHERE proof ->> 'issuer' = $3 + ) OR EXISTS ( SELECT 1 FROM jsonb_array_elements(actor_profile.extra_fields) AS field @@ -390,7 +397,7 @@ pub async fn search_profile_by_wallet_address( AND field ->> 'value' ILIKE $2 ) ", - &[&field_name, &wallet_address], + &[&field_name, &wallet_address, &did_str], ).await?; let profiles: Vec = rows.iter() .map(|row| row.try_get("actor_profile")) @@ -481,7 +488,11 @@ mod tests { use crate::activitypub::actor::Actor; use crate::database::test_utils::create_test_database; use crate::models::profiles::queries::create_profile; - use crate::models::profiles::types::{ExtraField, ProfileCreateData}; + use crate::models::profiles::types::{ + ExtraField, + IdentityProof, + ProfileCreateData, + }; use crate::models::users::queries::create_user; use crate::models::users::types::UserCreateData; use super::*; @@ -575,4 +586,25 @@ mod tests { assert_eq!(profiles.len(), 1); assert_eq!(profiles[0].id, profile.id); } + + #[tokio::test] + #[serial] + async fn test_search_profile_by_wallet_address_identity_proof() { + let db_client = &mut create_test_database().await; + let identity_proof = IdentityProof { + issuer: DidPkh::from_address(ÐEREUM, "0x1234abcd"), + proof_type: "ethereum".to_string(), + value: "13590013185bdea963".to_string(), + }; + let profile_data = ProfileCreateData { + identity_proofs: vec![identity_proof], + ..Default::default() + }; + let profile = create_profile(db_client, profile_data).await.unwrap(); + let profiles = search_profile_by_wallet_address( + db_client, ÐEREUM, "0x1234abcd").await.unwrap(); + + assert_eq!(profiles.len(), 1); + assert_eq!(profiles[0].id, profile.id); + } }