Skip to content

Commit

Permalink
Uniffi simple message relay (#970)
Browse files Browse the repository at this point in the history
  • Loading branch information
swaptr authored Sep 24, 2023
1 parent adefa28 commit bfac035
Show file tree
Hide file tree
Showing 18 changed files with 269 additions and 44 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

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

14 changes: 14 additions & 0 deletions uniffi_aries_vcx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ sh uniffi_aries_vcx/scripts/android.build.cargo.ndk.sh

NB: Before running the demo application you need to generate the language bindings.

# Testing the simple message relay on Android

Aries-VCX supports connection to [tools/simple_message_relay](/tools/simple_message_relay/) on android. You need to setup the agent with instructions [here](/tools/simple_message_relay/README.md#service-setup) and exposing a public endpoint as explained [here](/tools/simple_message_relay/README.md#public-endpoints).

The demo app needs this endpoint to establish communication with the peer.

Update [BASE_RELAY_ENDPOINT](./demo/app/src/main/java/org/hyperledger/ariesvcx/Constants.kt) to reflect the public IP.

```kt
const val BASE_RELAY_ENDPOINT = <your-public-ip-endpoint>;
```

Now you are ready to start communicating. Use the QR scanner in the app to establish connection with the peer.

## Support

Currently the builds have been tested for android `arm64 (aarch64)` on a physical device. In the future we plan to support other architectures.
1 change: 1 addition & 0 deletions uniffi_aries_vcx/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tokio = { version = "1.24.1" }
once_cell = "1.17.0"
thiserror = "1.0.38"
serde_json = "1.0.91"
serde = { version = "1.0.188", features = ["derive"] }
async-trait = "0.1.64"
diddoc_legacy = { path = "../../diddoc_legacy" }
url = "2.3.1"
Expand Down
3 changes: 3 additions & 0 deletions uniffi_aries_vcx/core/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Core UniFFI Aries VCX wrapper

This crate contains the proof of concept UniFFI wrapper over the `aries-vcx` crate. The wrapper's purpose is to very thinly wrap over the `aries-vcx` crate, to make it UniFFI-friendly.

# Scaffolding

Scaffolding is the rust code generated by UniFFI to create C-callable bindings to this crate. These bindings are automatically built by `build.rs`, targetting the `vcx.udl` file. These bindings are located in the `target` directory of the project/workspace and will show compiler errors if the `vcx.udl` interface does not integrate with this crate.

# Wrapper generation

Wrappers can be generated using the `uniffi-bindgen.rs` build script for kotlin and swift:

```
Expand Down
1 change: 1 addition & 0 deletions uniffi_aries_vcx/core/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod http_client;
pub mod profile;
pub mod unpack_message;
28 changes: 28 additions & 0 deletions uniffi_aries_vcx/core/src/core/unpack_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::sync::Arc;

use super::profile::ProfileHolder;
use crate::{errors::error::VcxUniFFIResult, runtime::block_on};
use aries_vcx::errors::error::AriesVcxError;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct UnpackMessage {
pub message: String,
pub recipient_verkey: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sender_verkey: Option<String>,
}

pub fn unpack_message(
profile_holder: Arc<ProfileHolder>,
packed_msg: String,
) -> VcxUniFFIResult<UnpackMessage> {
block_on(async {
let packed_bytes = packed_msg.as_bytes();
let wallet = profile_holder.inner.inject_wallet();
let unpacked_bytes = wallet.unpack_message(&packed_bytes).await?;
let unpacked_string = String::from_utf8(unpacked_bytes)?;
let unpacked_message = serde_json::from_str::<UnpackMessage>(&unpacked_string)?;
Ok(unpacked_message)
})
}
2 changes: 2 additions & 0 deletions uniffi_aries_vcx/core/src/errors/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub enum VcxUniFFIError {
error_msg
)]
SerializationError { error_msg: String },
#[error("A string could not be parsed. More Info: {}", error_msg)]
StringParseError { error_msg: String },
#[error("An unexpected internal error occured. More Info: {}", error_msg)]
InternalError { error_msg: String },
}
10 changes: 9 additions & 1 deletion uniffi_aries_vcx/core/src/errors/mapping_from_others.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::PoisonError;
use std::{string::FromUtf8Error, sync::PoisonError};

use super::error::VcxUniFFIError;

Expand All @@ -17,3 +17,11 @@ impl From<serde_json::Error> for VcxUniFFIError {
}
}
}

impl From<FromUtf8Error> for VcxUniFFIError {
fn from(e: FromUtf8Error) -> Self {
VcxUniFFIError::StringParseError {
error_msg: e.to_string(),
}
}
}
10 changes: 5 additions & 5 deletions uniffi_aries_vcx/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pub mod errors;
pub mod handlers;
pub mod runtime;

use aries_vcx::{
aries_vcx_core::wallet::indy::WalletConfig, protocols::connection::pairwise_info::PairwiseInfo,
};
use crate::core::profile::*;
use crate::core::unpack_message::*;
use crate::errors::error::*;
use aries_vcx::aries_vcx_core::wallet::indy::WalletConfig;
use aries_vcx::protocols::connection::pairwise_info::PairwiseInfo;
use handlers::connection::*;

use crate::{core::profile::*, errors::error::*};
13 changes: 11 additions & 2 deletions uniffi_aries_vcx/core/src/vcx.udl
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ enum ConnectionProtocolState {
"Completed",
};


dictionary PairwiseInfo {
string pw_did;
string pw_vk;
};

dictionary UnpackMessage {
string message;
string recipient_verkey;
string? sender_verkey;
};

interface Connection {
[Throws=VcxUniFFIError]
ConnectionState get_state();
Expand Down Expand Up @@ -68,6 +73,7 @@ enum VcxUniFFIError {
"AriesVcxError",
"SerializationError",
"InternalError",
"StringParseError"
};

namespace vcx {
Expand All @@ -79,4 +85,7 @@ namespace vcx {

[Throws=VcxUniFFIError]
Connection create_invitee(ProfileHolder profile);
};

[Throws=VcxUniFFIError]
UnpackMessage unpack_message(ProfileHolder profile, string packed_msg);
};
3 changes: 3 additions & 0 deletions uniffi_aries_vcx/demo/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,7 @@ dependencies {

// QR
implementation 'com.google.zxing:core:3.5.2'

// HTTP requests
implementation("com.squareup.okhttp3:okhttp:4.11.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.hyperledger.ariesvcx

// Set your public IP address here, this endpoint will be used while communicating with the peer(agent).
const val BASE_RELAY_ENDPOINT = "https://b199-27-57-116-96.ngrok-free.app";
const val RELAY_USER_ID = "demo-user-1";
Original file line number Diff line number Diff line change
@@ -1,37 +1,76 @@
package org.hyperledger.ariesvcx

import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.hyperledger.ariesvcx.utils.await


@Composable
fun HomeScreen(navController: NavHostController, setProfileHolder: (ProfileHolder) -> Unit, profileHolder: ProfileHolder?, connection: Connection?) {
fun HomeScreen(
navController: NavHostController,
setProfileHolder: (ProfileHolder) -> Unit,
profileHolder: ProfileHolder?,
connection: Connection?,
walletConfig: WalletConfig,
connectionRequestState: Boolean,
httpClient: OkHttpClient
) {

val scope = rememberCoroutineScope()
val context = LocalContext.current

val walletConfigState = WalletConfig(
walletName = "test_create_wallet_add_uuid_here",
walletKey = "8dvfYSt5d1taSd6yJdpjq4emkwsPDDLYxkNFysFD2cZY",
walletKeyDerivation = "RAW",
walletType = null,
storageConfig = null,
storageCredentials = null,
rekey = null,
rekeyDerivationMethod = null
)
var flagKeepFetching by remember {
mutableStateOf(true)
}

val request = Request.Builder()
.url("$BASE_RELAY_ENDPOINT/pop_user_message/$RELAY_USER_ID")
.build()


LaunchedEffect(true) {
scope.launch(Dispatchers.IO) {
while (flagKeepFetching && connectionRequestState) {
delay(500)
val response = httpClient.newCall(request).await()
if (response.code == 200) {
val message = response.body!!.string()

val unpackedMessage = unpackMessage(
profileHolder!!,
message
)

Log.d("HOMESCREEN", "HomeScreen: ${unpackedMessage.message}")
connection?.handleResponse(profileHolder, unpackedMessage.message)
flagKeepFetching = false
connection?.sendAck(profileHolder)
}
}
}
}

Column(
modifier = Modifier.fillMaxSize(),
Expand All @@ -41,20 +80,24 @@ fun HomeScreen(navController: NavHostController, setProfileHolder: (ProfileHolde
Button(
enabled = (connection == null),
onClick = {
scope.launch(Dispatchers.IO) {
val profile = newIndyProfile(walletConfigState)
setProfileHolder(profile)
withContext(Dispatchers.Main) {
Toast.makeText(context, "New Profile Created: ${profile.toString()}", Toast.LENGTH_SHORT).show()
scope.launch(Dispatchers.IO) {
val profile = newIndyProfile(walletConfig)
setProfileHolder(profile)
withContext(Dispatchers.Main) {
Toast.makeText(
context,
"New Profile Created: $profile",
Toast.LENGTH_SHORT
).show()
}
}
}
}) {
}) {
Text(text = "New Indy Profile")
}
Button(enabled = (profileHolder != null && connection != null),
onClick = {
navController.navigate(Destination.QRScan.route)
}) {
navController.navigate(Destination.QRScan.route)
}) {
Text(text = "Scan QR Code")
}
}
Expand Down
Loading

0 comments on commit bfac035

Please sign in to comment.