Skip to content

Commit

Permalink
feat(chatffi): get conversationalists (#5849)
Browse files Browse the repository at this point in the history
Description
---
Add a feature to fetch a list of TariAddresses that a client currently
has conversations with. This is any address a message has been sent to
or received from.

Motivation and Context
---
Without being about to fetch a list of addresses a client has
conversations with, the client can't actually fetch any independent
conversations, as conversations are fetched with an address id.

How Has This Been Tested?
---
New test additions

What process can a PR reviewer use to test or verify this change?
---
Changes are pretty standard throughout. Handler/Service/DB updates.

Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify
  • Loading branch information
brianp authored Oct 24, 2023
1 parent 43b994e commit d9e8e22
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions base_layer/chat_ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ crate-type = ["staticlib","cdylib"]

[dev-dependencies]
chrono = { version = "0.4.19", default-features = false }
rand = "0.8"
tari_crypto = "0.18.0"

[build-dependencies]
cbindgen = "0.24.3"
Expand Down
66 changes: 66 additions & 0 deletions base_layer/chat_ffi/chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct Confirmation;

struct ContactsLivenessData;

struct ConversationalistsVector;

struct Message;

struct MessageMetadata;
Expand Down Expand Up @@ -373,6 +375,70 @@ long long read_liveness_data_last_seen(struct ContactsLivenessData *liveness,
*/
void destroy_contacts_liveness_data(struct ContactsLivenessData *ptr);

/**
* Return a ptr to a ConversationalistsVector
*
* ## Arguments
* `client` - The ChatClient
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `*mut ptr ConversationalistsVector` - a pointer to a ConversationalistsVector
*
* ## Safety
* The `ConversationalistsVector` should be destroyed after use
*/
struct ConversationalistsVector *get_conversationalists(struct ChatClient *client, int *error_out);

/**
* Returns the length of the ConversationalistsVector
*
* ## Arguments
* `conversationalists` - A pointer to a ConversationalistsVector
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `c_int` - The length of the vector. May return -1 if something goes wrong
*
* ## Safety
* `conversationalists` should be destroyed eventually
*/
int conversationalists_vector_len(struct ConversationalistsVector *conversationalists,
int *error_out);

/**
* Reads the ConversationalistsVector and returns a pointer to a TariAddress at a given position
*
* ## Arguments
* `conversationalists` - A pointer to a ConversationalistsVector
* `position` - The index of the vector for a TariAddress
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `*mut ptr TariAddress` - A pointer to a TariAddress
*
* ## Safety
* `conversationalists` should be destroyed eventually
* the returned `TariAddress` should be destroyed eventually
*/
struct TariAddress *conversationalists_vector_get_at(struct ConversationalistsVector *conversationalists,
unsigned int position,
int *error_out);

/**
* Frees memory for ConversationalistsVector
*
* ## Arguments
* `ptr` - The pointer of a ConversationalistsVector
*
* ## Returns
* `()` - Does not return a value, equivalent to void in C
*
* # Safety
* None
*/
void destroy_conversationalists_vector(struct ConversationalistsVector *ptr);

/**
* Creates a message and returns a pointer to it
*
Expand Down
186 changes: 186 additions & 0 deletions base_layer/chat_ffi/src/conversationalists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2023, The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::{convert::TryFrom, ptr};

use libc::{c_int, c_uint};
use tari_chat_client::ChatClient as ChatClientTrait;
use tari_common_types::tari_address::TariAddress;

use crate::{
error::{InterfaceError, LibChatError},
ChatClient,
};

#[derive(Debug, PartialEq, Clone)]
pub struct ConversationalistsVector(pub Vec<TariAddress>);

/// Return a ptr to a ConversationalistsVector
///
/// ## Arguments
/// `client` - The ChatClient
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `*mut ptr ConversationalistsVector` - a pointer to a ConversationalistsVector
///
/// ## Safety
/// The `ConversationalistsVector` should be destroyed after use
#[no_mangle]
pub unsafe extern "C" fn get_conversationalists(
client: *mut ChatClient,
error_out: *mut c_int,
) -> *mut ConversationalistsVector {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

if client.is_null() {
error = LibChatError::from(InterfaceError::NullError("client".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
}

let conversationalists = (*client).runtime.block_on((*client).client.get_conversationalists());

Box::into_raw(Box::new(ConversationalistsVector(conversationalists)))
}

/// Returns the length of the ConversationalistsVector
///
/// ## Arguments
/// `conversationalists` - A pointer to a ConversationalistsVector
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `c_int` - The length of the vector. May return -1 if something goes wrong
///
/// ## Safety
/// `conversationalists` should be destroyed eventually
#[no_mangle]
pub unsafe extern "C" fn conversationalists_vector_len(
conversationalists: *mut ConversationalistsVector,
error_out: *mut c_int,
) -> c_int {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

if conversationalists.is_null() {
error = LibChatError::from(InterfaceError::NullError("conversationalists".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return -1;
}

let conversationalists = &(*conversationalists);
c_int::try_from(conversationalists.0.len()).unwrap_or(-1)
}

/// Reads the ConversationalistsVector and returns a pointer to a TariAddress at a given position
///
/// ## Arguments
/// `conversationalists` - A pointer to a ConversationalistsVector
/// `position` - The index of the vector for a TariAddress
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `*mut ptr TariAddress` - A pointer to a TariAddress
///
/// ## Safety
/// `conversationalists` should be destroyed eventually
/// the returned `TariAddress` should be destroyed eventually
#[no_mangle]
pub unsafe extern "C" fn conversationalists_vector_get_at(
conversationalists: *mut ConversationalistsVector,
position: c_uint,
error_out: *mut c_int,
) -> *mut TariAddress {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

if conversationalists.is_null() {
error = LibChatError::from(InterfaceError::NullError("conversationalists".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}

let conversationalists = &(*conversationalists);

let len = conversationalists.0.len() - 1;
if position as usize > len {
error = LibChatError::from(InterfaceError::PositionInvalidError).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}

Box::into_raw(Box::new(conversationalists.0[position as usize].clone()))
}

/// Frees memory for ConversationalistsVector
///
/// ## Arguments
/// `ptr` - The pointer of a ConversationalistsVector
///
/// ## Returns
/// `()` - Does not return a value, equivalent to void in C
///
/// # Safety
/// None
#[no_mangle]
pub unsafe extern "C" fn destroy_conversationalists_vector(ptr: *mut ConversationalistsVector) {
if !ptr.is_null() {
drop(Box::from_raw(ptr))
}
}

#[cfg(test)]
mod test {
use rand::rngs::OsRng;
use tari_common::configuration::Network;
use tari_common_types::types::PublicKey;
use tari_crypto::keys::PublicKey as PubKeyTrait;

use super::*;
use crate::tari_address::destroy_tari_address;

#[test]
fn test_retrieving_conversationalists_from_vector() {
let (_, pk) = PublicKey::random_keypair(&mut OsRng);
let a = TariAddress::from_public_key(&pk, Network::LocalNet);
let conversationalists =
ConversationalistsVector(vec![TariAddress::default(), TariAddress::default(), a.clone()]);

let conversationalists_len = conversationalists.0.len();
let conversationalist_vector_ptr = Box::into_raw(Box::new(conversationalists));
let error_out = Box::into_raw(Box::new(0));

unsafe {
let conversationalist_vector_len = conversationalists_vector_len(conversationalist_vector_ptr, error_out);
assert_eq!(conversationalist_vector_len as usize, conversationalists_len);

let address_ptr = conversationalists_vector_get_at(conversationalist_vector_ptr, 2, error_out);
let address = (*address_ptr).clone();
assert_eq!(a, address);

destroy_conversationalists_vector(conversationalist_vector_ptr);
destroy_tari_address(address_ptr);
drop(Box::from_raw(error_out));
}
}
}
1 change: 1 addition & 0 deletions base_layer/chat_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod callback_handler;
mod confirmation;
mod contacts;
mod contacts_liveness_data;
mod conversationalists;
mod error;
mod logging;
mod message;
Expand Down
13 changes: 13 additions & 0 deletions base_layer/contacts/src/chat_client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub trait ChatClient {
async fn get_messages(&self, sender: &TariAddress, limit: u64, page: u64) -> Vec<Message>;
async fn send_message(&self, message: Message);
async fn send_read_receipt(&self, message: Message);
async fn get_conversationalists(&self) -> Vec<TariAddress>;
fn identity(&self) -> &NodeIdentity;
fn shutdown(&mut self);
}
Expand Down Expand Up @@ -194,6 +195,18 @@ impl ChatClient for Client {
message.push(metadata);
message
}

async fn get_conversationalists(&self) -> Vec<TariAddress> {
let mut addresses = vec![];
if let Some(mut contacts_service) = self.contacts.clone() {
addresses = contacts_service
.get_conversationalists()
.await
.expect("Addresses from conversations not fetched");
}

addresses
}
}

pub async fn wait_for_connectivity(comms: CommsNode) -> anyhow::Result<()> {
Expand Down
13 changes: 13 additions & 0 deletions base_layer/contacts/src/contacts_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub enum ContactsServiceRequest {
SendMessage(TariAddress, Message),
GetMessages(TariAddress, i64, i64),
SendReadConfirmation(TariAddress, Confirmation),
GetConversationalists,
}

#[derive(Debug)]
Expand All @@ -151,6 +152,7 @@ pub enum ContactsServiceResponse {
Messages(Vec<Message>),
MessageSent,
ReadConfirmationSent,
Conversationalists(Vec<TariAddress>),
}

#[derive(Clone)]
Expand Down Expand Up @@ -306,4 +308,15 @@ impl ContactsServiceHandle {
_ => Err(ContactsServiceError::UnexpectedApiResponse),
}
}

pub async fn get_conversationalists(&mut self) -> Result<Vec<TariAddress>, ContactsServiceError> {
match self
.request_response_service
.call(ContactsServiceRequest::GetConversationalists)
.await??
{
ContactsServiceResponse::Conversationalists(addresses) => Ok(addresses),
_ => Err(ContactsServiceError::UnexpectedApiResponse),
}
}
}
4 changes: 4 additions & 0 deletions base_layer/contacts/src/contacts_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ where T: ContactsBackend + 'static

Ok(ContactsServiceResponse::ReadConfirmationSent)
},
ContactsServiceRequest::GetConversationalists => {
let result = self.db.get_conversationlists();
Ok(result.map(ContactsServiceResponse::Conversationalists)?)
},
}
}

Expand Down
Loading

0 comments on commit d9e8e22

Please sign in to comment.