From 450006a6dc9143752c4dd43ab5a56926fa91f4a2 Mon Sep 17 00:00:00 2001 From: Ivan Reshetnikov Date: Mon, 20 Jan 2025 09:52:59 +0100 Subject: [PATCH] feat: tvf data in publish payload (#87) --- .github/workflows/ci.yaml | 2 +- relay_client/src/http.rs | 1 + relay_client/src/lib.rs | 25 ++++++++++++++++++- relay_client/src/websocket.rs | 1 + relay_rpc/Cargo.toml | 14 ++++++----- relay_rpc/src/auth/cacao.rs | 2 +- relay_rpc/src/auth/cacao/payload.rs | 2 +- relay_rpc/src/auth/cacao/signature/eip1271.rs | 2 +- relay_rpc/src/auth/cacao/signature/eip191.rs | 4 +-- relay_rpc/src/auth/cacao/signature/eip6492.rs | 2 +- relay_rpc/src/auth/cacao/signature/mod.rs | 2 +- relay_rpc/src/jwt.rs | 2 +- relay_rpc/src/rpc.rs | 25 +++++++++++++++++++ relay_rpc/src/rpc/tests.rs | 13 +++++++++- 14 files changed, 80 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0288989..7c35ba4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: rust: nightly - name: "Tests" cmd: nextest - args: run --workspace --all-features + args: run --workspace rust: stable - name: "Documentation Tests" cmd: test diff --git a/relay_client/src/http.rs b/relay_client/src/http.rs index 475292e..7ab3305 100644 --- a/relay_client/src/http.rs +++ b/relay_client/src/http.rs @@ -125,6 +125,7 @@ impl Client { ttl_secs, tag, prompt, + analytics: None, }) .await .map(|_| ()) diff --git a/relay_client/src/lib.rs b/relay_client/src/lib.rs index 7408975..7c708fb 100644 --- a/relay_client/src/lib.rs +++ b/relay_client/src/lib.rs @@ -48,6 +48,13 @@ pub struct ConnectionOptions { /// Optional origin of the request. Subject to allow-list validation. pub origin: Option, + /// Optional package name. Used instead of `origin` for allow-list + /// validation. + pub package_name: Option, + + /// Optional bundle ID. Used instead of `origin` for allow-list validation. + pub bundle_id: Option, + /// Optional user agent parameters. pub user_agent: Option, } @@ -60,6 +67,8 @@ impl ConnectionOptions { auth: Authorization::Query(auth), origin: None, user_agent: None, + package_name: None, + bundle_id: None, } } @@ -68,6 +77,16 @@ impl ConnectionOptions { self } + pub fn with_package_name(mut self, package_name: impl Into) -> Self { + self.package_name = Some(package_name.into()); + self + } + + pub fn with_bundle_id(mut self, bundle_id: impl Into) -> Self { + self.bundle_id = Some(bundle_id.into()); + self + } + pub fn with_origin(mut self, origin: impl Into>) -> Self { self.origin = origin.into(); self @@ -85,6 +104,8 @@ impl ConnectionOptions { project_id: &'a ProjectId, auth: Option<&'a SerializedAuthToken>, ua: Option<&'a UserAgent>, + package_name: Option<&'a str>, + bundle_id: Option<&'a str>, } let query = serde_qs::to_string(&QueryParams { @@ -95,6 +116,8 @@ impl ConnectionOptions { None }, ua: self.user_agent.as_ref(), + package_name: self.package_name.as_deref(), + bundle_id: self.bundle_id.as_deref(), }) .map_err(RequestBuildError::Query)?; @@ -157,7 +180,7 @@ impl MessageIdGenerator { pub fn next(&self) -> MessageId { let next = self.next.fetch_add(1, Ordering::Relaxed) as u64; let timestamp = chrono::Utc::now().timestamp_millis() as u64; - let id = timestamp << 8 | next; + let id = (timestamp << 8) | next; MessageId::new(id) } diff --git a/relay_client/src/websocket.rs b/relay_client/src/websocket.rs index 29bb2f5..1eee8aa 100644 --- a/relay_client/src/websocket.rs +++ b/relay_client/src/websocket.rs @@ -168,6 +168,7 @@ impl Client { ttl_secs: ttl.as_secs() as u32, tag, prompt, + analytics: None, }); self.request(request); diff --git a/relay_rpc/Cargo.toml b/relay_rpc/Cargo.toml index c3206d1..ebabbb4 100644 --- a/relay_rpc/Cargo.toml +++ b/relay_rpc/Cargo.toml @@ -5,11 +5,8 @@ edition = "2021" license = "Apache-2.0" [features] -cacao = [ - "dep:k256", - "dep:sha3", - "dep:alloy" -] +cacao = ["dep:k256", "dep:sha3", "dep:alloy"] +cacao-tests = [] [dependencies] bs58 = "0.4" @@ -37,7 +34,12 @@ k256 = { version = "0.13", optional = true } sha3 = { version = "0.10", optional = true } sha2 = { version = "0.10.6" } url = "2" -alloy = { version = "0.3.6", optional = true, features = ["json-rpc", "provider-http", "contract", "rpc-types-eth"] } +alloy = { version = "0.3.6", optional = true, features = [ + "json-rpc", + "provider-http", + "contract", + "rpc-types-eth", +] } strum = { version = "0.26", features = ["strum_macros", "derive"] } [dev-dependencies] diff --git a/relay_rpc/src/auth/cacao.rs b/relay_rpc/src/auth/cacao.rs index 470a74a..609334e 100644 --- a/relay_rpc/src/auth/cacao.rs +++ b/relay_rpc/src/auth/cacao.rs @@ -169,5 +169,5 @@ impl Cacao { } } -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod tests; diff --git a/relay_rpc/src/auth/cacao/payload.rs b/relay_rpc/src/auth/cacao/payload.rs index 71cf252..b71fdc3 100644 --- a/relay_rpc/src/auth/cacao/payload.rs +++ b/relay_rpc/src/auth/cacao/payload.rs @@ -117,7 +117,7 @@ impl Payload { } } -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod tests { use super::*; diff --git a/relay_rpc/src/auth/cacao/signature/eip1271.rs b/relay_rpc/src/auth/cacao/signature/eip1271.rs index 4e5f1c8..19ce3f3 100644 --- a/relay_rpc/src/auth/cacao/signature/eip1271.rs +++ b/relay_rpc/src/auth/cacao/signature/eip1271.rs @@ -66,7 +66,7 @@ pub async fn verify_eip1271( } } -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod test { use { super::*, diff --git a/relay_rpc/src/auth/cacao/signature/eip191.rs b/relay_rpc/src/auth/cacao/signature/eip191.rs index ba30394..f7ddc2c 100644 --- a/relay_rpc/src/auth/cacao/signature/eip191.rs +++ b/relay_rpc/src/auth/cacao/signature/eip191.rs @@ -10,7 +10,7 @@ pub const EIP191: &str = "eip191"; pub fn eip191_bytes(message: &str) -> Vec { format!( "\u{0019}Ethereum Signed Message:\n{}{}", - message.as_bytes().len(), + message.len(), message ) .into() @@ -51,7 +51,7 @@ pub fn verify_eip191( } } -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod tests { use { crate::auth::cacao::signature::{ diff --git a/relay_rpc/src/auth/cacao/signature/eip6492.rs b/relay_rpc/src/auth/cacao/signature/eip6492.rs index 3a08b67..04c7cd5 100644 --- a/relay_rpc/src/auth/cacao/signature/eip6492.rs +++ b/relay_rpc/src/auth/cacao/signature/eip6492.rs @@ -69,7 +69,7 @@ pub async fn verify_eip6492( } } -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod test { use { super::*, diff --git a/relay_rpc/src/auth/cacao/signature/mod.rs b/relay_rpc/src/auth/cacao/signature/mod.rs index 2732460..f4f9d49 100644 --- a/relay_rpc/src/auth/cacao/signature/mod.rs +++ b/relay_rpc/src/auth/cacao/signature/mod.rs @@ -17,7 +17,7 @@ pub mod eip191; pub mod eip6492; pub mod get_rpc_url; -#[cfg(test)] +#[cfg(all(test, feature = "cacao-tests"))] mod test_helpers; #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)] diff --git a/relay_rpc/src/jwt.rs b/relay_rpc/src/jwt.rs index 120a99d..d37599f 100644 --- a/relay_rpc/src/jwt.rs +++ b/relay_rpc/src/jwt.rs @@ -67,7 +67,7 @@ impl Default for JwtHeader<'_> { } } -impl<'a> JwtHeader<'a> { +impl JwtHeader<'_> { pub fn is_valid(&self) -> bool { self.typ == JWT_HEADER_TYP && self.alg == JWT_HEADER_ALG } diff --git a/relay_rpc/src/rpc.rs b/relay_rpc/src/rpc.rs index 751eaec..bd99469 100644 --- a/relay_rpc/src/rpc.rs +++ b/relay_rpc/src/rpc.rs @@ -88,6 +88,12 @@ impl Payload { Self::Response(response) => response.validate(), } } + + pub fn strip_analytics(&mut self) { + if let Self::Request(req) = self { + req.strip_analytics(); + } + } } impl From for Payload @@ -520,6 +526,16 @@ impl ServiceRequest for BatchReceiveMessages { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AnalyticsData { + pub correlation_id: Option>, + pub chain_id: Option>, + pub rpc_methods: Option>>, + pub tx_hashes: Option>>, + pub contract_addresses: Option>>, +} + /// Data structure representing publish request params. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Publish { @@ -545,6 +561,9 @@ pub struct Publish { /// webhook to a client through a push server. #[serde(default, skip_serializing_if = "is_default")] pub prompt: bool, + + #[serde(default, flatten, skip_serializing_if = "is_default")] + pub analytics: Option, } impl Publish { @@ -860,4 +879,10 @@ impl Request { Params::Subscription(params) => params.validate(), } } + + pub fn strip_analytics(&mut self) { + if let Params::Publish(params) = &mut self.params { + params.analytics = None; + } + } } diff --git a/relay_rpc/src/rpc/tests.rs b/relay_rpc/src/rpc/tests.rs index 80da24d..543e5ee 100644 --- a/relay_rpc/src/rpc/tests.rs +++ b/relay_rpc/src/rpc/tests.rs @@ -11,6 +11,13 @@ fn request() { ttl_secs: 12, tag: 0, prompt: false, + analytics: Some(AnalyticsData { + correlation_id: Some("correlation_id".into()), + chain_id: Some("chain_id".into()), + rpc_methods: Some(vec!["rpc_method".into()]), + tx_hashes: Some(vec!["tx_hash".into()]), + contract_addresses: Some(vec!["contract_address".into()]), + }), }), )); @@ -18,7 +25,7 @@ fn request() { assert_eq!( &serialized, - r#"{"id":1,"jsonrpc":"2.0","method":"irn_publish","params":{"topic":"topic","message":"payload","attestation":"attestation_payload","ttl":12,"tag":0}}"# + r#"{"id":1,"jsonrpc":"2.0","method":"irn_publish","params":{"topic":"topic","message":"payload","attestation":"attestation_payload","ttl":12,"tag":0,"correlationId":"correlation_id","chainId":"chain_id","rpcMethods":["rpc_method"],"txHashes":["tx_hash"],"contractAddresses":["contract_address"]}}"# ); let deserialized: Payload = serde_json::from_str(&serialized).unwrap(); @@ -294,6 +301,7 @@ fn validation() { ttl_secs: 0, tag: 0, prompt: false, + analytics: None, }), }; assert_eq!(request.validate(), Err(PayloadError::InvalidRequestId)); @@ -309,6 +317,7 @@ fn validation() { ttl_secs: 0, tag: 0, prompt: false, + analytics: None, }), }; assert_eq!(request.validate(), Err(PayloadError::InvalidJsonRpcVersion)); @@ -324,6 +333,7 @@ fn validation() { ttl_secs: 0, tag: 0, prompt: false, + analytics: None, }), }; assert_eq!(request.validate(), Ok(())); @@ -339,6 +349,7 @@ fn validation() { ttl_secs: 0, tag: 0, prompt: false, + analytics: None, }), }; assert_eq!(request.validate(), Err(PayloadError::InvalidTopic));