Skip to content

Commit

Permalink
feat(chatffi): message metadata (#5766)
Browse files Browse the repository at this point in the history
Description
---
This adds a metadata field for chat messages. Metadata can be expected
to hold information about a message such as it being a reply to a
previous message, or it being a token request.

Motivation and Context
---
Enhanced chat functionality

How Has This Been Tested?
---
CI / Cucumber.

What process can a PR reviewer use to test or verify this change?
---


Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify

---------

Co-authored-by: SW van Heerden <[email protected]>
  • Loading branch information
brianp and SWvheerden authored Sep 15, 2023
1 parent 6f0d20a commit a9b730a
Show file tree
Hide file tree
Showing 19 changed files with 500 additions and 87 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.

52 changes: 45 additions & 7 deletions base_layer/chat_ffi/chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ struct ChatClientFFI;

struct ChatMessages;

struct Message;

struct TariAddress;

struct TransportConfig;
Expand Down Expand Up @@ -116,20 +118,56 @@ void destroy_chat_config(struct ApplicationConfig *config);
*
* ## Arguments
* `client` - The Client pointer
* `message` - Pointer to a Message struct
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `()` - Does not return a value, equivalent to void in C
*
* # Safety
* The ```message``` should be destroyed after use
*/
void send_chat_message(struct ChatClientFFI *client, struct Message *message, int *error_out);

/**
* Creates a message and returns a ptr to it
*
* ## Arguments
* `receiver` - A string containing a tari address
* `message` - The peer seeds config for the node
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `()` - Does not return a value, equivalent to void in C
* `*mut Message` - A pointer to a message object
*
* # Safety
* The ```receiver``` should be destroyed after use
*/
void send_chat_message(struct ChatClientFFI *client,
struct TariAddress *receiver,
const char *message_c_char,
int *error_out);
struct Message *create_chat_message(struct TariAddress *receiver,
const char *message,
int *error_out);

/**
* Creates message metadata and appends it to a Message
*
* ## Arguments
* `message` - A pointer to a message
* `metadata_type` - An int8 that maps to MessageMetadataType enum
* '0' -> Reply
* '1' -> TokenRequest
* `data` - contents for the metadata in string format
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `()` - Does not return a value, equivalent to void in C
*
* ## Safety
* `message` should be destroyed eventually
*/
void add_chat_message_metadata(struct Message *message,
const int *metadata_type,
const char *data,
int *error_out);

/**
* Add a contact
Expand All @@ -143,9 +181,9 @@ void send_chat_message(struct ChatClientFFI *client,
* `()` - Does not return a value, equivalent to void in C
*
* # Safety
* The ```address``` should be destroyed after use
* The ```receiver``` should be destroyed after use
*/
void add_chat_contact(struct ChatClientFFI *client, struct TariAddress *receiver, int *error_out);
void add_chat_contact(struct ChatClientFFI *client, struct TariAddress *address, int *error_out);

/**
* Check the online status of a contact
Expand Down
141 changes: 123 additions & 18 deletions base_layer/chat_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use tari_common_types::tari_address::TariAddress;
use tari_comms::multiaddr::Multiaddr;
use tari_contacts::contacts_service::{
handle::{DEFAULT_MESSAGE_LIMIT, DEFAULT_MESSAGE_PAGE},
types::Message,
types::{Message, MessageBuilder, MessageMetadata, MessageMetadataType},
};
use tari_p2p::{SocksAuthentication, TorControlAuthentication, TorTransportConfig, TransportConfig, TransportType};
use tari_utilities::hex;
Expand Down Expand Up @@ -485,22 +485,16 @@ unsafe fn init_logging(log_path: PathBuf, error_out: *mut c_int) {
///
/// ## Arguments
/// `client` - The Client pointer
/// `receiver` - A string containing a tari address
/// `message` - The peer seeds config for the node
/// `message` - Pointer to a Message struct
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `()` - Does not return a value, equivalent to void in C
///
/// # Safety
/// The ```receiver``` should be destroyed after use
/// The ```message``` should be destroyed after use
#[no_mangle]
pub unsafe extern "C" fn send_chat_message(
client: *mut ChatClientFFI,
receiver: *mut TariAddress,
message_c_char: *const c_char,
error_out: *mut c_int,
) {
pub unsafe extern "C" fn send_chat_message(client: *mut ChatClientFFI, message: *mut Message, error_out: *mut c_int) {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

Expand All @@ -509,23 +503,119 @@ pub unsafe extern "C" fn send_chat_message(
ptr::swap(error_out, &mut error as *mut c_int);
}

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

(*client)
.runtime
.block_on((*client).client.send_message((*message).clone()));
}

/// Creates a message and returns a ptr to it
///
/// ## Arguments
/// `receiver` - A string containing a tari address
/// `message` - The peer seeds config for the node
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `*mut Message` - A pointer to a message object
///
/// # Safety
/// The ```receiver``` should be destroyed after use
#[no_mangle]
pub unsafe extern "C" fn create_chat_message(
receiver: *mut TariAddress,
message: *const c_char,
error_out: *mut c_int,
) -> *mut Message {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

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

let message = match CStr::from_ptr(message_c_char).to_str() {
let message_str = match CStr::from_ptr(message).to_str() {
Ok(str) => str.to_string(),
Err(e) => {
error = LibChatError::from(InterfaceError::InvalidArgument(e.to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
},
};

let message_out = MessageBuilder::new()
.address((*receiver).clone())
.message(message_str)
.build();

Box::into_raw(Box::new(message_out))
}

/// Creates message metadata and appends it to a Message
///
/// ## Arguments
/// `message` - A pointer to a message
/// `metadata_type` - An int8 that maps to MessageMetadataType enum
/// '0' -> Reply
/// '1' -> TokenRequest
/// `data` - contents for the metadata in string format
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `()` - Does not return a value, equivalent to void in C
///
/// ## Safety
/// `message` should be destroyed eventually
#[no_mangle]
pub unsafe extern "C" fn add_chat_message_metadata(
message: *mut Message,
metadata_type: *const c_int,
data: *const c_char,
error_out: *mut c_int,
) {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

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

let metadata_type = match MessageMetadataType::from_byte(metadata_type as u8) {
Some(t) => t,
None => {
error = LibChatError::from(InterfaceError::InvalidArgument(
"Couldn't convert byte to Metadata type".to_string(),
))
.code;
ptr::swap(error_out, &mut error as *mut c_int);
return;
},
};

(*client)
.runtime
.block_on((*client).client.send_message((*receiver).clone(), message));
if data.is_null() {
error = LibChatError::from(InterfaceError::NullError("data".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return;
}

let data: Vec<u8> = match CStr::from_ptr(data).to_str() {
Ok(str) => str.as_bytes().into(),
Err(e) => {
error = LibChatError::from(InterfaceError::InvalidArgument(e.to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return;
},
};

let metadata = MessageMetadata { metadata_type, data };
(*message).push(metadata);
}

/// Add a contact
Expand All @@ -539,11 +629,11 @@ pub unsafe extern "C" fn send_chat_message(
/// `()` - Does not return a value, equivalent to void in C
///
/// # Safety
/// The ```address``` should be destroyed after use
/// The ```receiver``` should be destroyed after use
#[no_mangle]
pub unsafe extern "C" fn add_chat_contact(
client: *mut ChatClientFFI,
receiver: *mut TariAddress,
address: *mut TariAddress,
error_out: *mut c_int,
) {
let mut error = 0;
Expand All @@ -554,12 +644,12 @@ pub unsafe extern "C" fn add_chat_contact(
ptr::swap(error_out, &mut error as *mut c_int);
}

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

(*client).runtime.block_on((*client).client.add_contact(&(*receiver)));
(*client).runtime.block_on((*client).client.add_contact(&(*address)));
}

/// Check the online status of a contact
Expand Down Expand Up @@ -969,4 +1059,19 @@ mod test {
destroy_chat_tor_transport_config(transport_config);
}
}

#[test]
fn test_metadata_adding() {
let message_ptr = Box::into_raw(Box::default());

let data_c_str = CString::new("hello".to_string()).unwrap();
let data_char: *const c_char = CString::into_raw(data_c_str) as *const c_char;

let error_out = Box::into_raw(Box::new(0));

unsafe { add_chat_message_metadata(message_ptr, 1 as *const c_int, data_char, error_out) }

let message = unsafe { Box::from_raw(message_ptr) };
assert_eq!(message.metadata.len(), 1)
}
}
2 changes: 2 additions & 0 deletions base_layer/contacts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ num-derive = "0.3.3"
num-traits = "0.2.15"
prost = "0.9"
rand = "0.8"
serde = "1.0.136"
serde_json = "1.0.79"
thiserror = "1.0.26"
tokio = { version = "1.23", features = ["sync", "macros"] }
tower = "0.4"
Expand Down
24 changes: 20 additions & 4 deletions base_layer/contacts/examples/chat_client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use tari_comms::{CommsNode, NodeIdentity};
use tari_contacts::contacts_service::{
handle::ContactsServiceHandle,
service::ContactOnlineStatus,
types::{Message, MessageBuilder},
types::{Message, MessageBuilder, MessageMetadata, MessageMetadataType},
};
use tari_shutdown::Shutdown;

Expand All @@ -44,9 +44,11 @@ const LOG_TARGET: &str = "contacts::chat_client";
#[async_trait]
pub trait ChatClient {
async fn add_contact(&self, address: &TariAddress);
fn add_metadata(&self, message: Message, metadata_type: MessageMetadataType, data: String) -> Message;
async fn check_online_status(&self, address: &TariAddress) -> ContactOnlineStatus;
async fn send_message(&self, receiver: TariAddress, message: String);
fn create_message(&self, receiver: &TariAddress, message: String) -> Message;
async fn get_messages(&self, sender: &TariAddress, limit: u64, page: u64) -> Vec<Message>;
async fn send_message(&self, message: Message);
fn identity(&self) -> &NodeIdentity;
fn shutdown(&mut self);
}
Expand Down Expand Up @@ -148,10 +150,10 @@ impl ChatClient for Client {
ContactOnlineStatus::Offline
}

async fn send_message(&self, receiver: TariAddress, message: String) {
async fn send_message(&self, message: Message) {
if let Some(mut contacts_service) = self.contacts.clone() {
contacts_service
.send_message(MessageBuilder::new().message(message).address(receiver).build())
.send_message(message)
.await
.expect("Message wasn't sent");
}
Expand All @@ -168,6 +170,20 @@ impl ChatClient for Client {

messages
}

fn create_message(&self, receiver: &TariAddress, message: String) -> Message {
MessageBuilder::new().address(receiver.clone()).message(message).build()
}

fn add_metadata(&self, mut message: Message, metadata_type: MessageMetadataType, data: String) -> Message {
let metadata = MessageMetadata {
metadata_type,
data: data.into_bytes(),
};

message.push(metadata);
message
}
}

pub async fn wait_for_connectivity(comms: CommsNode) -> anyhow::Result<()> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE contacts drop metadata;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE messages ADD metadata BLOB;
18 changes: 14 additions & 4 deletions base_layer/contacts/proto/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ package tari.contacts.chat;

message Message {
bytes body = 1;
bytes address = 2;
DirectionEnum direction = 3;
uint64 stored_at = 4;
bytes message_id = 5;
repeated MessageMetadata metadata = 2;
bytes address = 3;
DirectionEnum direction = 4;
uint64 stored_at = 5;
bytes message_id = 6;
}

enum DirectionEnum {
Inbound = 0;
Outbound = 1;
}

message MessageMetadata {
MessageTypeEnum metadata_type = 1;
bytes data = 2;
}

enum MessageTypeEnum {
TokenRequest = 0;
}
Loading

0 comments on commit a9b730a

Please sign in to comment.