diff --git a/backend/canisters/event_relay/CHANGELOG.md b/backend/canisters/event_relay/CHANGELOG.md index b3fa7c2673..01e1b71ad8 100644 --- a/backend/canisters/event_relay/CHANGELOG.md +++ b/backend/canisters/event_relay/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [unreleased] +### Changed + +- Switch to using `push_many` ([#5364](https://github.com/open-chat-labs/open-chat/pull/5364)) + ## [[2.0.1052](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1052-event_relay)] - 2024-02-09 ### Added diff --git a/backend/canisters/event_relay/impl/src/lib.rs b/backend/canisters/event_relay/impl/src/lib.rs index 87d6cf641c..acc58faccb 100644 --- a/backend/canisters/event_relay/impl/src/lib.rs +++ b/backend/canisters/event_relay/impl/src/lib.rs @@ -1,10 +1,10 @@ +use crate::model::salt::Salt; use candid::Principal; use canister_state_macros::canister_state; use event_sink_client::{EventSinkClient, EventSinkClientBuilder, EventSinkClientInfo}; use event_sink_client_cdk_runtime::CdkRuntime; use event_sink_utils::EventDeduper; use serde::{Deserialize, Serialize}; -use sha256::sha256_string; use std::cell::RefCell; use std::collections::HashSet; use std::time::Duration; @@ -65,7 +65,7 @@ struct Data { pub events_sink_client: EventSinkClient, pub event_deduper: EventDeduper, pub cycles_dispenser_canister_id: CanisterId, - pub salt: [u8; 32], + pub salt: Salt, pub rng_seed: [u8; 32], pub test_mode: bool, } @@ -84,24 +84,11 @@ impl Data { .build(), event_deduper: EventDeduper::default(), cycles_dispenser_canister_id, - salt: [0; 32], + salt: Salt::default(), rng_seed: [0; 32], test_mode, } } - - pub fn obfuscate_user(&self, user: String) -> String { - // We only want to obfuscate userId principals, so if the string is not a principal we return it as is - if Principal::from_text(&user).is_err() { - return user; - } - - // Generates a 32 character string from the input value + the salt - let mut bytes = Vec::new(); - bytes.extend_from_slice(user.as_bytes()); - bytes.extend_from_slice(&self.salt); - sha256_string(&bytes).split_off(32) - } } #[derive(Serialize, Debug)] diff --git a/backend/canisters/event_relay/impl/src/lifecycle/mod.rs b/backend/canisters/event_relay/impl/src/lifecycle/mod.rs index d8eca93275..2807c53728 100644 --- a/backend/canisters/event_relay/impl/src/lifecycle/mod.rs +++ b/backend/canisters/event_relay/impl/src/lifecycle/mod.rs @@ -35,8 +35,8 @@ fn reseed_rng() { state.env = Box::new(CanisterEnv::new(seed)); // We only want to set the salt once - if state.data.salt == [0; 32] { - state.data.salt = seed; + if !state.data.salt.is_initialized() { + state.data.salt.set(seed); } }); trace!("Successfully reseeded rng"); diff --git a/backend/canisters/event_relay/impl/src/model/mod.rs b/backend/canisters/event_relay/impl/src/model/mod.rs index 8b13789179..80e00c84a0 100644 --- a/backend/canisters/event_relay/impl/src/model/mod.rs +++ b/backend/canisters/event_relay/impl/src/model/mod.rs @@ -1 +1 @@ - +pub mod salt; diff --git a/backend/canisters/event_relay/impl/src/model/salt.rs b/backend/canisters/event_relay/impl/src/model/salt.rs new file mode 100644 index 0000000000..e05331f950 --- /dev/null +++ b/backend/canisters/event_relay/impl/src/model/salt.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Default)] +#[serde(from = "[u8; 32]")] +pub struct Salt { + salt: [u8; 32], +} + +impl Salt { + pub fn get(&self) -> [u8; 32] { + assert!(self.is_initialized()); + self.salt + } + + pub fn set(&mut self, salt: [u8; 32]) { + assert!(!self.is_initialized()); + self.salt = salt; + } + + pub fn is_initialized(&self) -> bool { + self.salt != [0; 32] + } +} + +impl From<[u8; 32]> for Salt { + fn from(value: [u8; 32]) -> Self { + Salt { salt: value } + } +} diff --git a/backend/canisters/event_relay/impl/src/updates/push_events.rs b/backend/canisters/event_relay/impl/src/updates/push_events.rs index a70cc2f7db..e3c1a02520 100644 --- a/backend/canisters/event_relay/impl/src/updates/push_events.rs +++ b/backend/canisters/event_relay/impl/src/updates/push_events.rs @@ -1,9 +1,11 @@ use crate::guards::caller_can_push_events; use crate::{mutate_state, RuntimeState}; +use candid::Principal; use canister_tracing_macros::trace; use event_relay_canister::push_events::*; use event_sink_canister::Event; use ic_cdk_macros::update; +use sha256::sha256_string; #[update(guard = "caller_can_push_events")] #[trace] @@ -14,17 +16,34 @@ fn push_events(args: Args) { fn push_events_impl(args: Args, state: &mut RuntimeState) { let now = state.env.now(); - for event in args.events { - if state.data.event_deduper.try_push(event.idempotency_key, now) { - let user = event.user.map(|u| state.data.obfuscate_user(u)); + let salt = state.data.salt.get(); - state.data.events_sink_client.push(Event { - name: event.name, - timestamp: event.timestamp, - user, - source: event.source, - payload: event.payload, - }); - } + state.data.events_sink_client.push_many( + args.events + .into_iter() + .filter(|e| state.data.event_deduper.try_push(e.idempotency_key, now)) + .map(|e| { + let user = e.user.map(|u| obfuscate_user(u, salt)); + Event { + name: e.name, + timestamp: e.timestamp, + user, + source: e.source, + payload: e.payload, + } + }), + ); +} + +pub fn obfuscate_user(user: String, salt: [u8; 32]) -> String { + // We only want to obfuscate userId principals, so if the string is not a principal we return it as is + if Principal::from_text(&user).is_err() { + return user; } + + // Generates a 32 character string from the input value + the salt + let mut bytes = Vec::new(); + bytes.extend_from_slice(user.as_bytes()); + bytes.extend_from_slice(&salt); + sha256_string(&bytes).split_off(32) }