Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version_id support #21

Merged
merged 42 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4fe4525
incorporate contract,resolver, and create did-url
insipx Dec 11, 2023
ee73997
try to incorporate network and address parsing into did url
insipx Dec 12, 2023
0b3da61
tests for address and network
insipx Dec 12, 2023
c5059fb
create history of DID events
insipx Dec 12, 2023
ce50b03
comment
insipx Dec 12, 2023
7ff973c
fix some comments
insipx Dec 12, 2023
ca81018
dont need to specify address
insipx Dec 12, 2023
8f70bb6
parsing events, updated gpg keys
insipx Dec 13, 2023
73e5d05
refactor to make document logic clearer to follow
insipx Dec 19, 2023
bae9ee2
remove unused
insipx Dec 19, 2023
b046108
ethr builder, docs tests
insipx Dec 19, 2023
08b623f
tests
insipx Dec 20, 2023
159eec7
revert
insipx Dec 20, 2023
25d7b3d
integration testing
insipx Dec 20, 2023
47b225a
integration tests and fmt
insipx Dec 20, 2023
b5fe46d
history.reverse() to read events in the right order
insipx Dec 20, 2023
38b4fd3
docs
insipx Dec 20, 2023
86bae60
fixes and more docs
insipx Dec 20, 2023
94354eb
doc
insipx Dec 20, 2023
1a46a8d
remove spurious newline
insipx Dec 20, 2023
115fd4e
add some light error handling
insipx Dec 21, 2023
86db1f0
0x for blockchain_account_id
insipx Dec 21, 2023
1d82d56
remove some unwraps and polish error reporting
insipx Dec 21, 2023
51aba6b
warnings
insipx Dec 21, 2023
54e7e8f
more docs
insipx Dec 21, 2023
80c3bca
doc typos
insipx Dec 22, 2023
dfcb108
add pkg-config openssl libssl-dev to dockerbuild
insipx Dec 22, 2023
ca370b2
fix anvil spawn
insipx Dec 22, 2023
e4da0c8
Merge branch 'main' of github.com:xmtp/didethresolver into insipx/res…
insipx Dec 22, 2023
19d0af0
a quick test
insipx Dec 22, 2023
02adc57
Use Environment Variables instead of CLI Arguments (#18)
37ng Dec 22, 2023
381674b
fmt
insipx Dec 22, 2023
c64f7b0
better error handling
tsachiherman Jan 2, 2024
94b9212
Merge branch 'main' into tsachi/add_version_id
tsachiherman Jan 3, 2024
b395e61
fmt
tsachiherman Jan 3, 2024
a954680
update
tsachiherman Jan 8, 2024
b918070
fix fmt
tsachiherman Jan 8, 2024
c3472bd
update3
tsachiherman Jan 8, 2024
9b15673
update per peer review.
tsachiherman Jan 9, 2024
7f8f120
update per peer review.
tsachiherman Jan 10, 2024
a94ecaf
Merge branch 'main' into tsachi/add_version_id
tsachiherman Jan 10, 2024
1a20273
update
tsachiherman Jan 10, 2024
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
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rustc-hex= "2.1"
peg = "0.8"
dotenvy = "0.15.7"
envy = "0.4.2"
chrono = "0.4.27"

[dev-dependencies]
jsonrpsee = { version = "0.21", features = ["macros", "server", "client"] }
Expand Down
24 changes: 20 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod util;
use serde::Deserialize;
use std::str::FromStr;

use anyhow::Result;
use anyhow::{Context, Result};
use ethers::types::Address;
use jsonrpsee::server::Server;

Expand Down Expand Up @@ -130,13 +130,29 @@ fn default_provider() -> String {
/// Entrypoint for the did:ethr Gateway
pub async fn run() -> Result<()> {
crate::util::init_logging();
dotenvy::dotenv()?;
let opts: DidEthGatewayApp = envy::from_env()?;
match dotenvy::dotenv() {
Ok(path) => {
// .env file successfully loaded.
log::debug!("Env file {} was loaded successfully", path.display());
}
Err(err) => {
// Error handling for the case where dotenv() fails
log::info!("Unable to load env file(s) : {err}");
}
}
let opts = envy::from_env::<DidEthGatewayApp>()?;

let server = Server::builder().build(opts.address).await?;
let addr = server.local_addr()?;
let registry_address = Address::from_str(DID_ETH_REGISTRY)?;
let resolver = Resolver::new(opts.provider, registry_address).await?;
let provider = opts.provider.clone();
let resolver: Resolver = Resolver::new(opts.provider, registry_address)
.await
.context(format!(
"Unable to create a resolver for provider {} and registry address {}",
provider, registry_address,
))?;

let handle = server.start(rpc::DidRegistryMethods::new(resolver).into_rpc());

log::info!("Server Started at {addr}");
Expand Down
161 changes: 124 additions & 37 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ use ethers::{
contract::LogMeta,
prelude::{LocalWallet, Provider, SignerMiddleware},
providers::{Middleware, Ws},
types::{Address, H160, H256, U256, U64},
types::{Address, Block, H160, H256, U256, U64},
};

use rand::{rngs::StdRng, SeedableRng};

use self::did_registry::{DIDRegistry, DIDRegistryEvents};
use crate::types::DidDocument;
use crate::types::{
DidDocument, DidDocumentMetadata, DidResolutionMetadata, DidResolutionResult, EthrBuilder,
};

type ResolverSigner = SignerMiddleware<Provider<Ws>, LocalWallet>;

Expand All @@ -37,9 +40,14 @@ impl Resolver {
Ok(Self { signer, registry })
}

pub async fn resolve_did(&self, public_key: H160) -> Result<DidDocument> {
pub async fn resolve_did(
&self,
public_key: H160,
version_id: Option<U64>,
) -> Result<DidResolutionResult> {
let history = self.changelog(public_key).await?;
self.wrap_did_document(public_key, history).await
self.wrap_did_resolution(public_key, version_id, history)
.await
}

async fn changelog(&self, public_key: H160) -> Result<Vec<(DIDRegistryEvents, LogMeta)>> {
Expand Down Expand Up @@ -79,56 +87,135 @@ impl Resolver {
Ok(history)
}

async fn wrap_did_document(
fn dispatch_event(
&self,
doc: &mut EthrBuilder,
public_key: H160,
event: DIDRegistryEvents,
meta: LogMeta,
deactivated: &mut bool,
) {
let res = match event {
DIDRegistryEvents::DiddelegateChangedFilter(delegate_changed) => {
doc.delegate_event(delegate_changed)
}
DIDRegistryEvents::DidattributeChangedFilter(attribute_event) => {
doc.attribute_event(attribute_event)
}
DIDRegistryEvents::DidownerChangedFilter(owner_changed) => {
if doc
.owner_event(owner_changed)
.is_ok_and(|deactivated| deactivated)
{
*deactivated = true;
}
Ok(())
}
};

// if a did was set with the wrong format, for instance set-attribute was called with
// raw bytes instead of hex-encoded bytes, we don't want to cancel resolution of the
// rest of the DID
//
// TODO: Send this info as an extra json field to the caller apart from the DID Document
if let Err(e) = res {
log::error!(
"Error while resolving for {} at event block={}, log index={}, incorrect format?: {}",
public_key, meta.block_number, meta.log_index, e,
);
};
}

async fn wrap_did_resolution(
&self,
public_key: H160,
version_id: Option<U64>,
history: Vec<(DIDRegistryEvents, LogMeta)>,
) -> Result<DidDocument> {
) -> Result<DidResolutionResult> {
let mut base_document = DidDocument::ethr_builder();
base_document.public_key(&public_key)?;

let current_block = self.signer.get_block_number().await?;
let current_block = self.signer.get_block(current_block).await?;

let now = current_block.map(|b| b.timestamp).unwrap_or(U256::zero());
let mut version_id = U64::zero();
let mut current_version_id = U64::zero();

base_document.now(now);
let mut last_updated_did_version_id: Option<U64> = None;
let mut deactivated = false;

for (event, meta) in history {
let LogMeta {
block_number,
log_index,
..
} = meta;

if version_id < block_number {
version_id = block_number;
}

let res = match event {
DIDRegistryEvents::DiddelegateChangedFilter(delegate_changed) => {
base_document.delegate_event(delegate_changed)
}
DIDRegistryEvents::DidattributeChangedFilter(attribute_event) => {
base_document.attribute_event(attribute_event)
let LogMeta { block_number, .. } = meta;

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,
&mut deactivated,
);
// 2. set latest version
if current_version_id < block_number {
current_version_id = block_number;
}
} else {
// just update the next version before quitting.
last_updated_did_version_id = Some(block_number);
break;
}
DIDRegistryEvents::DidownerChangedFilter(owner_changed) => {
base_document.owner_event(owner_changed)
} else {
// 1. delegate events
Resolver::dispatch_event(
self,
&mut base_document,
public_key,
event,
meta,
&mut deactivated,
);
// 2. set latest version
if current_version_id < block_number {
current_version_id = block_number;
}
};

// if a did was set with the wrong format, for instance set-attribute was called with
// raw bytes instead of hex-encoded bytes, we don't want to cancel resolution of the
// rest of the DID
//
// TODO: Send this info as an extra json field to the caller apart from the DID Document
if let Err(e) = res {
log::error!(
"Error while resolving for {public_key} at event block={block_number}, log index={log_index}, incorrect format?: {e}",
);
};
}
Ok(base_document.build())

let block_time = |block: Block<H256>| {
block
.time()
.unwrap_or_default()
.format("%Y-%m-%dT%H:%M:%SZ")
.to_string()
};

// get the timestamp for the current_verison_id
let current_version_timestamp = self
.signer
.get_block(current_version_id)
.await?
.map(block_time);

let resolution_result = DidResolutionResult {
document: base_document.build(),
metadata: Some(DidDocumentMetadata {
deactivated,
version_id: current_version_id.as_u64(),
updated: current_version_timestamp,
next_version_id: last_updated_did_version_id.map(|ver| ver.as_u64()),
next_update: match last_updated_did_version_id {
Some(ver) => self.signer.get_block(ver).await?.map(block_time),
None => None::<String>,
},
}),
resolution_metadata: Some(DidResolutionMetadata {
content_type: "application/did+ld+json".to_string(),
}),
};
Ok(resolution_result)
}
}
8 changes: 6 additions & 2 deletions src/rpc/api.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
//! Trait Interface Definitions for DID Registry JSON-RPC

use crate::types::DidDocument;
use crate::types::DidResolutionResult;

use jsonrpsee::{proc_macros::rpc, types::ErrorObjectOwned};

/// Decentralized Identifier JSON-RPC Interface Methods
#[rpc(server, client, namespace = "did")]
pub trait DidRegistry {
#[method(name = "resolveDid")]
async fn resolve_did(&self, public_key: String) -> Result<DidDocument, ErrorObjectOwned>;
async fn resolve_did(
&self,
public_key: String,
version_id: Option<String>,
) -> Result<DidResolutionResult, ErrorObjectOwned>;
}
Loading
Loading