From 9de4e6dbaa442d8b48648c2cf9275e36a2376339 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 13:06:49 -0500 Subject: [PATCH 01/10] add timestamp to ethr builder --- Cargo.lock | 1 + lib/Cargo.toml | 1 + lib/src/error.rs | 6 +++ lib/src/lib.rs | 1 + lib/src/resolver.rs | 35 ++++++++++++++--- lib/src/types/ethr.rs | 74 ++++++++++++++++++++++------------- lib/src/types/xmtp.rs | 14 +++++-- lib/tests/integration_test.rs | 14 +++---- 8 files changed, 99 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b99e0e..8613d23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2580,6 +2580,7 @@ dependencies = [ "peg", "percent-encoding", "rand 0.8.5", + "regex", "rustc-hex", "serde", "serde_json", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 16af6c1..f3bd624 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -50,6 +50,7 @@ tokio-test = "0.4" futures = "0.3" ctor = "0.2.5" surf = "2.3" +regex = "1.10" [features] default = [] diff --git a/lib/src/error.rs b/lib/src/error.rs index f48af7a..9975d53 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -3,10 +3,12 @@ use ethers::{ contract::ContractError, providers::{Middleware, ProviderError}, signers::WalletError, + types::U64 }; use jsonrpsee::types::ErrorObjectOwned; use thiserror::Error; + /// Errors originating from resolution with the [`Resolver`](crate::resolver::Resolver) #[derive(Error, Debug)] pub enum ResolverError { @@ -16,6 +18,10 @@ pub enum ResolverError { ContractError(#[from] ContractError), #[error("{0}")] Middleware(String), + #[error("Block {0} containing Registry event not found")] + MissingBlock(U64), + #[error("Block {0} timestamp out of range")] + TimestampOutOfRange(U64), } /// Errors originating from the parsing of a did url identifier, [`Did`](crate::types::DidUrl) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index e0a056c..a62928b 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -90,3 +90,4 @@ pub use rpc::DidRegistryServer; #[cfg(feature = "client")] pub use rpc::DidRegistryClient; + diff --git a/lib/src/resolver.rs b/lib/src/resolver.rs index ea61702..9fa692a 100644 --- a/lib/src/resolver.rs +++ b/lib/src/resolver.rs @@ -28,6 +28,27 @@ impl From> for Resolver { } } +/// Extra context passed to the document builder from the [`Resolver`] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventContext { + /// the timestamp in nanoseconds in which the block from the document was built. + pub timestamp: i64 +} + +impl EventContext { + pub async fn new(meta: &LogMeta, signer: impl Middleware) -> Result> { + let block = signer.get_block(meta.block_number).await.map_err(|e| ResolverError::Middleware(e.to_string()))?; + let timestamp = block + .ok_or(ResolverError::MissingBlock(meta.block_number))? + .time() + .unwrap_or_default() + .timestamp_nanos_opt() + .ok_or(ResolverError::TimestampOutOfRange(meta.block_number))?; + + Ok(Self { timestamp }) + } +} + impl Resolver { /// Instantiate a new did:ethr resolver pub async fn new(middleware: M, registry: Address) -> Result> { @@ -90,21 +111,22 @@ impl Resolver { Ok(history) } - fn dispatch_event( + async fn dispatch_event( &self, doc: &mut EthrBuilder, public_key: H160, event: DIDRegistryEvents, meta: LogMeta, - ) { + ) -> Result<(), ResolverError> { + let context = EventContext::new(&meta, self.signer()).await?; let res = match event { DIDRegistryEvents::DiddelegateChangedFilter(delegate_changed) => { log::trace!("Delegate Changed {:?}", delegate_changed); - doc.delegate_event(delegate_changed) + doc.delegate_event(delegate_changed, &context) } DIDRegistryEvents::DidattributeChangedFilter(attribute_event) => { log::trace!("Attribute Changed {:?}", attribute_event); - doc.attribute_event(attribute_event) + doc.attribute_event(attribute_event, &context) } DIDRegistryEvents::DidownerChangedFilter(owner_changed) => { log::trace!("Owner Changed {:?}", owner_changed); @@ -123,6 +145,7 @@ impl Resolver { public_key, meta.block_number, meta.log_index, e, ); }; + Ok(()) } async fn wrap_did_resolution( @@ -157,7 +180,7 @@ impl Resolver { if version_id.unwrap_or_default() > U64::zero() { if meta.block_number <= version_id.unwrap_or_default() { // 1. delegate events - Resolver::dispatch_event(self, &mut base_document, public_key, event, meta); + Resolver::dispatch_event(self, &mut base_document, public_key, event, meta).await?; // 2. set latest version if current_version_id < block_number { current_version_id = block_number; @@ -169,7 +192,7 @@ impl Resolver { } } else { // 1. delegate events - Resolver::dispatch_event(self, &mut base_document, public_key, event, meta); + Resolver::dispatch_event(self, &mut base_document, public_key, event, meta).await?; // 2. set latest version if current_version_id < block_number { current_version_id = block_number; diff --git a/lib/src/types/ethr.rs b/lib/src/types/ethr.rs index 09634fd..3089dea 100644 --- a/lib/src/types/ethr.rs +++ b/lib/src/types/ethr.rs @@ -16,14 +16,17 @@ use super::{ }; use crate::{ error::EthrBuilderError, - resolver::did_registry::{ - DidattributeChangedFilter, DiddelegateChangedFilter, DidownerChangedFilter, + resolver::{ + did_registry::{DidattributeChangedFilter, DiddelegateChangedFilter, DidownerChangedFilter}, + EventContext, }, types::{self, NULL_ADDRESS}, }; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use ethers::types::{Address, Bytes, U256}; +use ethers::{ + types::{Address, Bytes, U256}, +}; use serde::{Deserialize, Serialize}; use url::Url; @@ -78,7 +81,7 @@ pub struct EthrBuilder { /// Map of keys to their index in the document /// _*NOTE*_: this is used to ensure the order of keys is maintained, but indexes of the same /// number are expected. (EX: a delegate and service both with index 0) - pub(super) keys: HashMap, + pub(super) keys: HashMap, } impl Default for EthrBuilder { @@ -139,6 +142,7 @@ impl EthrBuilder { Ok(()) } + /// check whether the document has been deactivated pub fn is_deactivated(&mut self) -> bool { self.is_deactivated } @@ -152,6 +156,7 @@ impl EthrBuilder { pub fn delegate_event( &mut self, event: DiddelegateChangedFilter, + context: &EventContext, ) -> Result<(), EthrBuilderError> { let delegate_type = String::from_utf8_lossy(&event.delegate_type); let key_purpose = types::parse_delegate(&delegate_type)?; @@ -167,7 +172,7 @@ impl EthrBuilder { return Ok(()); } - self.keys.insert(key, self.delegate_count); + self.keys.insert(key, (self.delegate_count, context.clone())); self.delegate_count += 1; Ok(()) @@ -180,6 +185,7 @@ impl EthrBuilder { pub fn attribute_event( &mut self, event: DidattributeChangedFilter, + context: &EventContext, ) -> Result<(), EthrBuilderError> { let name = event.name_string_lossy(); let attribute = types::parse_attribute(&name).unwrap_or(Attribute::Other(name.to_string())); @@ -208,19 +214,19 @@ impl EthrBuilder { match attribute { Attribute::PublicKey(_) => { if event.is_valid(&self.now) { - self.keys.insert(key, self.delegate_count); + self.keys.insert(key, (self.delegate_count, context.clone())); } self.delegate_count += 1; } Attribute::Service(_) => { if event.is_valid(&self.now) { - self.keys.insert(key, self.service_count); + self.keys.insert(key, (self.service_count, context.clone())); } self.service_count += 1; } Attribute::Xmtp(_) => { if event.is_valid(&self.now) { - self.keys.insert(key, self.xmtp_count); + self.keys.insert(key, (self.xmtp_count, context.clone())); } self.xmtp_count += 1; } @@ -413,10 +419,10 @@ impl EthrBuilder { } fn build_keys(&mut self) -> Result<(), EthrBuilderError> { - let mut keys = self.keys.drain().collect::>(); - keys.sort_by_key(|k| k.1); + let mut keys = self.keys.drain().collect::>(); + keys.sort_by_key(|(_, (index, _))| *index); - for (key, index) in keys { + for (key, (index, context)) in keys { match key { Key::Attribute { value, attribute, .. @@ -427,7 +433,9 @@ impl EthrBuilder { Attribute::Service(service) => { self.service(index, value, service)?; } - Attribute::Xmtp(xmtp) => self.xmtp_key(index, value, xmtp)?, + Attribute::Xmtp(xmtp) => { + self.xmtp_key(index, value, xmtp, &context)? + } Attribute::Other(_) => (), }, Key::Delegate { delegate, purpose } => { @@ -444,6 +452,14 @@ impl EthrBuilder { pub(crate) mod tests { use super::*; use crate::types::test::address; + + impl EventContext { + pub fn mock(timestamp: i64) -> Self { + Self { + timestamp + } + } + } //TODO: dids are case-sensitive w.r.t their addresses. One did should equal the other, no //matter the case of the address (other than blockchain_account_id b/c of EIP55) @@ -473,7 +489,8 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event).unwrap(); + let context = EventContext::mock(0); + builder.attribute_event(event, &context).unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.verification_method[1], @@ -503,7 +520,7 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.verification_method[1], @@ -532,7 +549,7 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.verification_method[1], @@ -562,7 +579,7 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.service, @@ -612,7 +629,7 @@ pub(crate) mod tests { builder.now(U256::zero()); for event in events { - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); } let doc = builder.build().unwrap(); @@ -667,7 +684,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(100)); for event in events { - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); } let doc = builder.build().unwrap(); @@ -726,7 +743,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); for event in events { - builder.delegate_event(event).unwrap(); + builder.delegate_event(event, &EventContext::mock(0)).unwrap(); } let doc = builder.build().unwrap(); @@ -802,7 +819,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); for event in &events { - builder.delegate_event(event.clone()).unwrap(); + builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); } // both events are valid @@ -812,7 +829,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(75)); for event in &events { - builder.delegate_event(event.clone()).unwrap(); + builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); } // only one event is valid assert_eq!(builder.keys.len(), 1); @@ -821,7 +838,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(125)); for event in &events { - builder.delegate_event(event.clone()).unwrap(); + builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); } // no events valid @@ -863,11 +880,12 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - - builder.attribute_event(attributes[0].clone()).unwrap(); - builder.delegate_event(delegates[0].clone()).unwrap(); - builder.attribute_event(attributes[1].clone()).unwrap(); - builder.delegate_event(delegates[1].clone()).unwrap(); + + let context = EventContext::mock(0); + builder.attribute_event(attributes[0].clone(), &context).unwrap(); + builder.delegate_event(delegates[0].clone(), &context).unwrap(); + builder.attribute_event(attributes[1].clone(), &context).unwrap(); + builder.delegate_event(delegates[1].clone(), &context).unwrap(); let doc = builder.build().unwrap(); @@ -940,7 +958,7 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event).unwrap(); + builder.attribute_event(event, &EventContext::mock(0)).unwrap(); let doc = builder.build().unwrap(); diff --git a/lib/src/types/xmtp.rs b/lib/src/types/xmtp.rs index fae6399..6b1846c 100644 --- a/lib/src/types/xmtp.rs +++ b/lib/src/types/xmtp.rs @@ -20,6 +20,7 @@ use crate::error::EthrBuilderError; use std::fmt; use serde::{Deserialize, Serialize}; +use crate::resolver::EventContext; /// The XMTP Attribute Type #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] @@ -83,12 +84,17 @@ impl EthrBuilder { index: usize, value: V, key: XmtpAttribute, + context: &EventContext, ) -> Result<(), EthrBuilderError> { log::debug!("index: {}", index); + let did_url = self .id .with_fragment(Some(&format!("xmtp-{}", index))) - .with_query("meta", Some(&key.purpose.to_string())); + .with_query("meta", Some(&key.purpose.to_string())) + .with_query("timestamp", Some(&context.timestamp.to_string())); + + let method = VerificationMethod { id: did_url, controller: self.id.clone(), @@ -131,7 +137,7 @@ mod test { builder.now(U256::zero()); for attr in attributes { - builder.attribute_event(attr).unwrap() + builder.attribute_event(attr, &EventContext::mock(10_000)).unwrap() } let doc = builder.build().unwrap(); @@ -139,7 +145,7 @@ mod test { assert_eq!(doc.verification_method[1].id.fragment().unwrap(), "xmtp-0"); assert_eq!( doc.verification_method[1].id.query().unwrap(), - vec![("meta".to_string(), "installation".to_string())] + vec![("meta".to_string(), "installation".to_string()), ("timestamp".to_string(), "10000".to_string())] ); assert_eq!(doc.verification_method.len(), 2); @@ -160,7 +166,7 @@ mod test { builder.now(U256::from(100)); for attr in attributes { - builder.attribute_event(attr).unwrap() + builder.attribute_event(attr, &EventContext::mock(10_000)).unwrap() } assert_eq!(builder.xmtp_count, 1); diff --git a/lib/tests/integration_test.rs b/lib/tests/integration_test.rs index 62c0586..a096f37 100644 --- a/lib/tests/integration_test.rs +++ b/lib/tests/integration_test.rs @@ -7,6 +7,7 @@ use ethers::{ signers::{LocalWallet, Signer as _}, types::{Address, U256}, }; +use regex::Regex; use integration_util::{validate_document, with_client}; #[cfg(test)] @@ -333,15 +334,10 @@ mod it { did.send().await?.await?; let document = client.resolve_did(hex::encode(me), None).await?.document; - assert_eq!( - document.verification_method[1].id, - DidUrl::parse(format!( - "did:ethr:0x{}?meta=installation#xmtp-0", - hex::encode(me) - )) - .unwrap() - ); - + let regexr = format!(r"did:ethr:mainnet:0x{}\?meta=installation×tamp=\d+#xmtp-0", hex::encode(me)); + let test = Regex::new(®exr).unwrap(); + assert!(test.is_match(&document.verification_method[1].id.to_string())); + let did = registry.revoke_attribute( me, attribute_name, From 7dcc75ce9ec90e9ac466e146850be85083d04b83 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 13:17:38 -0500 Subject: [PATCH 02/10] improve coverage --- lib/src/error.rs | 5 +- lib/src/lib.rs | 1 - lib/src/resolver.rs | 36 +++++++++++++-- lib/src/types/ethr.rs | 87 +++++++++++++++++++++++------------ lib/src/types/xmtp.rs | 16 +++++-- lib/tests/integration_test.rs | 9 ++-- 6 files changed, 109 insertions(+), 45 deletions(-) diff --git a/lib/src/error.rs b/lib/src/error.rs index 9975d53..5e24388 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -3,12 +3,11 @@ use ethers::{ contract::ContractError, providers::{Middleware, ProviderError}, signers::WalletError, - types::U64 + types::U64, }; use jsonrpsee::types::ErrorObjectOwned; use thiserror::Error; - /// Errors originating from resolution with the [`Resolver`](crate::resolver::Resolver) #[derive(Error, Debug)] pub enum ResolverError { @@ -18,7 +17,7 @@ pub enum ResolverError { ContractError(#[from] ContractError), #[error("{0}")] Middleware(String), - #[error("Block {0} containing Registry event not found")] + #[error("Block {0} containing Registry event not found")] MissingBlock(U64), #[error("Block {0} timestamp out of range")] TimestampOutOfRange(U64), diff --git a/lib/src/lib.rs b/lib/src/lib.rs index a62928b..e0a056c 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -90,4 +90,3 @@ pub use rpc::DidRegistryServer; #[cfg(feature = "client")] pub use rpc::DidRegistryClient; - diff --git a/lib/src/resolver.rs b/lib/src/resolver.rs index 9fa692a..a9f112d 100644 --- a/lib/src/resolver.rs +++ b/lib/src/resolver.rs @@ -32,12 +32,18 @@ impl From> for Resolver { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EventContext { /// the timestamp in nanoseconds in which the block from the document was built. - pub timestamp: i64 + pub timestamp: i64, } impl EventContext { - pub async fn new(meta: &LogMeta, signer: impl Middleware) -> Result> { - let block = signer.get_block(meta.block_number).await.map_err(|e| ResolverError::Middleware(e.to_string()))?; + pub async fn new( + meta: &LogMeta, + signer: impl Middleware, + ) -> Result> { + let block = signer + .get_block(meta.block_number) + .await + .map_err(|e| ResolverError::Middleware(e.to_string()))?; let timestamp = block .ok_or(ResolverError::MissingBlock(meta.block_number))? .time() @@ -180,7 +186,8 @@ impl Resolver { if version_id.unwrap_or_default() > U64::zero() { if meta.block_number <= version_id.unwrap_or_default() { // 1. delegate events - Resolver::dispatch_event(self, &mut base_document, public_key, event, meta).await?; + Resolver::dispatch_event(self, &mut base_document, public_key, event, meta) + .await?; // 2. set latest version if current_version_id < block_number { current_version_id = block_number; @@ -246,6 +253,8 @@ impl Resolver { #[cfg(test)] mod tests { + use ethers::{prelude::Provider, providers::MockProvider, types::TxHash}; + use super::*; #[test] @@ -255,4 +264,23 @@ mod tests { let resolver = Resolver::from(registry); assert_eq!(resolver.registry.address(), Address::zero()); } + + #[tokio::test] + async fn test_context_constructor() { + let (provider, mock) = Provider::mocked(); + mock.push(Block::::default()).unwrap(); + + let meta = LogMeta { + address: Address::zero(), + block_hash: H256::zero(), + block_number: U64::zero(), + log_index: U256::zero(), + transaction_hash: H256::zero(), + transaction_index: U64::zero(), + }; + let context = EventContext::new::>(&meta, Arc::new(provider)) + .await + .unwrap(); + assert_eq!(context.timestamp, 0); + } } diff --git a/lib/src/types/ethr.rs b/lib/src/types/ethr.rs index 3089dea..60ad76a 100644 --- a/lib/src/types/ethr.rs +++ b/lib/src/types/ethr.rs @@ -17,16 +17,16 @@ use super::{ use crate::{ error::EthrBuilderError, resolver::{ - did_registry::{DidattributeChangedFilter, DiddelegateChangedFilter, DidownerChangedFilter}, + did_registry::{ + DidattributeChangedFilter, DiddelegateChangedFilter, DidownerChangedFilter, + }, EventContext, }, types::{self, NULL_ADDRESS}, }; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use ethers::{ - types::{Address, Bytes, U256}, -}; +use ethers::types::{Address, Bytes, U256}; use serde::{Deserialize, Serialize}; use url::Url; @@ -172,7 +172,8 @@ impl EthrBuilder { return Ok(()); } - self.keys.insert(key, (self.delegate_count, context.clone())); + self.keys + .insert(key, (self.delegate_count, context.clone())); self.delegate_count += 1; Ok(()) @@ -214,7 +215,8 @@ impl EthrBuilder { match attribute { Attribute::PublicKey(_) => { if event.is_valid(&self.now) { - self.keys.insert(key, (self.delegate_count, context.clone())); + self.keys + .insert(key, (self.delegate_count, context.clone())); } self.delegate_count += 1; } @@ -419,7 +421,10 @@ impl EthrBuilder { } fn build_keys(&mut self) -> Result<(), EthrBuilderError> { - let mut keys = self.keys.drain().collect::>(); + let mut keys = self + .keys + .drain() + .collect::>(); keys.sort_by_key(|(_, (index, _))| *index); for (key, (index, context)) in keys { @@ -433,9 +438,7 @@ impl EthrBuilder { Attribute::Service(service) => { self.service(index, value, service)?; } - Attribute::Xmtp(xmtp) => { - self.xmtp_key(index, value, xmtp, &context)? - } + Attribute::Xmtp(xmtp) => self.xmtp_key(index, value, xmtp, &context)?, Attribute::Other(_) => (), }, Key::Delegate { delegate, purpose } => { @@ -452,12 +455,10 @@ impl EthrBuilder { pub(crate) mod tests { use super::*; use crate::types::test::address; - + impl EventContext { pub fn mock(timestamp: i64) -> Self { - Self { - timestamp - } + Self { timestamp } } } @@ -520,7 +521,9 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.verification_method[1], @@ -549,7 +552,9 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.verification_method[1], @@ -579,7 +584,9 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); let doc = builder.build().unwrap(); assert_eq!( doc.service, @@ -629,7 +636,9 @@ pub(crate) mod tests { builder.now(U256::zero()); for event in events { - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); } let doc = builder.build().unwrap(); @@ -684,7 +693,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(100)); for event in events { - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); } let doc = builder.build().unwrap(); @@ -743,7 +754,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); for event in events { - builder.delegate_event(event, &EventContext::mock(0)).unwrap(); + builder + .delegate_event(event, &EventContext::mock(0)) + .unwrap(); } let doc = builder.build().unwrap(); @@ -819,7 +832,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); for event in &events { - builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); + builder + .delegate_event(event.clone(), &EventContext::mock(0)) + .unwrap(); } // both events are valid @@ -829,7 +844,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(75)); for event in &events { - builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); + builder + .delegate_event(event.clone(), &EventContext::mock(0)) + .unwrap(); } // only one event is valid assert_eq!(builder.keys.len(), 1); @@ -838,7 +855,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::from(125)); for event in &events { - builder.delegate_event(event.clone(), &EventContext::mock(0)).unwrap(); + builder + .delegate_event(event.clone(), &EventContext::mock(0)) + .unwrap(); } // no events valid @@ -880,12 +899,20 @@ pub(crate) mod tests { let mut builder = EthrBuilder::default(); builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - + let context = EventContext::mock(0); - builder.attribute_event(attributes[0].clone(), &context).unwrap(); - builder.delegate_event(delegates[0].clone(), &context).unwrap(); - builder.attribute_event(attributes[1].clone(), &context).unwrap(); - builder.delegate_event(delegates[1].clone(), &context).unwrap(); + builder + .attribute_event(attributes[0].clone(), &context) + .unwrap(); + builder + .delegate_event(delegates[0].clone(), &context) + .unwrap(); + builder + .attribute_event(attributes[1].clone(), &context) + .unwrap(); + builder + .delegate_event(delegates[1].clone(), &context) + .unwrap(); let doc = builder.build().unwrap(); @@ -958,7 +985,9 @@ pub(crate) mod tests { builder.account_address(&identity).unwrap(); builder.now(U256::zero()); - builder.attribute_event(event, &EventContext::mock(0)).unwrap(); + builder + .attribute_event(event, &EventContext::mock(0)) + .unwrap(); let doc = builder.build().unwrap(); diff --git a/lib/src/types/xmtp.rs b/lib/src/types/xmtp.rs index 6b1846c..e3f409c 100644 --- a/lib/src/types/xmtp.rs +++ b/lib/src/types/xmtp.rs @@ -19,8 +19,8 @@ use crate::error::EthrBuilderError; use std::fmt; -use serde::{Deserialize, Serialize}; use crate::resolver::EventContext; +use serde::{Deserialize, Serialize}; /// The XMTP Attribute Type #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] @@ -94,7 +94,6 @@ impl EthrBuilder { .with_query("meta", Some(&key.purpose.to_string())) .with_query("timestamp", Some(&context.timestamp.to_string())); - let method = VerificationMethod { id: did_url, controller: self.id.clone(), @@ -137,7 +136,9 @@ mod test { builder.now(U256::zero()); for attr in attributes { - builder.attribute_event(attr, &EventContext::mock(10_000)).unwrap() + builder + .attribute_event(attr, &EventContext::mock(10_000)) + .unwrap() } let doc = builder.build().unwrap(); @@ -145,7 +146,10 @@ mod test { assert_eq!(doc.verification_method[1].id.fragment().unwrap(), "xmtp-0"); assert_eq!( doc.verification_method[1].id.query().unwrap(), - vec![("meta".to_string(), "installation".to_string()), ("timestamp".to_string(), "10000".to_string())] + vec![ + ("meta".to_string(), "installation".to_string()), + ("timestamp".to_string(), "10000".to_string()) + ] ); assert_eq!(doc.verification_method.len(), 2); @@ -166,7 +170,9 @@ mod test { builder.now(U256::from(100)); for attr in attributes { - builder.attribute_event(attr, &EventContext::mock(10_000)).unwrap() + builder + .attribute_event(attr, &EventContext::mock(10_000)) + .unwrap() } assert_eq!(builder.xmtp_count, 1); diff --git a/lib/tests/integration_test.rs b/lib/tests/integration_test.rs index a096f37..7c5f0b6 100644 --- a/lib/tests/integration_test.rs +++ b/lib/tests/integration_test.rs @@ -7,8 +7,8 @@ use ethers::{ signers::{LocalWallet, Signer as _}, types::{Address, U256}, }; -use regex::Regex; use integration_util::{validate_document, with_client}; +use regex::Regex; #[cfg(test)] mod it { @@ -334,10 +334,13 @@ mod it { did.send().await?.await?; let document = client.resolve_did(hex::encode(me), None).await?.document; - let regexr = format!(r"did:ethr:mainnet:0x{}\?meta=installation×tamp=\d+#xmtp-0", hex::encode(me)); + let regexr = format!( + r"did:ethr:mainnet:0x{}\?meta=installation×tamp=\d+#xmtp-0", + hex::encode(me) + ); let test = Regex::new(®exr).unwrap(); assert!(test.is_match(&document.verification_method[1].id.to_string())); - + let did = registry.revoke_attribute( me, attribute_name, From a7a12137f0128125a72952bca1c3675b8675eb80 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 13:41:19 -0500 Subject: [PATCH 03/10] add `get_query_value` fn to did_url --- lib/src/types/did_url.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/src/types/did_url.rs b/lib/src/types/did_url.rs index fdf50e6..7fd1a46 100644 --- a/lib/src/types/did_url.rs +++ b/lib/src/types/did_url.rs @@ -300,6 +300,18 @@ impl DidUrl { minion } + /// get a query value based on its `key` + pub fn get_query_value(&self, key: String) -> Option { + self.query + .as_ref() + .map(|q| { + q.iter() + .find(|(k, _)| k == &key) + .map(|(_, v)| v.to_string()) + }) + .flatten() + } + /// Immutable copy constructor to add a query parameter to the DID URL. /// # Examples /// ```rust @@ -745,4 +757,17 @@ mod tests { assert_eq!(Network::Sepolia.to_string(), "sepolia"); assert_eq!(Network::Other(0x1a1).to_string(), "417"); } + + #[test] + fn test_get_query_value() { + let did_url = DidUrl::parse("did:ethr:mainnet:0x0000000000000000000000000000000000000000?meta=hi&username=&password=hunter2").unwrap(); + + assert_eq!(did_url.get_query_value("meta".into()), Some("hi".into())); + assert_eq!(did_url.get_query_value("username".into()), Some("".into())); + assert_eq!( + did_url.get_query_value("password".into()), + Some("hunter2".into()) + ); + assert_eq!(did_url.get_query_value("does_not_exist".into()), None); + } } From f4aadbcce475b1525d57520bc1153500541c7fea Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 13:45:10 -0500 Subject: [PATCH 04/10] clippy --- lib/src/types/did_url.rs | 13 +++++-------- lib/tests/integration_test.rs | 10 ++++------ lib/tests/integration_util/mod.rs | 2 +- resolver/src/argenv.rs | 16 ++++++++-------- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/src/types/did_url.rs b/lib/src/types/did_url.rs index 7fd1a46..739b38f 100644 --- a/lib/src/types/did_url.rs +++ b/lib/src/types/did_url.rs @@ -302,14 +302,11 @@ impl DidUrl { /// get a query value based on its `key` pub fn get_query_value(&self, key: String) -> Option { - self.query - .as_ref() - .map(|q| { - q.iter() - .find(|(k, _)| k == &key) - .map(|(_, v)| v.to_string()) - }) - .flatten() + self.query.as_ref().and_then(|q| { + q.iter() + .find(|(k, _)| k == &key) + .map(|(_, v)| v.to_string()) + }) } /// Immutable copy constructor to add a query parameter to the DID URL. diff --git a/lib/tests/integration_test.rs b/lib/tests/integration_test.rs index 7c5f0b6..77d0192 100644 --- a/lib/tests/integration_test.rs +++ b/lib/tests/integration_test.rs @@ -82,9 +82,8 @@ mod it { .to_string() }) ); - assert_eq!( - resolution_response.metadata.clone().deactivated, - false + assert!( + !resolution_response.metadata.clone().deactivated ); assert_eq!( resolution_response.metadata.clone().version_id, @@ -123,9 +122,8 @@ mod it { let resolution_response = client.resolve_did(hex::encode(me), Some::("2".to_string())).await?; validate_document(&resolution_response.document).await; - assert_eq!( - resolution_response.metadata.clone().deactivated, - false + assert!( + !resolution_response.metadata.clone().deactivated ); assert_eq!( resolution_response.metadata.clone().version_id, diff --git a/lib/tests/integration_util/mod.rs b/lib/tests/integration_util/mod.rs index 64a9c31..37d24c8 100644 --- a/lib/tests/integration_util/mod.rs +++ b/lib/tests/integration_util/mod.rs @@ -109,7 +109,7 @@ where async fn deploy_to_anvil(anvil: &AnvilInstance) -> Address { let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let client = client(&anvil, wallet).await; + let client = client(anvil, wallet).await; let registry = DIDRegistry::deploy(client.clone(), ()) .unwrap() diff --git a/resolver/src/argenv.rs b/resolver/src/argenv.rs index f812bb7..59f4e42 100644 --- a/resolver/src/argenv.rs +++ b/resolver/src/argenv.rs @@ -53,12 +53,12 @@ mod tests { #[test] fn test_parse_host_arg() -> anyhow::Result<()> { let env = setup(); - let args = Args::parse_from(&["didethresolver", "-s", "host.xyz"]); + let args = Args::parse_from(["didethresolver", "-s", "host.xyz"]); assert_eq!(args.host, "host.xyz"); assert_eq!(args.port, DEFAULT_PORT); assert_eq!(args.rpc_url, DEFAULT_RPC_URL); assert_eq!(args.did_registry, DID_ETH_REGISTRY); - let args2 = Args::parse_from(&["didethresolver", "--host", "h.xyz"]); + let args2 = Args::parse_from(["didethresolver", "--host", "h.xyz"]); assert_eq!(args2.host, "h.xyz"); putback(env) } @@ -66,12 +66,12 @@ mod tests { #[test] fn test_parse_port_arg() -> anyhow::Result<()> { let env = setup(); - let args = Args::parse_from(&["didethresolver", "-p", "1234"]); + let args = Args::parse_from(["didethresolver", "-p", "1234"]); assert_eq!(args.host, DEFAULT_HOST); assert_eq!(args.port, 1234); assert_eq!(args.rpc_url, DEFAULT_RPC_URL); assert_eq!(args.did_registry, DID_ETH_REGISTRY); - let args2 = Args::parse_from(&["didethresolver", "--port", "4321"]); + let args2 = Args::parse_from(["didethresolver", "--port", "4321"]); assert_eq!(args2.host, DEFAULT_HOST); assert_eq!(args2.port, 4321); putback(env) @@ -80,12 +80,12 @@ mod tests { #[test] fn test_parse_rpc_url_arg() -> anyhow::Result<()> { let env = setup(); - let args = Args::parse_from(&["didethresolver", "-r", "http://rpc.xyz"]); + let args = Args::parse_from(["didethresolver", "-r", "http://rpc.xyz"]); assert_eq!(args.host, DEFAULT_HOST); assert_eq!(args.port, DEFAULT_PORT); assert_eq!(args.rpc_url, "http://rpc.xyz"); assert_eq!(args.did_registry, DID_ETH_REGISTRY); - let args2 = Args::parse_from(&["didethresolver", "--rpc-url", "http://rpc2.xyz"]); + let args2 = Args::parse_from(["didethresolver", "--rpc-url", "http://rpc2.xyz"]); assert_eq!(args2.host, DEFAULT_HOST); assert_eq!(args2.port, DEFAULT_PORT); assert_eq!(args2.rpc_url, "http://rpc2.xyz"); @@ -95,12 +95,12 @@ mod tests { #[test] fn test_parse_did_registry_arg() -> anyhow::Result<()> { let env = setup(); - let args = Args::parse_from(&["didethresolver", "-d", "0x1234567890"]); + let args = Args::parse_from(["didethresolver", "-d", "0x1234567890"]); assert_eq!(args.host, DEFAULT_HOST); assert_eq!(args.port, DEFAULT_PORT); assert_eq!(args.rpc_url, DEFAULT_RPC_URL); assert_eq!(args.did_registry, "0x1234567890"); - let args2 = Args::parse_from(&[ + let args2 = Args::parse_from([ "didethresolver", "--did-registry", "0x0987654321", From 6fab2a02d19ff90e1c0b3a4f039749f8e88ea28c Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 13:55:41 -0500 Subject: [PATCH 05/10] use AsRef --- lib/src/types/did_url.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/src/types/did_url.rs b/lib/src/types/did_url.rs index 739b38f..e33179f 100644 --- a/lib/src/types/did_url.rs +++ b/lib/src/types/did_url.rs @@ -301,10 +301,10 @@ impl DidUrl { } /// get a query value based on its `key` - pub fn get_query_value(&self, key: String) -> Option { + pub fn get_query_value>(&self, key: S) -> Option { self.query.as_ref().and_then(|q| { q.iter() - .find(|(k, _)| k == &key) + .find(|(k, _)| k == key.as_ref()) .map(|(_, v)| v.to_string()) }) } @@ -759,12 +759,9 @@ mod tests { fn test_get_query_value() { let did_url = DidUrl::parse("did:ethr:mainnet:0x0000000000000000000000000000000000000000?meta=hi&username=&password=hunter2").unwrap(); - assert_eq!(did_url.get_query_value("meta".into()), Some("hi".into())); - assert_eq!(did_url.get_query_value("username".into()), Some("".into())); - assert_eq!( - did_url.get_query_value("password".into()), - Some("hunter2".into()) - ); - assert_eq!(did_url.get_query_value("does_not_exist".into()), None); + assert_eq!(did_url.get_query_value("meta"), Some("hi".into())); + assert_eq!(did_url.get_query_value("username"), Some("".into())); + assert_eq!(did_url.get_query_value("password"), Some("hunter2".into())); + assert_eq!(did_url.get_query_value("does_not_exist"), None); } } From 81b7ebb2dc80bedc4eb39de27629f806534cc7b3 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 15:17:31 -0500 Subject: [PATCH 06/10] timestamp -> block_timestamp, always return error --- lib/src/error.rs | 2 ++ lib/src/resolver.rs | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/src/error.rs b/lib/src/error.rs index 5e24388..02d2a50 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -19,6 +19,8 @@ pub enum ResolverError { Middleware(String), #[error("Block {0} containing Registry event not found")] MissingBlock(U64), + #[error(transparent)] + Time(#[from] ethers::core::types::TimeError), #[error("Block {0} timestamp out of range")] TimestampOutOfRange(U64), } diff --git a/lib/src/resolver.rs b/lib/src/resolver.rs index a9f112d..1ba1193 100644 --- a/lib/src/resolver.rs +++ b/lib/src/resolver.rs @@ -32,7 +32,7 @@ impl From> for Resolver { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EventContext { /// the timestamp in nanoseconds in which the block from the document was built. - pub timestamp: i64, + pub block_timestamp: i64, } impl EventContext { @@ -44,14 +44,14 @@ impl EventContext { .get_block(meta.block_number) .await .map_err(|e| ResolverError::Middleware(e.to_string()))?; - let timestamp = block + + let block_timestamp = block .ok_or(ResolverError::MissingBlock(meta.block_number))? - .time() - .unwrap_or_default() + .time()? .timestamp_nanos_opt() .ok_or(ResolverError::TimestampOutOfRange(meta.block_number))?; - Ok(Self { timestamp }) + Ok(Self { block_timestamp }) } } From 2a563d1dbf1102d20606a61e90cdfd4c0493a482 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 15:25:55 -0500 Subject: [PATCH 07/10] store block timestamp in seconds --- lib/src/resolver.rs | 13 ++++++------- lib/src/types/ethr.rs | 4 ++-- lib/src/types/xmtp.rs | 11 +++++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/src/resolver.rs b/lib/src/resolver.rs index 1ba1193..44cec3c 100644 --- a/lib/src/resolver.rs +++ b/lib/src/resolver.rs @@ -31,8 +31,8 @@ impl From> for Resolver { /// Extra context passed to the document builder from the [`Resolver`] #[derive(Debug, Clone, PartialEq, Eq)] pub struct EventContext { - /// the timestamp in nanoseconds in which the block from the document was built. - pub block_timestamp: i64, + /// the timestamp in seconds in which the block from the document was built. + pub block_timestamp: u64, } impl EventContext { @@ -45,11 +45,10 @@ impl EventContext { .await .map_err(|e| ResolverError::Middleware(e.to_string()))?; - let block_timestamp = block + let block_timestamp: u64 = block .ok_or(ResolverError::MissingBlock(meta.block_number))? - .time()? - .timestamp_nanos_opt() - .ok_or(ResolverError::TimestampOutOfRange(meta.block_number))?; + .timestamp + .as_u64(); Ok(Self { block_timestamp }) } @@ -281,6 +280,6 @@ mod tests { let context = EventContext::new::>(&meta, Arc::new(provider)) .await .unwrap(); - assert_eq!(context.timestamp, 0); + assert_eq!(context.block_timestamp, 0); } } diff --git a/lib/src/types/ethr.rs b/lib/src/types/ethr.rs index 60ad76a..d83d16f 100644 --- a/lib/src/types/ethr.rs +++ b/lib/src/types/ethr.rs @@ -457,8 +457,8 @@ pub(crate) mod tests { use crate::types::test::address; impl EventContext { - pub fn mock(timestamp: i64) -> Self { - Self { timestamp } + pub fn mock(block_timestamp: u64) -> Self { + Self { block_timestamp } } } diff --git a/lib/src/types/xmtp.rs b/lib/src/types/xmtp.rs index e3f409c..f6607f6 100644 --- a/lib/src/types/xmtp.rs +++ b/lib/src/types/xmtp.rs @@ -22,6 +22,8 @@ use std::fmt; use crate::resolver::EventContext; use serde::{Deserialize, Serialize}; +pub const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; + /// The XMTP Attribute Type #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct XmtpAttribute { @@ -88,11 +90,13 @@ impl EthrBuilder { ) -> Result<(), EthrBuilderError> { log::debug!("index: {}", index); + let timestamp_ns = context.block_timestamp * NANOSECONDS_PER_SECOND; + let did_url = self .id .with_fragment(Some(&format!("xmtp-{}", index))) .with_query("meta", Some(&key.purpose.to_string())) - .with_query("timestamp", Some(&context.timestamp.to_string())); + .with_query("timestamp", Some(×tamp_ns.to_string())); let method = VerificationMethod { id: did_url, @@ -148,7 +152,10 @@ mod test { doc.verification_method[1].id.query().unwrap(), vec![ ("meta".to_string(), "installation".to_string()), - ("timestamp".to_string(), "10000".to_string()) + ( + "timestamp".to_string(), + (10000 * NANOSECONDS_PER_SECOND).to_string() + ) ] ); assert_eq!(doc.verification_method.len(), 2); From 8a618d788db7fadc5ca4cd4acb8e10c1bd06ca64 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 14 Feb 2024 16:10:26 -0500 Subject: [PATCH 08/10] fix bugs w.r.t byte conversions --- lib/src/rpc/methods.rs | 7 ++- lib/src/types.rs | 2 +- lib/src/types/ethr.rs | 55 +++++++++++++++------ lib/tests/integration_test.rs | 79 ++++++------------------------- lib/tests/integration_util/mod.rs | 37 +++++++++++++-- 5 files changed, 95 insertions(+), 85 deletions(-) diff --git a/lib/src/rpc/methods.rs b/lib/src/rpc/methods.rs index a47dca0..af626d2 100644 --- a/lib/src/rpc/methods.rs +++ b/lib/src/rpc/methods.rs @@ -34,6 +34,8 @@ impl DidRegistryServer for DidRegistryMethods { ) -> Result { log::debug!("did_resolveDid called"); + log::trace!("Resolving for key {}", public_key); + // parse the version_id let parsed_version_id = version_id.map(|str| U64::from(u64::from_str(&str).unwrap())); @@ -43,9 +45,10 @@ impl DidRegistryServer for DidRegistryMethods { H160::from_str(&public_key).map_err(RpcError::from)?, parsed_version_id, ) - .await?; + .await; + log::debug!("Resolution Result {:?}", resolution_result); - Ok(resolution_result) + Ok(resolution_result?) } } diff --git a/lib/src/types.rs b/lib/src/types.rs index 531b217..33561ac 100644 --- a/lib/src/types.rs +++ b/lib/src/types.rs @@ -272,7 +272,7 @@ impl From for [u8; 32] { // internal function to fill a [u8; 32] with bytes. // anything over 32 bytes will be cutoff. -fn string_to_bytes32>(s: S) -> [u8; 32] { +pub fn string_to_bytes32>(s: S) -> [u8; 32] { let s = s.as_ref(); let mut attr_bytes: [u8; 32] = [b' '; 32]; let length = std::cmp::min(s.as_bytes().len(), 32); diff --git a/lib/src/types/ethr.rs b/lib/src/types/ethr.rs index d83d16f..c01b3e2 100644 --- a/lib/src/types/ethr.rs +++ b/lib/src/types/ethr.rs @@ -321,8 +321,13 @@ impl EthrBuilder { value: V, encoding: KeyEncoding, ) -> Result, EthrBuilderError> { - let value = hex::decode(value.as_ref())?; - Ok(match encoding { + log::debug!( + "decoding attribute value {:?} with encoding: {}", + value.as_ref(), + encoding + ); + + let enc = match encoding { KeyEncoding::Hex => Some(VerificationMethodProperties::PublicKeyHex { public_key_hex: hex::encode(value), }), @@ -332,7 +337,9 @@ impl EthrBuilder { KeyEncoding::Base58 => Some(VerificationMethodProperties::PublicKeyBase58 { public_key_base58: bs58::encode(value).into_string(), }), - }) + }; + log::debug!("Encoded {:?}", enc); + Ok(enc) } /// Adds a delegate to the document @@ -483,7 +490,11 @@ pub(crate) mod tests { let event = DidattributeChangedFilter { name: *b"did/pub/Secp256k1/veriKey/hex ", - value: b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode( + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .unwrap() + .into(), ..base_attr_changed(identity, None) }; @@ -514,7 +525,9 @@ pub(crate) mod tests { let identity = address("0x7e575682a8e450e33eb0493f9972821ae333cd7f"); let event = DidattributeChangedFilter { name: *b"did/pub/Ed25519/veriKey/base58 ", - value: b"b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode("b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71") + .unwrap() + .into(), ..base_attr_changed(identity, None) }; @@ -545,7 +558,7 @@ pub(crate) mod tests { let identity = address("0x7e575682a8e450e33eb0493f9972821ae333cd7f"); let event = DidattributeChangedFilter { name: *b"did/pub/X25519/enc/base64 ", - value: b"302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052".into(), + value: hex::decode("302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052").unwrap().into(), ..base_attr_changed(identity, None) }; @@ -606,17 +619,17 @@ pub(crate) mod tests { let events = vec![ DidattributeChangedFilter { name: *b"did/pub/Secp256k1/veriKey/hex ", - value: b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").unwrap().into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { name: *b"did/pub/Secp256k1/sigAuth/base58", - value: b"b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode("b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").unwrap().into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { name: *b"did/pub/X25519/enc/base64 ", - value: b"302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052".into(), + value: hex::decode("302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052").unwrap().into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { @@ -664,17 +677,17 @@ pub(crate) mod tests { let events = vec![ DidattributeChangedFilter { name: *b"did/pub/Secp256k1/veriKey/hex ", - value: b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").unwrap().into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { name: *b"did/pub/Secp256k1/sigAuth/base58", - value: b"b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode("b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").unwrap().into(), ..base_attr_changed(identity, Some(10)) }, DidattributeChangedFilter { name: *b"did/pub/X25519/enc/base64 ", - value: b"302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052".into(), + value: hex::decode("302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052").unwrap().into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { @@ -870,12 +883,20 @@ pub(crate) mod tests { let attributes = vec![ DidattributeChangedFilter { name: *b"did/pub/Secp256k1/veriKey/hex ", - value: b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode( + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .unwrap() + .into(), ..base_attr_changed(identity, None) }, DidattributeChangedFilter { name: *b"did/pub/Secp256k1/sigAuth/base58", - value: b"b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode( + "b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .unwrap() + .into(), ..base_attr_changed(identity, None) }, ]; @@ -977,7 +998,11 @@ pub(crate) mod tests { let identity = address("0x7e575682a8e450e33eb0493f9972821ae333cd7f"); let event = DidattributeChangedFilter { name: *b"test/random/attribute99999999 ", - value: b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), + value: hex::decode( + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .unwrap() + .into(), ..base_attr_changed(identity, None) }; diff --git a/lib/tests/integration_test.rs b/lib/tests/integration_test.rs index 77d0192..fc465c6 100644 --- a/lib/tests/integration_test.rs +++ b/lib/tests/integration_test.rs @@ -7,7 +7,7 @@ use ethers::{ signers::{LocalWallet, Signer as _}, types::{Address, U256}, }; -use integration_util::{validate_document, with_client}; +use integration_util::{set_attribute, revoke_attribute, validate_document, with_client}; use regex::Regex; #[cfg(test)] @@ -25,22 +25,9 @@ mod it { pub async fn test_attributes() -> Result<()> { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); - let did = registry.set_attribute( - me, - *b"did/pub/Secp256k1/veriKey/hex ", - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - U256::from(604_800), - ); - did.send().await?.await?; - - let did = registry.set_attribute( - me, - *b"did/pub/Ed25519/veriKey/base64 ", - b"302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052".into(), - U256::from(604_800), - ); - did.send().await?.await?; - + set_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; + set_attribute(®istry, me, "did/pub/Ed25519/veriKey/base64", "302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052", 604_800).await?; + let resolution_response = client.resolve_did(hex::encode(me), None).await?; validate_document(&resolution_response.document).await; assert_eq!( @@ -58,8 +45,7 @@ mod it { assert_eq!( resolution_response.document.verification_method[1].verification_properties, Some(VerificationMethodProperties::PublicKeyHex { - public_key_hex: - "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".to_string() + public_key_hex: "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".to_string() }) ); @@ -103,21 +89,8 @@ mod it { pub async fn test_attributes_versions() -> Result<()> { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); - let did = registry.set_attribute( - me, - *b"did/pub/Secp256k1/veriKey/hex ", - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - U256::from(604_800), - ); - did.send().await?.await?; - - let did = registry.set_attribute( - me, - *b"did/pub/Ed25519/veriKey/base64 ", - b"302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052".into(), - U256::from(604_800), - ); - did.send().await?.await?; + set_attribute(®istry, me,"did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; + set_attribute(®istry, me, "did/pub/Ed25519/veriKey/base64", "302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052", 604_800).await?; let resolution_response = client.resolve_did(hex::encode(me), Some::("2".to_string())).await?; validate_document(&resolution_response.document).await; @@ -225,20 +198,9 @@ mod it { pub async fn test_attribute_revocation() -> Result<()> { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); - let did = registry.set_attribute( - me, - *b"did/pub/Secp256k1/veriKey/hex ", - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - U256::from(604_800), - ); - did.send().await?.await?; - - let did = registry.revoke_attribute( - me, - *b"did/pub/Secp256k1/veriKey/hex ", - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - ); - did.send().await?.await?; + + set_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; + revoke_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").await?; let document = client.resolve_did(hex::encode(me), None).await?.document; validate_document(&document).await; @@ -322,14 +284,8 @@ mod it { pub async fn test_xmtp_revocation() -> Result<()> { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); - let attribute_name = *b"xmtp/installation/hex "; - let did = registry.set_attribute( - me, - attribute_name, - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - U256::from(604_800), - ); - did.send().await?.await?; + let attribute_name = "xmtp/installation/hex "; + set_attribute(®istry, me, attribute_name, "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; let document = client.resolve_did(hex::encode(me), None).await?.document; let regexr = format!( @@ -338,14 +294,9 @@ mod it { ); let test = Regex::new(®exr).unwrap(); assert!(test.is_match(&document.verification_method[1].id.to_string())); - - let did = registry.revoke_attribute( - me, - attribute_name, - b"02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71".into(), - ); - did.send().await?.await?; - + + revoke_attribute(®istry, me, attribute_name, "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").await?; + let document = client.resolve_did(hex::encode(me), None).await?.document; validate_document(&document).await; assert_eq!( diff --git a/lib/tests/integration_util/mod.rs b/lib/tests/integration_util/mod.rs index 37d24c8..92b8e59 100644 --- a/lib/tests/integration_util/mod.rs +++ b/lib/tests/integration_util/mod.rs @@ -1,5 +1,7 @@ //! Shared setup code for integration tests use ethers::providers::Middleware; +use ethers::types::{Bytes, H160}; +use hex::FromHex; use std::sync::{Arc, Once}; use std::{future::Future, time::Duration}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; @@ -11,7 +13,7 @@ use ethers::{ middleware::SignerMiddleware, providers::{Provider, Ws}, signers::{LocalWallet, Signer as _}, - types::Address, + types::{Address, U256}, }; use futures::future::FutureExt; use jsonrpsee::{ @@ -19,8 +21,8 @@ use jsonrpsee::{ ws_client::{WsClient, WsClientBuilder}, }; use lib_didethresolver::{ - did_registry::DIDRegistry, rpc::DidRegistryMethods, types::DidDocument, DidRegistryServer, - Resolver, + did_registry::DIDRegistry, rpc::DidRegistryMethods, types::string_to_bytes32, + types::DidDocument, DidRegistryServer, Resolver, }; use serde::{Deserialize, Serialize}; use tokio::time::timeout as timeout_tokio; @@ -164,3 +166,32 @@ pub async fn validate_document(document: &DidDocument) { } assert!(response.valid); } + +pub async fn set_attribute( + registry: &DIDRegistry, + did: H160, + key: &str, + value: &str, + validity: u64, +) -> Result<()> { + let attribute = registry.set_attribute( + did, + string_to_bytes32(key), + Bytes::from_hex(value)?, + U256::from(validity), + ); + + attribute.send().await?.await?; + Ok(()) +} + +pub async fn revoke_attribute( + registry: &DIDRegistry, + did: H160, + key: &str, + value: &str, +) -> Result<()> { + let attribute = registry.revoke_attribute(did, string_to_bytes32(key), Bytes::from_hex(value)?); + attribute.send().await?.await?; + Ok(()) +} From e821cda718e63e381a384d10896be0fb7da9ec24 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 15 Feb 2024 15:23:43 -0500 Subject: [PATCH 09/10] remove constant use std::time::Duration --- lib/src/types/xmtp.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/types/xmtp.rs b/lib/src/types/xmtp.rs index f6607f6..5fd2f8e 100644 --- a/lib/src/types/xmtp.rs +++ b/lib/src/types/xmtp.rs @@ -17,13 +17,11 @@ use super::{string_to_bytes32, Attribute, EthrBuilder, KeyEncoding, KeyType, VerificationMethod}; use crate::error::EthrBuilderError; -use std::fmt; +use std::{fmt, time::Duration}; use crate::resolver::EventContext; use serde::{Deserialize, Serialize}; -pub const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; - /// The XMTP Attribute Type #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct XmtpAttribute { @@ -90,7 +88,7 @@ impl EthrBuilder { ) -> Result<(), EthrBuilderError> { log::debug!("index: {}", index); - let timestamp_ns = context.block_timestamp * NANOSECONDS_PER_SECOND; + let timestamp_ns = Duration::from_secs(context.block_timestamp).as_nanos(); let did_url = self .id @@ -154,7 +152,7 @@ mod test { ("meta".to_string(), "installation".to_string()), ( "timestamp".to_string(), - (10000 * NANOSECONDS_PER_SECOND).to_string() + Duration::from_secs(10000).as_nanos().to_string() ) ] ); From 77f5c5a8fb65c13472a0e0b304b62e1c96d6822e Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 15 Feb 2024 16:46:48 -0500 Subject: [PATCH 10/10] fmt --- lib/tests/integration_test.rs | 46 +++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/tests/integration_test.rs b/lib/tests/integration_test.rs index fc465c6..89cd97f 100644 --- a/lib/tests/integration_test.rs +++ b/lib/tests/integration_test.rs @@ -7,7 +7,7 @@ use ethers::{ signers::{LocalWallet, Signer as _}, types::{Address, U256}, }; -use integration_util::{set_attribute, revoke_attribute, validate_document, with_client}; +use integration_util::{revoke_attribute, set_attribute, validate_document, with_client}; use regex::Regex; #[cfg(test)] @@ -26,8 +26,8 @@ mod it { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); set_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; - set_attribute(®istry, me, "did/pub/Ed25519/veriKey/base64", "302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052", 604_800).await?; - + set_attribute(®istry, me, "did/pub/Ed25519/veriKey/base64", "302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052", 604_800).await?; + let resolution_response = client.resolve_did(hex::encode(me), None).await?; validate_document(&resolution_response.document).await; assert_eq!( @@ -198,9 +198,22 @@ mod it { pub async fn test_attribute_revocation() -> Result<()> { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); - - set_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; - revoke_attribute(®istry, me, "did/pub/Secp256k1/veriKey/hex", "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").await?; + + set_attribute( + ®istry, + me, + "did/pub/Secp256k1/veriKey/hex", + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + 604_800, + ) + .await?; + revoke_attribute( + ®istry, + me, + "did/pub/Secp256k1/veriKey/hex", + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .await?; let document = client.resolve_did(hex::encode(me), None).await?.document; validate_document(&document).await; @@ -285,7 +298,14 @@ mod it { with_client(None, |client, registry, signer, _| async move { let me = signer.address(); let attribute_name = "xmtp/installation/hex "; - set_attribute(®istry, me, attribute_name, "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", 604_800).await?; + set_attribute( + ®istry, + me, + attribute_name, + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + 604_800, + ) + .await?; let document = client.resolve_did(hex::encode(me), None).await?.document; let regexr = format!( @@ -294,9 +314,15 @@ mod it { ); let test = Regex::new(®exr).unwrap(); assert!(test.is_match(&document.verification_method[1].id.to_string())); - - revoke_attribute(®istry, me, attribute_name, "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71").await?; - + + revoke_attribute( + ®istry, + me, + attribute_name, + "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", + ) + .await?; + let document = client.resolve_did(hex::encode(me), None).await?.document; validate_document(&document).await; assert_eq!(