From ec73ada6b3850df29583958dc6ef4dafc3f080e0 Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 19 Jun 2025 16:30:12 +0200 Subject: [PATCH 1/9] feat(perms): tx-cache client wrapper that has a SharedToken For use with endpoints that need authentication. --- Cargo.toml | 3 +++ src/lib.rs | 2 +- src/perms/mod.rs | 2 ++ src/perms/tx_cache.rs | 63 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/perms/tx_cache.rs diff --git a/Cargo.toml b/Cargo.toml index 7ebad4d..3807f3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,9 @@ init4-from-env-derive = "0.1.0" # Signet signet-constants = { version = "0.2.0" } +signet-types = { version = "0.2.0" } +signet-tx-cache = { version = "0.2.0" } +eyre = "0.6.12" # Tracing tracing = "0.1.40" diff --git a/src/lib.rs b/src/lib.rs index c85a41b..e85403a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[cfg(feature = "perms")] +// #[cfg(feature = "perms")] /// Permissioning and authorization utilities for Signet builders. pub mod perms; diff --git a/src/perms/mod.rs b/src/perms/mod.rs index a54d5ac..640f93c 100644 --- a/src/perms/mod.rs +++ b/src/perms/mod.rs @@ -6,3 +6,5 @@ pub use config::{SlotAuthzConfig, SlotAuthzConfigEnvError}; pub(crate) mod oauth; pub use oauth::{Authenticator, OAuthConfig, SharedToken}; + +pub mod tx_cache; \ No newline at end of file diff --git a/src/perms/tx_cache.rs b/src/perms/tx_cache.rs new file mode 100644 index 0000000..222de3b --- /dev/null +++ b/src/perms/tx_cache.rs @@ -0,0 +1,63 @@ +use serde::de::DeserializeOwned; +use signet_tx_cache::{client::TxCache, types::{TxCacheBundle, TxCacheBundleResponse, TxCacheBundlesResponse}}; +use eyre::{bail, Error}; +use tracing::{instrument, warn}; +use crate::perms::oauth::{SharedToken}; + +const BUNDLES: &str = "bundles"; + +pub struct TxCacheClient { + /// The transaction cache client. + pub tx_cache: TxCache, + /// The shared token for authentication. + pub token: SharedToken, +} + +impl TxCacheClient { + /// Create a new `TxCacheClient` with the given transaction cache and shared token. + pub fn new(tx_cache: TxCache, token: SharedToken) -> Self { + Self { tx_cache, token } + } + + /// Get a reference to the transaction cache client. + pub fn tx_cache(&self) -> &TxCache { + &self.tx_cache + } + + /// Get a reference to the shared token. + pub fn token(&self) -> &SharedToken { + &self.token + } + + async fn get_inner_with_token(&self, join: &str) -> Result { + let url = self.tx_cache.url().join(join)?; + let Some(token) = self.token.read() else { + bail!("No token available for authentication"); + }; + + self.tx_cache.client().get(url) + .bearer_auth(token.access_token().secret()) + .send() + .inspect_err(|e| warn!(%e, "Failed to get object from transaction cache"))? + .json::() + .await + .map_err(Into::into) + } + + /// Get bundles from the cache. + #[instrument(skip_all)] + pub async fn get_bundles(&self) -> Result, Error> { + let response: TxCacheBundlesResponse = + self.get_inner_with_token::(BUNDLES).await?; + Ok(response.bundles) + } + + /// Get a bundle from the cache. + #[instrument(skip_all)] + pub async fn get_bundle(&self) -> Result { + let response: TxCacheBundleResponse = + self.get_inner_with_token::(BUNDLES).await?; + Ok(response.bundle) + } +} + From d3e21fca51f64dc58801365ee935604614f5ac24 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 19 Jun 2025 11:07:05 -0400 Subject: [PATCH 2/9] fix: optional deps and id arg in get_bundle --- Cargo.toml | 10 +++--- src/perms/mod.rs | 6 +++- src/perms/tx_cache.rs | 76 ++++++++++++++++++++++++++++++------------- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3807f3b..ca91f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,8 @@ init4-from-env-derive = "0.1.0" # Signet -signet-constants = { version = "0.2.0" } -signet-types = { version = "0.2.0" } -signet-tx-cache = { version = "0.2.0" } -eyre = "0.6.12" +signet-constants = { version = "0.3.0", optional = true } +signet-tx-cache = { version = "0.3.0", optional = true } # Tracing tracing = "0.1.40" @@ -51,6 +49,8 @@ thiserror = "2.0.11" alloy = { version = "1.0.5", optional = true, default-features = false, features = ["std", "signer-aws", "signer-local", "consensus", "network"] } serde = { version = "1", features = ["derive"] } async-trait = { version = "0.1.80", optional = true } +eyre = { version = "0.6.12", optional = true } + # AWS aws-config = { version = "1.1.7", optional = true } @@ -68,7 +68,7 @@ tokio = { version = "1.43.0", features = ["macros"] } [features] default = ["alloy"] alloy = ["dep:alloy", "dep:async-trait", "dep:aws-config", "dep:aws-sdk-kms"] -perms = ["dep:oauth2", "dep:tokio", "dep:reqwest"] +perms = ["dep:oauth2", "dep:tokio", "dep:reqwest", "dep:signet-tx-cache", "dep:eyre"] [[example]] name = "oauth" diff --git a/src/perms/mod.rs b/src/perms/mod.rs index 640f93c..fb9193b 100644 --- a/src/perms/mod.rs +++ b/src/perms/mod.rs @@ -7,4 +7,8 @@ pub use config::{SlotAuthzConfig, SlotAuthzConfigEnvError}; pub(crate) mod oauth; pub use oauth::{Authenticator, OAuthConfig, SharedToken}; -pub mod tx_cache; \ No newline at end of file +/// Contains [`BuilderTxCache`] client and related types for interacting with +/// the transaction cache. +/// +/// [`BuilderTxCache`]: tx_cache::BuilderTxCache +pub mod tx_cache; diff --git a/src/perms/tx_cache.rs b/src/perms/tx_cache.rs index 222de3b..51c415e 100644 --- a/src/perms/tx_cache.rs +++ b/src/perms/tx_cache.rs @@ -1,43 +1,68 @@ +use crate::perms::oauth::SharedToken; +use eyre::{bail, Result}; +use oauth2::TokenResponse; use serde::de::DeserializeOwned; -use signet_tx_cache::{client::TxCache, types::{TxCacheBundle, TxCacheBundleResponse, TxCacheBundlesResponse}}; -use eyre::{bail, Error}; +use signet_tx_cache::{ + client::TxCache, + types::{TxCacheBundle, TxCacheBundleResponse, TxCacheBundlesResponse}, +}; use tracing::{instrument, warn}; -use crate::perms::oauth::{SharedToken}; const BUNDLES: &str = "bundles"; -pub struct TxCacheClient { +/// A client for interacting with the transaction cache, a thin wrapper around +/// the [`TxCache`] and [`SharedToken`] that implements the necessary methods +/// to fetch bundles and bundle details. +#[derive(Debug, Clone)] +pub struct BuilderTxCache { /// The transaction cache client. - pub tx_cache: TxCache, + tx_cache: TxCache, /// The shared token for authentication. - pub token: SharedToken, + token: SharedToken, } -impl TxCacheClient { +impl std::ops::Deref for BuilderTxCache { + type Target = TxCache; + + fn deref(&self) -> &Self::Target { + &self.tx_cache + } +} + +impl std::ops::DerefMut for BuilderTxCache { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.tx_cache + } +} + +impl BuilderTxCache { /// Create a new `TxCacheClient` with the given transaction cache and shared token. - pub fn new(tx_cache: TxCache, token: SharedToken) -> Self { + pub const fn new(tx_cache: TxCache, token: SharedToken) -> Self { Self { tx_cache, token } } /// Get a reference to the transaction cache client. - pub fn tx_cache(&self) -> &TxCache { + pub const fn tx_cache(&self) -> &TxCache { &self.tx_cache } /// Get a reference to the shared token. - pub fn token(&self) -> &SharedToken { + pub const fn token(&self) -> &SharedToken { &self.token } - async fn get_inner_with_token(&self, join: &str) -> Result { + async fn get_inner_with_token(&self, join: &str) -> Result { let url = self.tx_cache.url().join(join)?; let Some(token) = self.token.read() else { bail!("No token available for authentication"); }; - - self.tx_cache.client().get(url) + + self.tx_cache + .client() + .get(url) .bearer_auth(token.access_token().secret()) .send() + .await .inspect_err(|e| warn!(%e, "Failed to get object from transaction cache"))? .json::() .await @@ -46,18 +71,23 @@ impl TxCacheClient { /// Get bundles from the cache. #[instrument(skip_all)] - pub async fn get_bundles(&self) -> Result, Error> { - let response: TxCacheBundlesResponse = - self.get_inner_with_token::(BUNDLES).await?; - Ok(response.bundles) + pub async fn get_bundles(&self) -> Result> { + self.get_inner_with_token::(BUNDLES) + .await + .map(|response| response.bundles) } - /// Get a bundle from the cache. + fn get_bundle_url_path(&self, bundle_id: &str) -> String { + format!("{}/{}", BUNDLES, bundle_id) + } + + /// Get a bundle from the cache by its UUID. For convenience, this method + /// takes a string reference, which is expected to be a valid UUID. #[instrument(skip_all)] - pub async fn get_bundle(&self) -> Result { - let response: TxCacheBundleResponse = - self.get_inner_with_token::(BUNDLES).await?; - Ok(response.bundle) + pub async fn get_bundle(&self, bundle_id: &str) -> Result { + let url = self.get_bundle_url_path(bundle_id); + self.get_inner_with_token::(&url) + .await + .map(|response| response.bundle) } } - From 6feed644294b69501c72ac6221c6b1e33adbe523 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 19 Jun 2025 11:10:16 -0400 Subject: [PATCH 3/9] feat: oauth example --- examples/tx_cache.rs | 24 ++++++++++++++++++++++++ src/perms/tx_cache.rs | 3 +++ tests/tx_cache.rs | 1 + 3 files changed, 28 insertions(+) create mode 100644 examples/tx_cache.rs create mode 100644 tests/tx_cache.rs diff --git a/examples/tx_cache.rs b/examples/tx_cache.rs new file mode 100644 index 0000000..7353dee --- /dev/null +++ b/examples/tx_cache.rs @@ -0,0 +1,24 @@ +use init4_bin_base::{ + perms::tx_cache::BuilderTxCache, perms::OAuthConfig, utils::from_env::FromEnv, +}; +use signet_tx_cache::client::TxCache; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + let cfg = OAuthConfig::from_env()?; + let authenticator = cfg.authenticator(); + let token = authenticator.token(); + + let _jh = authenticator.spawn(); + + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + dbg!(token.read()); + + let tx_cache = BuilderTxCache::new(TxCache::pecorino(), token); + + let bundles = tx_cache.get_bundles().await?; + + dbg!(bundles.len()); + + Ok(()) +} diff --git a/src/perms/tx_cache.rs b/src/perms/tx_cache.rs index 51c415e..d4d934f 100644 --- a/src/perms/tx_cache.rs +++ b/src/perms/tx_cache.rs @@ -41,6 +41,9 @@ impl BuilderTxCache { Self { tx_cache, token } } + pub fn new + + /// Get a reference to the transaction cache client. pub const fn tx_cache(&self) -> &TxCache { &self.tx_cache diff --git a/tests/tx_cache.rs b/tests/tx_cache.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/tx_cache.rs @@ -0,0 +1 @@ + From 16327427ebd15863195312b18c2bbaf8502fbd66 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 19 Jun 2025 11:12:42 -0400 Subject: [PATCH 4/9] fix: oops --- src/perms/tx_cache.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/perms/tx_cache.rs b/src/perms/tx_cache.rs index d4d934f..51c415e 100644 --- a/src/perms/tx_cache.rs +++ b/src/perms/tx_cache.rs @@ -41,9 +41,6 @@ impl BuilderTxCache { Self { tx_cache, token } } - pub fn new - - /// Get a reference to the transaction cache client. pub const fn tx_cache(&self) -> &TxCache { &self.tx_cache From 4670efc9cab923f93a9355c98b9c418cbc3c456e Mon Sep 17 00:00:00 2001 From: James Date: Thu, 19 Jun 2025 11:17:26 -0400 Subject: [PATCH 5/9] fix: re-add perms feature gate --- Cargo.toml | 9 ++++++--- src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca91f9d..303dec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,8 @@ repository = "https://github.com/init4tech/bin-base" [dependencies] init4-from-env-derive = "0.1.0" - # Signet -signet-constants = { version = "0.3.0", optional = true } +signet-constants = { version = "0.3.0" } signet-tx-cache = { version = "0.3.0", optional = true } # Tracing @@ -51,7 +50,6 @@ serde = { version = "1", features = ["derive"] } async-trait = { version = "0.1.80", optional = true } eyre = { version = "0.6.12", optional = true } - # AWS aws-config = { version = "1.1.7", optional = true } aws-sdk-kms = { version = "1.15.0", optional = true } @@ -74,3 +72,8 @@ perms = ["dep:oauth2", "dep:tokio", "dep:reqwest", "dep:signet-tx-cache", "dep:e name = "oauth" path = "examples/oauth.rs" required-features = ["perms"] + +[[example]] +name = "tx_cache" +path = "examples/tx_cache.rs" +required-features = ["perms"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e85403a..c85a41b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -// #[cfg(feature = "perms")] +#[cfg(feature = "perms")] /// Permissioning and authorization utilities for Signet builders. pub mod perms; From 2b34c6d49fb9d6da471604bcc4a378dd06a47d79 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 27 Jun 2025 08:50:23 -0400 Subject: [PATCH 6/9] fix: remove empty file --- tests/tx_cache.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/tx_cache.rs diff --git a/tests/tx_cache.rs b/tests/tx_cache.rs deleted file mode 100644 index 8b13789..0000000 --- a/tests/tx_cache.rs +++ /dev/null @@ -1 +0,0 @@ - From e2fe90e2213a7f8c5cf6480ab23463a33e324c14 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 27 Jun 2025 08:57:20 -0400 Subject: [PATCH 7/9] lint: clippy --- src/perms/tx_cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/perms/tx_cache.rs b/src/perms/tx_cache.rs index 51c415e..25113bc 100644 --- a/src/perms/tx_cache.rs +++ b/src/perms/tx_cache.rs @@ -78,7 +78,7 @@ impl BuilderTxCache { } fn get_bundle_url_path(&self, bundle_id: &str) -> String { - format!("{}/{}", BUNDLES, bundle_id) + format!("{BUNDLES}/{bundle_id}") } /// Get a bundle from the cache by its UUID. For convenience, this method From 35267e3e7aafd622bb98444872698ae3f1949588 Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 27 Jun 2025 15:15:26 +0200 Subject: [PATCH 8/9] chore: remove debug --- examples/tx_cache.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/tx_cache.rs b/examples/tx_cache.rs index 7353dee..d189f4c 100644 --- a/examples/tx_cache.rs +++ b/examples/tx_cache.rs @@ -12,13 +12,10 @@ async fn main() -> eyre::Result<()> { let _jh = authenticator.spawn(); tokio::time::sleep(std::time::Duration::from_secs(5)).await; - dbg!(token.read()); let tx_cache = BuilderTxCache::new(TxCache::pecorino(), token); let bundles = tx_cache.get_bundles().await?; - dbg!(bundles.len()); - Ok(()) } From b15bfb35b17abf15d26a9ed29355ce1f9cb1c94b Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 27 Jun 2025 15:30:34 +0200 Subject: [PATCH 9/9] chore: properly print bundles --- examples/tx_cache.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/tx_cache.rs b/examples/tx_cache.rs index d189f4c..b87ca03 100644 --- a/examples/tx_cache.rs +++ b/examples/tx_cache.rs @@ -17,5 +17,7 @@ async fn main() -> eyre::Result<()> { let bundles = tx_cache.get_bundles().await?; + println!("Bundles: {bundles:#?}"); + Ok(()) }