From 4adcb26544e897ade7c02b5d9d1e432b1ae69df5 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Mon, 6 May 2024 15:26:41 +0200 Subject: [PATCH] feat: add sent at timestamp to chat messages (#6314) Description --- Add sent at timestamp to messages. Motivation and Context --- Because of the lack of a central time server, its hard to get accurate timestamp, the chat services uses the received at as the timestamp. But when the receiver gets messages through saf, it can get multiple at the same time. This fudles up the order completely. So we add sent_at timestamp to help with ordering of messages. --- base_layer/chat_ffi/chat.h | 15 +++++++++++ base_layer/chat_ffi/src/message.rs | 25 +++++++++++++++++++ .../down.sql | 1 + .../up.sql | 1 + .../contacts/src/contacts_service/service.rs | 1 + .../storage/types/messages.rs | 8 +++++- .../src/contacts_service/types/message.rs | 1 + base_layer/contacts/src/schema.rs | 1 + base_layer/wallet_ffi/src/lib.rs | 2 ++ 9 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/down.sql create mode 100644 base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/up.sql diff --git a/base_layer/chat_ffi/chat.h b/base_layer/chat_ffi/chat.h index 89893c9b37..7117f6df17 100644 --- a/base_layer/chat_ffi/chat.h +++ b/base_layer/chat_ffi/chat.h @@ -621,6 +621,21 @@ unsigned char read_chat_message_direction(struct Message *message, int *error_ou */ unsigned long long read_chat_message_stored_at(struct Message *message, int *error_out); +/** + * Returns a c_ulonglong representation of the sent at timestamp as seconds since epoch + * + * ## Arguments + * `message` - A pointer to a Message + * `error_out` - Pointer to an int which will be modified + * + * ## Returns + * `c_ulonglong` - The stored_at timestamp, seconds since epoch. Returns 0 if message is null. + * + * ## Safety + * `message` should be destroyed eventually + */ +unsigned long long read_chat_message_sent_at(struct Message *message, int *error_out); + /** * Returns a c_ulonglong representation of the delivery confirmation timestamp as seconds since epoch * diff --git a/base_layer/chat_ffi/src/message.rs b/base_layer/chat_ffi/src/message.rs index 9be70ab11b..859ca1a76f 100644 --- a/base_layer/chat_ffi/src/message.rs +++ b/base_layer/chat_ffi/src/message.rs @@ -332,6 +332,31 @@ pub unsafe extern "C" fn read_chat_message_stored_at(message: *mut Message, erro (*message).stored_at as c_ulonglong } +/// Returns a c_ulonglong representation of the sent at timestamp as seconds since epoch +/// +/// ## Arguments +/// `message` - A pointer to a Message +/// `error_out` - Pointer to an int which will be modified +/// +/// ## Returns +/// `c_ulonglong` - The stored_at timestamp, seconds since epoch. Returns 0 if message is null. +/// +/// ## Safety +/// `message` should be destroyed eventually +#[no_mangle] +pub unsafe extern "C" fn read_chat_message_sent_at(message: *mut Message, error_out: *mut c_int) -> c_ulonglong { + 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 0; + } + + (*message).sent_at as c_ulonglong +} + /// Returns a c_ulonglong representation of the delivery confirmation timestamp as seconds since epoch /// /// ## Arguments diff --git a/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/down.sql b/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/down.sql new file mode 100644 index 0000000000..ec471b151f --- /dev/null +++ b/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/down.sql @@ -0,0 +1 @@ +ALTER TABLE messages drop sent_at; diff --git a/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/up.sql b/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/up.sql new file mode 100644 index 0000000000..37bad863b2 --- /dev/null +++ b/base_layer/contacts/migrations/2024-04-30-155100_add_sent_timestamp/up.sql @@ -0,0 +1 @@ + ALTER TABLE messages ADD sent_at TIMESTAMP; diff --git a/base_layer/contacts/src/contacts_service/service.rs b/base_layer/contacts/src/contacts_service/service.rs index 87f6379dbb..c09e13fa32 100644 --- a/base_layer/contacts/src/contacts_service/service.rs +++ b/base_layer/contacts/src/contacts_service/service.rs @@ -299,6 +299,7 @@ where T: ContactsBackend + 'static Ok(result.map(ContactsServiceResponse::Messages)?) }, ContactsServiceRequest::SendMessage(address, mut message) => { + message.sent_at = Utc::now().naive_utc().timestamp() as u64; let ob_message = OutboundDomainMessage::from(MessageDispatch::Message(message.clone())); message.stored_at = Utc::now().naive_utc().timestamp() as u64; diff --git a/base_layer/contacts/src/contacts_service/storage/types/messages.rs b/base_layer/contacts/src/contacts_service/storage/types/messages.rs index 83c1339836..2f32fba8f3 100644 --- a/base_layer/contacts/src/contacts_service/storage/types/messages.rs +++ b/base_layer/contacts/src/contacts_service/storage/types/messages.rs @@ -46,6 +46,7 @@ pub struct MessagesSqlInsert { pub body: Vec, pub metadata: Vec, pub stored_at: NaiveDateTime, + pub sent_at: NaiveDateTime, pub direction: i32, } @@ -58,6 +59,7 @@ pub struct MessagesSql { pub body: Vec, pub metadata: Vec, pub stored_at: NaiveDateTime, + pub sent_at: NaiveDateTime, pub delivery_confirmation_at: Option, pub read_confirmation_at: Option, pub direction: i32, @@ -145,6 +147,7 @@ impl TryFrom for Message { u8::try_from(o.direction).map_err(|_| ContactsServiceStorageError::ConversionError)?, ) .ok_or(ContactsServiceStorageError::ConversionError)?, + sent_at: o.sent_at.timestamp() as u64, stored_at: o.stored_at.timestamp() as u64, delivery_confirmation_at: Some(o.stored_at.timestamp() as u64), read_confirmation_at: Some(o.stored_at.timestamp() as u64), @@ -168,7 +171,10 @@ impl TryFrom for MessagesSqlInsert { message_id: o.message_id, body: o.body, metadata: metadata.into_bytes().to_vec(), - stored_at: NaiveDateTime::from_timestamp_opt(o.stored_at as i64, 0).unwrap(), + stored_at: NaiveDateTime::from_timestamp_opt(o.stored_at as i64, 0) + .ok_or(ContactsServiceStorageError::ConversionError)?, + sent_at: NaiveDateTime::from_timestamp_opt(o.sent_at as i64, 0) + .ok_or(ContactsServiceStorageError::ConversionError)?, direction: i32::from(o.direction.as_byte()), }) } diff --git a/base_layer/contacts/src/contacts_service/types/message.rs b/base_layer/contacts/src/contacts_service/types/message.rs index d259f8da78..7f26c9aece 100644 --- a/base_layer/contacts/src/contacts_service/types/message.rs +++ b/base_layer/contacts/src/contacts_service/types/message.rs @@ -38,6 +38,7 @@ pub struct Message { pub metadata: Vec, pub address: TariAddress, pub direction: Direction, + pub sent_at: u64, pub stored_at: u64, pub delivery_confirmation_at: Option, pub read_confirmation_at: Option, diff --git a/base_layer/contacts/src/schema.rs b/base_layer/contacts/src/schema.rs index 9a57007c44..4e62b4534e 100644 --- a/base_layer/contacts/src/schema.rs +++ b/base_layer/contacts/src/schema.rs @@ -18,6 +18,7 @@ diesel::table! { body -> Binary, metadata -> Binary, stored_at -> Timestamp, + sent_at -> Timestamp, delivery_confirmation_at -> Nullable, read_confirmation_at -> Nullable, direction -> Integer, diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index ec5050a0ca..482d5b37a6 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -11576,6 +11576,7 @@ mod test { address: bob_wallet_address.clone(), direction: Direction::Outbound, stored_at: u64::from(i), + sent_at: u64::from(i), delivery_confirmation_at: None, read_confirmation_at: None, message_id: vec![i], @@ -11592,6 +11593,7 @@ mod test { address: alice_wallet_address.clone(), direction: Direction::Outbound, stored_at: u64::from(i), + sent_at: u64::from(i), delivery_confirmation_at: None, read_confirmation_at: None, message_id: vec![i],