Skip to content

Commit

Permalink
Refactor/typestate connections (#739)
Browse files Browse the repository at this point in the history
Reimplement (non-mediated) Connection, update napi-rs, sync up rust agent, reformat libvdrtools

* Remove obsolete structs
* Removed transport type from type state Connection
* Factored out build_con_request method
* Moved all typestate connection related code to a different module
* Refactored inner methods and simplified them
* Reverted changes on current code as the typestate_con is separated into a different module
* Added serde capabilities to typestate Connection
* Added invitation processing to Invitee and moved some Connection methods to their appropriate types
* Renamed SerdeCon to VagueConnection. This will be used for deserialization only
* Added dedicated serialization types for borrowed serialization of Connection and implemented Serialize
* Fixed Connection serialization trait bounds
* Reverted to using invitation ID as thread ID
* Added direct deserialization support to Connection through try_from impl of VagueConnection
* Set typestate_con states attributes pub(crate)
* Added typestate_con libvcx implementation draft
* Completed typestate_con implementation in libvcx
* Renamed remove_connection in libvcx to get_cloned_connection and implemented cloning to persist previous state in the cache
* Removed the lazy_static HTTPCLIENT in favor of using the ZST directly
* Retro-fitted the process_request and send_response methods for backwards compatibility
* Added ProblemReport generation and sending
* Added test From implementations between SerializableConnection and VagueConnection for compile-time check that types match
* Added boilerplate macro for From impls for SerializationConnection
* Modified aries-vcx-agent to use typestate connections
* Fixed method rename in libvcx
* Removed send_invitation method
* Typestate connection cosmetic changes, comments, and modules re-organization
* Removed previous non-mediated Connection in favor of newer typestate Connection
* Fixed state differences in Node.js wrapper
* Reuse request did_doc
* Moved Transport trait and auto-implemented for references of types implementing it
* Fix FFI wrapper to return a Promise for the async request handling
* Reverting dependencies in vcxagent-core
* Ignore dist dirs
* Reverted NAPI changes and mimicked old connection states more accurately for backwards compatibility
* Fixed accept_invitation log comment and added more explicit logs to libvcx
* processInvite is now a Promise
* Fixed thread_id handling when accepting invitation and sending request as an invitee
* More state retro-fitting with previous implementation
* Fix thread_id usage in Request generation
* Added comments
* Added concrete and generic connection serialization and deserialization tests
* Added whitespace in libcx
* Ran cargo fmt
* Completely separated states between invitee and inviter
* Added BootstrapDidDoc trait
* Added non-mediated connection OOB handling
* Improved OOB connection existence look-up and implemented libvcx non-mediated connection calls
* Implemented necessary changes to node wrappers to expose non-mediated connections
* Unexposed internal OOB method
* Added re-export of PairwiseInfo to mediatedconnection to avoid struct duplication
* Ran cargo fmt
* Fixed GenericConnection doc test and comment
* Made processInvite in node wrapper async
* Fixed comments typos
* Updated napi and napi-derive dependencies version
* Made from_parts public and added into_parts method on Connection

Signed-off-by: Bogdan Mircea <mirceapetrebogdan@gmail.com>
  • Loading branch information
bobozaur authored Feb 13, 2023
1 parent 66ee076 commit 66c8d65
Show file tree
Hide file tree
Showing 147 changed files with 4,561 additions and 1,809 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
**/.DS_Store
**/node_modules
**/*.node
**/dist
wrappers/ios/vcx/vcx.framework/**
wrappers/ios/vcx/vcx.framework.dSYM/**
wrappers/ios_legacy/vcx/vcx.framework/**
Expand Down
23 changes: 13 additions & 10 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion agents/node/vcxagent-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"test:integration:out-of-band": "jest --forceExit --env=node --runInBand test/out-of-band.spec.js",
"test:integration:nonmediated-endpoint": "jest --forceExit --env=node --runInBand test/nonmediated-endpoint.spec.js",
"test:integration:nonmediated-connection": "jest --forceExit --env=node --runInBand test/nonmediated-connection.spec.js"
},
},
"dependencies": {
"axios": "^0.27.2",
"ffi-napi": "^4.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports.createServiceNonmediatedConnections = function createServiceNonme
logger.info(`inviteeConnectionCreateFromInvite >> connectionId=${connectionId}, invite: ${invite}`)
const connection = await NonmediatedConnection.createInvitee(invite)
logger.debug(`InviteeConnectionSM after created from invitation:\n${JSON.stringify(connection.serialize())}`)
connection.processInvite(invite)
await connection.processInvite(invite)
await connection.sendRequest(endpointInfo)
logger.debug(`InviteeConnectionSM after sending request:\n${JSON.stringify(connection.serialize())}`)
await saveNonmediatedConnection(connectionId, connection)
Expand Down
1 change: 1 addition & 0 deletions agents/rust/aries-vcx-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition.workspace = true
[dependencies]
serde = "1.0.145"
aries-vcx = { path = "../../../aries_vcx" }
async-trait = "0.1.64"
derive_builder = "0.11.2"
serde_json = "1.0.85"
log = "0.4.17"
Expand Down
13 changes: 13 additions & 0 deletions agents/rust/aries-vcx-agent/src/http_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use aries_vcx::{agency_client::httpclient::post_message, errors::error::VcxResult, transport::Transport};

use async_trait::async_trait;

pub struct HttpClient;

#[async_trait]
impl Transport for HttpClient {
async fn send_message(&self, msg: Vec<u8>, service_endpoint: &str) -> VcxResult<()> {
post_message(msg, service_endpoint).await?;
Ok(())
}
}
1 change: 1 addition & 0 deletions agents/rust/aries-vcx-agent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern crate uuid;

mod agent;
mod error;
mod http_client;
mod services;
mod storage;

Expand Down
131 changes: 82 additions & 49 deletions agents/rust/aries-vcx-agent/src/services/connection.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use std::sync::{Arc, Mutex};

use crate::error::*;
use crate::http_client::HttpClient;
use crate::storage::object_cache::ObjectCache;
use crate::storage::Storage;
use aries_vcx::common::ledger::transactions::into_did_doc;
use aries_vcx::core::profile::profile::Profile;
use aries_vcx::handlers::connection::connection::{Connection, ConnectionState};
use aries_vcx::messages::a2a::A2AMessage;
use aries_vcx::messages::concepts::ack::Ack;
use aries_vcx::messages::protocols::connection::invite::Invitation;
use aries_vcx::messages::protocols::connection::request::Request;
use aries_vcx::messages::protocols::connection::response::SignedResponse;
use aries_vcx::protocols::connection::pairwise_info::PairwiseInfo;
use aries_vcx::protocols::connection::{Connection, GenericConnection, State, ThinState};

pub type ServiceEndpoint = String;

pub struct ServiceConnections {
profile: Arc<dyn Profile>,
service_endpoint: ServiceEndpoint,
connections: Arc<ObjectCache<Connection>>,
connections: Arc<ObjectCache<GenericConnection>>,
}

impl ServiceConnections {
Expand All @@ -31,93 +31,126 @@ impl ServiceConnections {
}

pub async fn create_invitation(&self, pw_info: Option<PairwiseInfo>) -> AgentResult<Invitation> {
let inviter = Connection::create_inviter(&self.profile, pw_info)
.await?
.create_invite(self.service_endpoint.clone(), vec![])
.await?;
let invite = inviter
.get_invite_details()
.ok_or_else(|| AgentError::from_kind(AgentErrorKind::InviteDetails))?
.clone();
self.connections.insert(&inviter.get_thread_id(), inviter)?;
let pw_info = pw_info.unwrap_or(PairwiseInfo::create(&self.profile.inject_wallet()).await?);
let inviter =
Connection::new_inviter("".to_owned(), pw_info).create_invitation(vec![], self.service_endpoint.clone());
let invite = inviter.get_invitation().clone();
let thread_id = inviter.thread_id().to_owned();

self.connections.insert(&thread_id, inviter.into())?;

Ok(invite)
}

pub async fn receive_invitation(&self, invite: Invitation) -> AgentResult<String> {
let did_doc = into_did_doc(&self.profile, &invite).await?;
let invitee = Connection::create_invitee(&self.profile, did_doc)
.await?
.process_invite(invite)?;
self.connections.insert(&invitee.get_thread_id(), invitee)
let pairwise_info = PairwiseInfo::create(&self.profile.inject_wallet()).await?;
let invitee = Connection::new_invitee("".to_owned(), pairwise_info)
.accept_invitation(&self.profile, invite)
.await?;

let thread_id = invitee.thread_id().to_owned();

self.connections.insert(&thread_id, invitee.into())
}

pub async fn send_request(&self, thread_id: &str) -> AgentResult<()> {
let invitee = self
.connections
.get(thread_id)?
.send_request(&self.profile, self.service_endpoint.clone(), vec![], None)
let invitee: Connection<_, _> = self.connections.get(thread_id)?.try_into()?;
let invitee = invitee
.send_request(
&self.profile.inject_wallet(),
self.service_endpoint.clone(),
vec![],
&HttpClient,
)
.await?;
self.connections.insert(thread_id, invitee)?;

self.connections.insert(thread_id, invitee.into())?;
Ok(())
}

pub async fn accept_request(&self, thread_id: &str, request: Request) -> AgentResult<()> {
let inviter = self
.connections
.get(thread_id)?
.process_request(&self.profile, request, self.service_endpoint.clone(), vec![], None)
let inviter = self.connections.get(thread_id)?;

let inviter = match inviter.state() {
ThinState::Inviter(State::Initial) => Connection::try_from(inviter)
.map_err(From::from)
.map(|c| c.into_invited(&request.id.0)),
ThinState::Inviter(State::Invited) => Connection::try_from(inviter).map_err(From::from),
s => Err(AgentError::from_msg(
AgentErrorKind::GenericAriesVcxError,
&format!(
"Connection with handle {} cannot process a request; State: {:?}",
thread_id, s
),
)),
}?;

let inviter = inviter
.handle_request(
&self.profile.inject_wallet(),
request,
self.service_endpoint.clone(),
vec![],
&HttpClient,
)
.await?;
self.connections.insert(thread_id, inviter)?;

self.connections.insert(thread_id, inviter.into())?;

Ok(())
}

pub async fn send_response(&self, thread_id: &str) -> AgentResult<()> {
let inviter = self
.connections
.get(thread_id)?
.send_response(&self.profile, None)
let inviter: Connection<_, _> = self.connections.get(thread_id)?.try_into()?;
let inviter = inviter
.send_response(&self.profile.inject_wallet(), &HttpClient)
.await?;
self.connections.insert(thread_id, inviter)?;

self.connections.insert(thread_id, inviter.into())?;

Ok(())
}

pub async fn accept_response(&self, thread_id: &str, response: SignedResponse) -> AgentResult<()> {
let invitee = self
.connections
.get(thread_id)?
.process_response(&self.profile, response, None)
let invitee: Connection<_, _> = self.connections.get(thread_id)?.try_into()?;
let invitee = invitee
.handle_response(&self.profile.inject_wallet(), response, &HttpClient)
.await?;
self.connections.insert(thread_id, invitee)?;

self.connections.insert(thread_id, invitee.into())?;

Ok(())
}

pub async fn send_ack(&self, thread_id: &str) -> AgentResult<()> {
let invitee = self.connections.get(thread_id)?.send_ack(&self.profile, None).await?;
self.connections.insert(thread_id, invitee)?;
let invitee: Connection<_, _> = self.connections.get(thread_id)?.try_into()?;
let invitee = invitee.send_ack(&self.profile.inject_wallet(), &HttpClient).await?;

self.connections.insert(thread_id, invitee.into())?;

Ok(())
}

pub async fn process_ack(&self, thread_id: &str, ack: Ack) -> AgentResult<()> {
let inviter = self
.connections
.get(thread_id)?
.process_ack(A2AMessage::Ack(ack))
.await?;
self.connections.insert(thread_id, inviter)?;
let inviter: Connection<_, _> = self.connections.get(thread_id)?.try_into()?;
let inviter = inviter.acknowledge_connection(&A2AMessage::Ack(ack))?;

self.connections.insert(thread_id, inviter.into())?;

Ok(())
}

pub fn get_state(&self, thread_id: &str) -> AgentResult<ConnectionState> {
Ok(self.connections.get(thread_id)?.get_state())
pub fn get_state(&self, thread_id: &str) -> AgentResult<ThinState> {
Ok(self.connections.get(thread_id)?.state())
}

pub(in crate::services) fn get_by_id(&self, thread_id: &str) -> AgentResult<Connection> {
pub(in crate::services) fn get_by_id(&self, thread_id: &str) -> AgentResult<GenericConnection> {
self.connections.get(thread_id)
}

pub fn get_by_their_vk(&self, their_vk: &str) -> AgentResult<Vec<String>> {
let their_vk = their_vk.to_string();
let f = |(id, m): (&String, &Mutex<Connection>)| -> Option<String> {
let f = |(id, m): (&String, &Mutex<GenericConnection>)| -> Option<String> {
let connection = m.lock().unwrap();
match connection.remote_vk() {
Ok(remote_vk) if remote_vk == their_vk => Some(id.to_string()),
Expand Down
Loading

0 comments on commit 66c8d65

Please sign in to comment.