From e9c078591bb0f13e9aa1b9f4df73f1622e3fbcff Mon Sep 17 00:00:00 2001 From: Alex <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:14:33 -0400 Subject: [PATCH 1/3] feat(gadget-sdk): improve `MultiJobRunner` builder (#382) --- blueprints/incredible-squaring/src/main.rs | 7 +- blueprints/periodic-web-poller/src/main.rs | 6 +- sdk/src/error.rs | 6 + sdk/src/job_runner.rs | 416 ++++++++++++++------- 4 files changed, 295 insertions(+), 140 deletions(-) diff --git a/blueprints/incredible-squaring/src/main.rs b/blueprints/incredible-squaring/src/main.rs index d8c9a13ee..3d2aa096a 100644 --- a/blueprints/incredible-squaring/src/main.rs +++ b/blueprints/incredible-squaring/src/main.rs @@ -18,12 +18,7 @@ async fn main() { }; info!("~~~ Executing the incredible squaring blueprint ~~~"); - MultiJobRunner::new(&env) - .with_job() - .with_default_price_targets() - .finish(x_square) - .run() - .await?; + MultiJobRunner::new(env).job(x_square).run().await?; info!("Exiting..."); Ok(()) diff --git a/blueprints/periodic-web-poller/src/main.rs b/blueprints/periodic-web-poller/src/main.rs index dcdaf3d38..8a9d83468 100644 --- a/blueprints/periodic-web-poller/src/main.rs +++ b/blueprints/periodic-web-poller/src/main.rs @@ -10,11 +10,7 @@ async fn main() { }; info!("~~~ Executing the periodic web poller ~~~"); - MultiJobRunner::new(None) - .with_job() - .finish(web_poller) - .run() - .await?; + MultiJobRunner::new(None).job(web_poller).run().await?; info!("Exiting..."); Ok(()) diff --git a/sdk/src/error.rs b/sdk/src/error.rs index a0eb3591f..d613e73c3 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -20,6 +20,12 @@ pub enum Error { #[error("Keystore error: {0}")] Keystore(#[from] crate::keystore::error::Error), + #[error("Config error: {0}")] + Config(#[from] crate::config::Error), + + #[error("Job runner error: {0}")] + Runner(#[from] crate::job_runner::Error), + #[error("Missing network ID")] MissingNetworkId, diff --git a/sdk/src/job_runner.rs b/sdk/src/job_runner.rs index 4f15738f4..4e1e078a1 100644 --- a/sdk/src/job_runner.rs +++ b/sdk/src/job_runner.rs @@ -7,38 +7,105 @@ use std::future::Future; use std::pin::Pin; use tangle_subxt::tangle_testnet_runtime::api; use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services; -use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::PriceTargets; +use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::PriceTargets as TangleSubxtPriceTargets; -pub struct MultiJobRunner<'a> { - pub(crate) enqueued_job_runners: EnqueuedJobRunners<'a>, - pub(crate) env: Option>, +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("No jobs registered. Make sure to add a job with `MultiJobRunner::add_job`")] + NoJobs, + #[error("Job already initialized")] + AlreadyInitialized, + + #[error(transparent)] + Recv(#[from] tokio::sync::oneshot::error::RecvError), } -pub type EnqueuedJobRunners<'a> = Vec< - Pin< - Box< - dyn ScopedFuture< - 'a, - Output = Option>>, - >, - >, - >, ->; +/// Wrapper for `tangle_subxt`'s [`PriceTargets`] +/// +/// This provides a [`Default`] impl for a zeroed-out [`PriceTargets`]. +/// +/// [`PriceTargets`]: tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::PriceTargets +pub struct PriceTargets(TangleSubxtPriceTargets); -pub trait ScopedFuture<'a>: Future + 'a {} -impl<'a, T: Future + 'a> ScopedFuture<'a> for T {} +impl From for PriceTargets { + fn from(t: TangleSubxtPriceTargets) -> Self { + PriceTargets(t) + } +} -pub struct JobBuilder<'b, K: 'b> { - pub(crate) register_call: Option>, - runner: MultiJobRunner<'b>, - _pd: std::marker::PhantomData<&'b K>, +impl Default for PriceTargets { + fn default() -> Self { + Self(TangleSubxtPriceTargets { + cpu: 0, + mem: 0, + storage_hdd: 0, + storage_ssd: 0, + storage_nvme: 0, + }) + } } +pub trait ScopedFuture<'a>: Future + 'a {} +impl<'a, T: Future + 'a> ScopedFuture<'a> for T {} + pub(crate) type RegisterCall<'a> = Pin>>>; -impl<'a, K: InitializableEventHandler + Send + 'a> JobBuilder<'a, K> { - pub fn with_registration< +/// A builder for blueprint jobs +/// +/// Unless custom registration functions are needed, this can be avoided. See [`MultiJobRunner::job`]. +pub struct JobBuilder<'a, T> +where + T: InitializableEventHandler + Send + 'a, +{ + event_handler: T, + price_targets: Option, + registration: Option>, +} + +impl<'a, T> From for JobBuilder<'a, T> +where + T: InitializableEventHandler + Send + 'a, +{ + fn from(value: T) -> Self { + Self { + event_handler: value, + price_targets: None, + registration: None, + } + } +} + +impl<'a, T, P> From<(T, P)> for JobBuilder<'a, T> +where + T: InitializableEventHandler + Send + 'a, + T: markers::IsTangle, + P: Into, +{ + fn from(value: (T, P)) -> Self { + Self { + event_handler: value.0, + price_targets: Some(value.1.into()), + registration: None, + } + } +} + +impl<'a, T> JobBuilder<'a, T> +where + T: InitializableEventHandler + Send + 'a, +{ + /// Create a new `JobBuilder` + pub fn new(event_handler: T) -> Self { + Self { + event_handler, + price_targets: None, + registration: None, + } + } + + /// Set the registration function + pub fn registration< Fut: ScopedFuture<'a, Output = Result<(), crate::Error>> + 'a, Input: 'a, >( @@ -47,113 +114,219 @@ impl<'a, K: InitializableEventHandler + Send + 'a> JobBuilder<'a, K> { register_call: fn(Input) -> Fut, ) -> Self { let future = register_call(context); - self.register_call = Some(Box::pin(future)); + self.registration = Some(Box::pin(future)); self } - pub fn with_price_targets(self, price_targets: PriceTargets) -> Self - where - K: markers::IsTangle, - { - let env = self - .runner - .env - .clone() - .expect("Must have an env when using tangle"); - self.with_registration((env, price_targets), tangle_registration) - } - - pub fn with_default_price_targets(self) -> Self - where - K: markers::IsTangle, - { - self.with_price_targets(PriceTargets { - cpu: 0, - mem: 0, - storage_hdd: 0, - storage_ssd: 0, - storage_nvme: 0, - }) + /// Set the price targets + pub fn price_targets(mut self, targets: PriceTargets) -> Self { + self.price_targets = Some(targets); + self } +} - pub fn finish(mut self, job_runner: K) -> MultiJobRunner<'a> { - let registration = self.register_call.take(); - let skip_registration = self - .runner - .env - .as_ref() - .map(|r| r.test_mode) - .unwrap_or(true); - - let task = Box::pin(async move { - if let Some(registration) = registration { - // Skip registration if in test mode - if !skip_registration { - if let Err(err) = registration.await { - crate::error!("Failed to register job: {err:?}"); - return None; - } - } - } - - job_runner.init_event_handler().await - }); +pub type EnqueuedJobRunners<'a> = Vec>; - self.runner.enqueued_job_runners.push(task); +pub type JobRunner<'a> = Pin< + Box< + dyn ScopedFuture< + 'a, + Output = Option>>, + >, + >, +>; - self.runner - } +pub struct MultiJobRunner<'a> { + pub(crate) enqueued_job_runners: EnqueuedJobRunners<'a>, + pub(crate) env: Option>, } impl<'a> MultiJobRunner<'a> { - pub fn new>>>(env: T) -> Self { + /// Create a new `MultiJobRunner` + pub fn new>>>(env: T) -> Self { Self { enqueued_job_runners: Vec::new(), - env: env.into().cloned(), + env: env.into(), } } /// Add a job to the job runner - /// ```no_run - /// #[gadget_sdk::main(env)] - /// async fn main() { - /// let x_square = blueprint::XsquareEventHandler { - /// service_id: env.service_id.unwrap(), - /// client: client.clone(), - /// signer, - /// }; /// - /// let x_square2 = blueprint::XsquareEventHandler { - /// service_id: env.service_id.unwrap(), - /// client: client.clone(), - /// signer, - /// }; + /// # Examples + /// + /// ```rust,no_run + /// use gadget_sdk::job_runner::{JobBuilder, MultiJobRunner, PriceTargets}; + /// use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services; + /// + /// # #[gadget_sdk::main(env)] + /// # async fn main() { + /// # mod blueprint { + /// # #[gadget_sdk::job( + /// # id = 0, + /// # params(x), + /// # result(_), + /// # event_listener( + /// # listener = TangleEventListener, + /// # event = JobCalled, + /// # ), + /// # )] + /// # pub fn xsquare(x: u64) -> Result { + /// # Ok(x.saturating_pow(2u32)) + /// # } + /// # #[gadget_sdk::job( + /// # id = 1, + /// # params(x), + /// # result(_), + /// # event_listener( + /// # listener = TangleEventListener, + /// # event = JobCalled, + /// # ), + /// # )] + /// # pub fn xsquare2(x: u64) -> Result { + /// # Ok(x.saturating_pow(2u32)) + /// # } + /// # #[gadget_sdk::job( + /// # id = 2, + /// # params(x), + /// # result(_), + /// # event_listener( + /// # listener = TangleEventListener, + /// # event = JobCalled, + /// # ), + /// # )] + /// # pub fn xsquare3(x: u64) -> Result { + /// # Ok(x.saturating_pow(2u32)) + /// # } + /// # } + /// let client = env.client().await?; + /// let signer = env.first_sr25519_signer()?; + /// let x_square = blueprint::XsquareEventHandler { + /// service_id: env.service_id.unwrap(), + /// client: client.clone(), + /// signer: signer.clone(), + /// }; /// - /// MultiJobRunner::new(&env) - /// .with_job() - /// .with_default_price_targets() - /// .finish(x_square) - /// .with_job() - /// .with_default_price_targets() - /// .finish(x_square2) - /// .run() - /// .await?; + /// let x_square2 = blueprint::XsquareEventHandler { + /// service_id: env.service_id.unwrap(), + /// client: client.clone(), + /// signer: signer.clone(), + /// }; + /// + /// let custom_price_targets = services::PriceTargets { + /// cpu: 5, + /// mem: 10, + /// storage_hdd: 15, + /// storage_ssd: 20, + /// storage_nvme: 25, + /// }; + /// + /// let x_square3 = blueprint::XsquareEventHandler { + /// service_id: env.service_id.unwrap(), + /// client: client.clone(), + /// signer: signer.clone(), + /// }; + /// + /// MultiJobRunner::new(env) + /// .job(x_square) + /// // With custom price targets + /// .job((x_square2, custom_price_targets)) + /// // With custom registration + /// .job(JobBuilder::new(x_square3).registration(1, my_registration)) + /// .run() + /// .await?; + /// # Ok(()) } + /// + /// async fn my_registration(foo: u32) -> Result<(), gadget_sdk::Error> { + /// // ... + /// Ok(()) /// } /// ``` - pub fn with_job(self) -> JobBuilder<'a, K> { - JobBuilder { - register_call: None, - runner: self, - _pd: Default::default(), + pub fn job(&mut self, job: J) -> &mut Self + where + J: Into>, + T: InitializableEventHandler + Send + 'a, + { + let JobBuilder { + event_handler, + price_targets, + mut registration, + } = job.into(); + + // Skip registration if in test mode + let skip_registration = self.env.as_ref().map(|r| r.test_mode).unwrap_or(true); + if skip_registration { + registration = None; + } + + if !skip_registration && registration.is_none() { + let env = self + .env + .clone() + .expect("Must have an env when using tangle"); + + let price_targets = price_targets.unwrap_or(PriceTargets::default()); + + let future = tangle_registration((env, price_targets)); + registration = Some(Box::pin(future)); } + + let task = Box::pin(async move { + if let Some(registration) = registration { + if let Err(err) = registration.await { + crate::error!("Failed to register job: {err:?}"); + return None; + } + } + + event_handler.init_event_handler().await + }); + + self.enqueued_job_runners.push(task); + self } + /// Start the job runner + /// + /// # Errors + /// + /// * No jobs are registered + /// * A job exits prematurely + /// + /// # Examples + /// + /// ```rust,no_run + /// use gadget_sdk::job_runner::MultiJobRunner; + /// + /// # #[gadget_sdk::main(env)] + /// # async fn main() { + /// # mod blueprint { + /// # #[gadget_sdk::job( + /// # id = 0, + /// # params(x), + /// # result(_), + /// # event_listener( + /// # listener = TangleEventListener, + /// # event = JobCalled, + /// # ), + /// # )] + /// # pub fn xsquare(x: u64) -> Result { + /// # Ok(x.saturating_pow(2u32)) + /// # } + /// # } + /// let client = env.client().await?; + /// let signer = env.first_sr25519_signer()?; + /// let x_square = blueprint::XsquareEventHandler { + /// service_id: env.service_id.unwrap(), + /// client: client.clone(), + /// signer, + /// }; + /// + /// MultiJobRunner::new(env).job(x_square).run().await?; + /// # Ok(()) } + /// ``` pub async fn run(&mut self) -> Result<(), crate::Error> { if self.enqueued_job_runners.is_empty() { - return Err(crate::Error::Other( - "No jobs registered. Make sure to add a job with `MultiJobRunner::add_job` " - .to_string(), - )); + return Err(Error::NoJobs.into()); } let mut futures = Vec::new(); @@ -168,23 +341,15 @@ impl<'a> MultiJobRunner<'a> { // is None, return an error stating that the job already initialized let mut ordered_futures = Vec::new(); for receiver in receivers { - let receiver = receiver - .ok_or_else(|| crate::Error::Other("Job already initialized".to_string()))?; + let receiver = receiver.ok_or(Error::AlreadyInitialized)?; ordered_futures.push(receiver); } - let res = futures::future::select_all(ordered_futures).await; - let job_n = res.1; - let err = res - .0 - .map_err(|err| crate::Error::Other(err.to_string())) - .map_err(|_err| { - crate::Error::Other(format!("Job {job_n} exited prematurely (channel dropped)")) - })?; - - Err(crate::Error::Other(format!( - "Job {job_n} exited prematurely: {err:?}" - ))) + let (res, job_num, _) = futures::future::select_all(ordered_futures).await; + let err = res.map_err(Error::from)?; + + crate::error!("Job {job_num} exited prematurely"); + err } } @@ -192,22 +357,15 @@ async fn tangle_registration( this: (GadgetConfiguration, PriceTargets), ) -> Result<(), crate::Error> { let (this, price_targets) = this; - let client = this - .client() - .await - .map_err(|err| crate::Error::Other(err.to_string()))?; - let signer = this - .first_sr25519_signer() - .map_err(|err| crate::Error::Other(err.to_string()))?; - let ecdsa_pair = this - .first_ecdsa_signer() - .map_err(|err| crate::Error::Other(err.to_string()))?; + let client = this.client().await?; + let signer = this.first_sr25519_signer()?; + let ecdsa_pair = this.first_ecdsa_signer()?; let xt = api::tx().services().register( this.blueprint_id, services::OperatorPreferences { key: ecdsa_pair.signer().public().0, - price_targets, + price_targets: price_targets.0, }, Default::default(), ); From aa1393002e77f5008432d4dce00d97fd4e951c2b Mon Sep 17 00:00:00 2001 From: Thomas Braun <38082993+tbraun96@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:23:54 -0400 Subject: [PATCH 2/3] Event Flows for Tangle (#363) * wip: Working on tangle compat with new event flows * pre-processor w/ macro-injected encoding added * Add integration test, ensure tangle integration test works, pre/post processing works * Remove tangle-events * Cleanup * update CI * Adjust forge usage * chore: remove docs dir out of date --------- Co-authored-by: Drew Stone --- .github/workflows/ci.yml | 12 +- Cargo.lock | 533 ++++++++-------- blueprint-test-utils/src/lib.rs | 2 +- .../contracts/lib/forge-std | 2 +- .../incredible-squaring-eigenlayer/src/lib.rs | 5 +- .../src/tests.rs | 10 +- .../contracts/lib/forge-std | 2 +- .../contracts/lib/tnt-core | 2 +- blueprints/incredible-squaring/src/lib.rs | 14 +- blueprints/incredible-squaring/src/main.rs | 1 + blueprints/periodic-web-poller/src/lib.rs | 1 - .../contracts/lib/forge-std | 2 +- cli/src/deploy.rs | 14 +- docs/event_listeners.md | 256 -------- macros/blueprint-proc-macro-core/src/lib.rs | 24 + .../src/event_listener/evm.rs | 25 +- .../src/event_listener/tangle.rs | 40 +- macros/blueprint-proc-macro/src/job.rs | 578 ++++++++++-------- macros/blueprint-proc-macro/src/report.rs | 232 +++---- macros/blueprint-proc-macro/src/shared.rs | 15 + macros/blueprint-proc-macro/src/tangle/mod.rs | 197 ++++-- macros/playground/Cargo.toml | 7 +- macros/playground/src/lib.rs | 218 ++++++- sdk/Cargo.toml | 1 + sdk/src/error.rs | 2 + sdk/src/event_listener/executor.rs | 13 +- sdk/src/event_listener/mod.rs | 2 +- sdk/src/event_listener/tangle/jobs.rs | 124 ++++ sdk/src/event_listener/tangle/mod.rs | 182 ++++++ sdk/src/event_listener/tangle_events.rs | 204 ------- sdk/src/lib.rs | 1 + 31 files changed, 1436 insertions(+), 1285 deletions(-) delete mode 100644 docs/event_listeners.md create mode 100644 sdk/src/event_listener/tangle/jobs.rs create mode 100644 sdk/src/event_listener/tangle/mod.rs delete mode 100644 sdk/src/event_listener/tangle_events.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c8591ed1..f1e2f47aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,12 +37,6 @@ jobs: timeout-minutes: 120 name: cargo clippy runs-on: macos-latest - strategy: - matrix: - package: [ - gadget-sdk, - blueprint-manager - ] steps: - name: checkout code uses: actions/checkout@v2 @@ -71,8 +65,11 @@ jobs: - name: install protobuf run: brew install protobuf + - name: Forge build + run: forge update && cd blueprints/incredible-squaring-eigenlayer && forge build --root ./contracts + - name: Run Clippy - run: cargo clippy --package ${{ matrix.package }} --tests -- -D warnings + run: cargo clippy --tests -- -D warnings testing: timeout-minutes: 90 @@ -85,6 +82,7 @@ jobs: blueprint-manager, gadget-context-derive, gadget-blueprint-proc-macro, + gadget-blueprint-proc-macro-playground, ] steps: - name: checkout code diff --git a/Cargo.lock b/Cargo.lock index b12001c9e..41c5dc6b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,11 +128,10 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.40" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4932d790c723181807738cf1ac68198ab581cd699545b155601332541ee47bd" +checksum = "94c225801d42099570d0674701dddd4142f0ef715282aeb5985042e2ec962df7" dependencies = [ - "alloy-primitives 0.8.9", "num_enum", "strum", ] @@ -184,7 +183,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -411,7 +410,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -570,7 +569,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -584,7 +583,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -601,7 +600,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "syn-solidity 0.7.7", "tiny-keccak", ] @@ -619,7 +618,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "syn-solidity 0.8.9", "tiny-keccak", ] @@ -637,7 +636,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.82", + "syn 2.0.79", "syn-solidity 0.7.7", ] @@ -652,7 +651,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "syn-solidity 0.8.9", ] @@ -663,7 +662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ "serde", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -737,7 +736,7 @@ dependencies = [ "alloy-json-rpc 0.1.4", "alloy-transport 0.1.4", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "reqwest 0.12.8", "serde_json", @@ -771,7 +770,7 @@ dependencies = [ "alloy-transport 0.1.4", "futures", "http 1.1.0", - "rustls 0.23.15", + "rustls 0.23.14", "serde_json", "tokio", "tokio-tungstenite 0.23.1", @@ -854,9 +853,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "anymap2" @@ -1133,7 +1132,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "synstructure 0.13.1", ] @@ -1145,7 +1144,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1293,7 +1292,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1310,7 +1309,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1409,7 +1408,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1479,14 +1478,14 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.11.0", + "uuid 1.10.0", ] [[package]] name = "aws-sdk-kms" -version = "1.47.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "564a597a3c71a957d60a2e4c62c93d78ee5a0d636531e15b760acad983a5c18e" +checksum = "e33590e8d45206fdc4273ded8a1f292bcceaadd513037aa790fc67b237bc30ee" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1569,9 +1568,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.2" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a065c0fe6fdbdf9f11817eb68582b2ab4aff9e9c39e986ae48f7ec576c6322db" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1584,7 +1583,7 @@ dependencies = [ "http-body 0.4.6", "http-body 1.0.1", "httparse", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", @@ -1784,7 +1783,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.82", + "syn 2.0.79", "which", ] @@ -2006,7 +2005,7 @@ dependencies = [ "tokio-util 0.7.12", "tracing", "url", - "uuid 1.11.0", + "uuid 1.10.0", ] [[package]] @@ -2024,7 +2023,7 @@ dependencies = [ "home", "http 1.1.0", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-named-pipe", "hyper-rustls 0.26.0", "hyper-util", @@ -2112,9 +2111,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -2276,9 +2275,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "jobserver", "libc", @@ -2456,7 +2455,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2817,7 +2816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2876,7 +2875,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2933,7 +2932,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2955,7 +2954,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3051,7 +3050,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3062,7 +3061,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3075,7 +3074,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3095,7 +3094,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "unicode-xid", ] @@ -3183,7 +3182,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3213,7 +3212,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.82", + "syn 2.0.79", "termcolor", "toml", "walkdir", @@ -3471,7 +3470,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "thiserror", - "uuid 1.11.0", + "uuid 1.10.0", ] [[package]] @@ -3802,7 +3801,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3815,7 +3814,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4022,7 +4021,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.82", + "syn 2.0.79", "toml", "walkdir", ] @@ -4040,7 +4039,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4066,7 +4065,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.82", + "syn 2.0.79", "tempfile", "thiserror", "tiny-keccak", @@ -4253,7 +4252,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4383,9 +4382,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", @@ -4597,7 +4596,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4607,7 +4606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", ] @@ -4689,7 +4688,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.82", + "syn 2.0.79", "tracing", "trybuild", ] @@ -4711,6 +4710,9 @@ dependencies = [ "async-trait", "blueprint-metadata", "gadget-sdk", + "reqwest 0.12.8", + "serde_json", + "tokio", "tracing", ] @@ -4724,7 +4726,7 @@ dependencies = [ "gadget-sdk", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "trybuild", ] @@ -4782,12 +4784,13 @@ dependencies = [ "failure", "futures", "gadget-blueprint-proc-macro", + "gadget-blueprint-proc-macro-core", "gadget-context-derive", "gadget-io", "getrandom", "hex", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "k256", "libp2p", @@ -4818,7 +4821,7 @@ dependencies = [ "tracing", "tracing-subscriber 0.3.18", "url", - "uuid 1.11.0", + "uuid 1.10.0", "w3f-bls", ] @@ -4909,7 +4912,7 @@ dependencies = [ "gix-utils", "itoa", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -4930,14 +4933,14 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow", + "winnow 0.6.20", ] [[package]] name = "gix-config-value" -version = "0.14.9" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3de3fdca9c75fa4b83a76583d265fa49b1de6b088ebcd210749c24ceeb74660" +checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c" dependencies = [ "bitflags 2.6.0", "bstr", @@ -5033,14 +5036,14 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] name = "gix-path" -version = "0.10.12" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c04e5a94fdb56b1e91eb7df2658ad16832428b8eeda24ff1a0f0288de2bce554" +checksum = "ebfc4febd088abdcbc9f1246896e57e37b7a34f6909840045a1767c6dafac7af" dependencies = [ "bstr", "gix-trace", @@ -5068,14 +5071,14 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] name = "gix-sec" -version = "0.10.9" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2007538eda296445c07949cf04f4a767307d887184d6b3e83e2d636533ddc6e" +checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f" dependencies = [ "bitflags 2.6.0", "gix-path", @@ -5098,15 +5101,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.11" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" +checksum = "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b" [[package]] name = "gix-utils" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba427e3e9599508ed98a6ddf8ed05493db114564e338e41f6a996d2e4790335f" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" dependencies = [ "fastrand", "unicode-normalization", @@ -5588,9 +5591,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -5612,9 +5615,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", @@ -5638,7 +5641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ "hex", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "pin-project-lite", "tokio", @@ -5654,7 +5657,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -5670,7 +5673,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "log", "rustls 0.22.4", @@ -5689,10 +5692,10 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "log", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -5708,7 +5711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.31", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", @@ -5722,7 +5725,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -5741,7 +5744,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -5757,7 +5760,7 @@ checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc" dependencies = [ "hex", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "pin-project-lite", "tokio", @@ -5853,7 +5856,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "log", "rand", "tokio", @@ -5961,7 +5964,7 @@ dependencies = [ "tokio", "tokio-util 0.7.12", "tracing", - "uuid 1.11.0", + "uuid 1.10.0", ] [[package]] @@ -6038,7 +6041,7 @@ dependencies = [ "tokio", "tokio-util 0.7.12", "tracing", - "uuid 1.11.0", + "uuid 1.10.0", ] [[package]] @@ -6226,9 +6229,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -6255,7 +6258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ "futures", - "hyper 0.14.31", + "hyper 0.14.30", "jsonrpc-core", "jsonrpc-server-utils", "log", @@ -6342,7 +6345,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.23.2", "pin-project", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -6365,7 +6368,7 @@ dependencies = [ "beef", "futures-timer", "futures-util", - "hyper 0.14.31", + "hyper 0.14.30", "jsonrpsee-types 0.22.5", "pin-project", "rustc-hash 1.1.0", @@ -6408,7 +6411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ccf93fc4a0bfe05d851d37d7c32b7f370fe94336b52a2f0efc5f1981895c2e5" dependencies = [ "async-trait", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "jsonrpsee-core 0.22.5", "jsonrpsee-types 0.22.5", @@ -6590,9 +6593,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libgit2-sys" @@ -6984,7 +6987,7 @@ dependencies = [ "quinn", "rand", "ring 0.17.8", - "rustls 0.23.15", + "rustls 0.23.14", "socket2", "thiserror", "tokio", @@ -7071,7 +7074,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7103,7 +7106,7 @@ dependencies = [ "libp2p-identity", "rcgen", "ring 0.17.8", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-webpki 0.101.7", "thiserror", "x509-parser", @@ -7293,7 +7296,7 @@ checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7460,7 +7463,7 @@ checksum = "b4f0c8427b39666bf970460908b213ec09b3b350f20c0c2eabcbba51704a08e6" dependencies = [ "base64 0.22.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-rustls 0.27.3", "hyper-util", "indexmap 2.6.0", @@ -7802,9 +7805,9 @@ dependencies = [ [[package]] name = "ntex" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223834e688405dcc46b5c28bc9225648c603e64d7b61e8903da33064b6f1464e" +checksum = "93daf570cdedb9a01a3020ccd367b2ff37402b30c979aff2da23e3e84d2b6cfc" dependencies = [ "base64 0.22.1", "bitflags 2.6.0", @@ -7861,9 +7864,9 @@ dependencies = [ [[package]] name = "ntex-h2" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e01b86bf30768ed7dca26bf279d0e0798ba5acf0baef4b0ea8e17a91ba71ad4" +checksum = "98763f0ee78f247c02fe1bcdf6380f306a08d95169f9c2d83619d5e1cb26c738" dependencies = [ "bitflags 2.6.0", "fxhash", @@ -7896,15 +7899,14 @@ dependencies = [ [[package]] name = "ntex-io" -version = "2.7.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c49628e35ff52f36137a8e732261f392de621406a163571888f6163e3f6b10" +checksum = "ae8e90ceb0a9a1bcb57459b8e14c6257519afc8dc3460d68b507fb12e909d02c" dependencies = [ "bitflags 2.6.0", "log", "ntex-bytes", "ntex-codec", - "ntex-rt", "ntex-service", "ntex-util", "pin-project-lite", @@ -7953,9 +7955,9 @@ dependencies = [ [[package]] name = "ntex-rt" -version = "0.4.20" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f86c83f89053c29dcf5f1e9663c53726eea337a3221fa243e61e0410a40ad7" +checksum = "1d24cfad65aab77e56ee1f1a218ca248c141a22f6f65aba8ed56037ec3c6f3a4" dependencies = [ "async-channel", "futures-core", @@ -7987,9 +7989,9 @@ dependencies = [ [[package]] name = "ntex-service" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02daa9c4fc8b5382b24dd69d504599a72774d6828e4fc21e9013cb62096db7aa" +checksum = "ab2cc8f131d5fed702d758485fdc45a41dfab69d8ed71b84e2b910b4ea39f795" dependencies = [ "slab", ] @@ -8170,10 +8172,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8266,9 +8268,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", @@ -8287,7 +8289,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8298,18 +8300,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.4.0+3.4.0" +version = "300.3.2+3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -8326,9 +8328,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-float" -version = "4.4.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97" +checksum = "44d501f1a72f71d3c063a6bbc8f7271fa73aa09fe5d6283b6571e2ed176a2537" dependencies = [ "num-traits", ] @@ -8385,7 +8387,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -8467,7 +8469,7 @@ dependencies = [ "regex", "regex-syntax 0.8.5", "structmeta", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8615,9 +8617,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -8626,9 +8628,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -8636,22 +8638,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -8717,7 +8719,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8755,7 +8757,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8847,7 +8849,7 @@ dependencies = [ "polkavm-common 0.8.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8859,7 +8861,7 @@ dependencies = [ "polkavm-common 0.9.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8869,7 +8871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" dependencies = [ "polkavm-derive-impl 0.8.0", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8879,7 +8881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl 0.9.0", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8949,12 +8951,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8971,13 +8973,23 @@ dependencies = [ "uint", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -9023,14 +9035,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -9075,7 +9087,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9162,7 +9174,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.15", + "rustls 0.23.14", "socket2", "thiserror", "tokio", @@ -9179,7 +9191,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.15", + "rustls 0.23.14", "slab", "thiserror", "tinyvec", @@ -9374,7 +9386,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9455,7 +9467,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "hyper-tls 0.5.0", "ipnet", @@ -9501,7 +9513,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-rustls 0.27.3", "hyper-tls 0.6.0", "hyper-util", @@ -9514,7 +9526,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-native-certs 0.8.0", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -9580,7 +9592,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9877,9 +9889,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "aws-lc-rs", "log", @@ -9949,9 +9961,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-platform-verifier" @@ -9964,7 +9976,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", @@ -10004,9 +10016,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -10132,9 +10144,9 @@ dependencies = [ [[package]] name = "scale-encode" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528464e6ae6c8f98e2b79633bf79ef939552e795e316579dab09c61670d56602" +checksum = "4ba0b9c48dc0eb20c60b083c29447c0c4617cb7c4a4c9fef72aa5c5bc539e15e" dependencies = [ "derive_more 0.99.18", "parity-scale-codec", @@ -10147,26 +10159,26 @@ dependencies = [ [[package]] name = "scale-encode-derive" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef2618f123c88da9cd8853b69d766068f1eddc7692146d7dfe9b89e25ce2efd" +checksum = "82ab7e60e2d9c8d47105f44527b26f04418e5e624ffc034f6b4a86c0ba19c5bf" dependencies = [ - "darling 0.20.10", - "proc-macro-crate", + "darling 0.14.4", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.82", + "syn 1.0.109", ] [[package]] name = "scale-info" -version = "2.11.4" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22760a375f81a31817aeaf6f5081e9ccb7ffd7f2da1809a6e3fc82b6656f10d5" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "bitvec", "cfg-if 1.0.0", - "derive_more 1.0.0", + "derive_more 0.99.18", "parity-scale-codec", "scale-info-derive", "serde", @@ -10174,11 +10186,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.4" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc61ebe25a5c410c0e245028fc9934bf8fa4817199ef5a24a68092edfd34614" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -10203,7 +10215,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.82", + "syn 2.0.79", "thiserror", ] @@ -10414,9 +10426,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.213" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -10443,13 +10455,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -10460,14 +10472,14 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -10483,7 +10495,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -10534,7 +10546,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11032,7 +11044,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11150,10 +11162,10 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11578,7 +11590,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11589,7 +11601,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11635,7 +11647,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11711,16 +11723,16 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.82", + "syn 2.0.79", "thiserror", "tokio", ] [[package]] name = "subxt-core" -version = "0.37.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af3b36405538a36b424d229dc908d1396ceb0994c90825ce928709eac1a159a" +checksum = "59f41eb2e2eea6ed45649508cc735f92c27f1fcfb15229e75f8270ea73177345" dependencies = [ "base58", "blake2", @@ -11784,7 +11796,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11856,9 +11868,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -11874,7 +11886,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11886,7 +11898,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11924,7 +11936,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12009,12 +12021,6 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" -[[package]] -name = "target-triple" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" - [[package]] name = "tempfile" version = "3.10.1" @@ -12113,22 +12119,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12209,9 +12215,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -12233,7 +12239,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12284,7 +12290,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -12324,7 +12330,7 @@ checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -12370,7 +12376,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -12382,6 +12388,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.6.0", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.22" @@ -12392,7 +12409,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -12456,7 +12473,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12594,15 +12611,14 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.101" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" dependencies = [ "glob", "serde", "serde_derive", "serde_json", - "target-triple", "termcolor", "toml", ] @@ -12629,7 +12645,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12665,7 +12681,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "sha1", "thiserror", @@ -12701,7 +12717,7 @@ checksum = "f9534daa9fd3ed0bd911d462a37f172228077e7abf18c18a5f67199d959205f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12873,9 +12889,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.11.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] @@ -12918,9 +12934,9 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" [[package]] name = "w3f-bls" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" +checksum = "9c5da5fa2c6afa2c9158eaa7cd9aee249765eb32b5fb0c63ad8b9e79336a47ec" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -12982,9 +12998,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if 1.0.0", "once_cell", @@ -12993,24 +13009,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -13020,9 +13036,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -13030,22 +13046,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasmi" @@ -13231,9 +13247,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" dependencies = [ "futures", "js-sys", @@ -13245,9 +13261,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -13395,7 +13411,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13406,7 +13422,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13662,6 +13678,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.20" @@ -13823,7 +13848,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13843,7 +13868,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] diff --git a/blueprint-test-utils/src/lib.rs b/blueprint-test-utils/src/lib.rs index 3802ff00b..8f8e263bb 100644 --- a/blueprint-test-utils/src/lib.rs +++ b/blueprint-test-utils/src/lib.rs @@ -737,7 +737,7 @@ mod tests_standard { info!("Listening for TaskResponded events..."); while let Some(event) = event_stream.next().await { let TaskResponded { - taskResponse: response, + taskResponse: _response, .. } = event.log_decode::().unwrap().inner.data; let mut counter = successful_responses.lock().await; diff --git a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std index 1de6eecf8..035de35f5 160000 --- a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std +++ b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f +Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 diff --git a/blueprints/incredible-squaring-eigenlayer/src/lib.rs b/blueprints/incredible-squaring-eigenlayer/src/lib.rs index 868b866c5..9fa2b8b50 100644 --- a/blueprints/incredible-squaring-eigenlayer/src/lib.rs +++ b/blueprints/incredible-squaring-eigenlayer/src/lib.rs @@ -72,12 +72,11 @@ pub fn noop(_: u32) { event_listener( listener = EvmContractEventListener( instance = IncredibleSquaringTaskManager, - event = IncredibleSquaringTaskManager::NewTaskCreated, - event_converter = convert_event_to_inputs, - callback = noop, abi = INCREDIBLE_SQUARING_TASK_MANAGER_ABI_STRING, ), event = IncredibleSquaringTaskManager::NewTaskCreated, + pre_processor = convert_event_to_inputs, + post_processor = noop, ), )] pub async fn xsquare_eigen( diff --git a/blueprints/incredible-squaring-eigenlayer/src/tests.rs b/blueprints/incredible-squaring-eigenlayer/src/tests.rs index ad202f729..c481b1f9d 100644 --- a/blueprints/incredible-squaring-eigenlayer/src/tests.rs +++ b/blueprints/incredible-squaring-eigenlayer/src/tests.rs @@ -10,6 +10,7 @@ use blueprint_test_utils::test_ext::NAME_IDS; use blueprint_test_utils::{anvil, get_receipt, inject_test_keys}; use gadget_io::SupportedChains; use gadget_sdk::config::{ContextConfig, GadgetCLICoreSettings, Protocol}; +use gadget_sdk::info; use gadget_sdk::logging::setup_log; use gadget_sdk::run::GadgetRunner; use incredible_squaring_aggregator::aggregator::Aggregator; @@ -45,10 +46,11 @@ alloy_sol_types::sol!( #[allow(clippy::needless_return)] async fn test_eigenlayer_incredible_squaring_blueprint() -> Result<(), Box> { setup_log(); - let (_container, http_endpoint, ws_endpoint) = anvil::start_anvil_container(true).await; - let alice_keystore = setup_eigen_environment(0).await; // We use an ID of 0 for Alice + let (_container, http_endpoint, ws_endpoint) = + anvil::start_anvil_container(&alice_keystore, true).await; + std::env::set_var("EIGENLAYER_HTTP_ENDPOINT", http_endpoint.clone()); std::env::set_var("EIGENLAYER_WS_ENDPOINT", ws_endpoint.clone()); std::env::set_var("REGISTRATION_MODE_ON", "true"); // Set to run registration @@ -244,7 +246,7 @@ async fn test_eigenlayer_incredible_squaring_blueprint() -> Result<(), Box::default(), response_hash); info!("Shutting down aggregator..."); - aggregator_shutdown.send(()).unwrap(); + aggregator_shutdown.send(()).await.unwrap(); // chain_task.abort(); // runner_task.abort(); // aggregator_task.abort(); @@ -291,7 +293,7 @@ async fn mine_blocks(endpoint: &str, blocks: u8) { .output() .await .unwrap(); - info!( + gadget_sdk::info!( "Mined {} block{}", blocks, if blocks == 1 { "" } else { "s" } diff --git a/blueprints/incredible-squaring/contracts/lib/forge-std b/blueprints/incredible-squaring/contracts/lib/forge-std index 1de6eecf8..035de35f5 160000 --- a/blueprints/incredible-squaring/contracts/lib/forge-std +++ b/blueprints/incredible-squaring/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f +Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 diff --git a/blueprints/incredible-squaring/contracts/lib/tnt-core b/blueprints/incredible-squaring/contracts/lib/tnt-core index c1fa9c7c3..fad7030fe 160000 --- a/blueprints/incredible-squaring/contracts/lib/tnt-core +++ b/blueprints/incredible-squaring/contracts/lib/tnt-core @@ -1 +1 @@ -Subproject commit c1fa9c7c3c5891aab9bd25002a434d93c54942cd +Subproject commit fad7030fe53ea5d4a7576316880f5416fe656ee9 diff --git a/blueprints/incredible-squaring/src/lib.rs b/blueprints/incredible-squaring/src/lib.rs index 720c5981e..32f6f5e6c 100644 --- a/blueprints/incredible-squaring/src/lib.rs +++ b/blueprints/incredible-squaring/src/lib.rs @@ -1,5 +1,8 @@ +use gadget_sdk::clients::tangle::runtime::TangleClient; +use gadget_sdk::event_listener::tangle::jobs::{services_post_processor, services_pre_processor}; +use gadget_sdk::event_listener::tangle::TangleEventListener; use gadget_sdk::job; -use std::convert::Infallible; +use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobCalled; /// Returns x^2 saturating to [`u64::MAX`] if overflow occurs. #[job( @@ -7,10 +10,11 @@ use std::convert::Infallible; params(x), result(_), event_listener( - listener = TangleEventListener, - event = JobCalled, + listener = TangleEventListener, + pre_processor = services_pre_processor, + post_processor = services_post_processor, ), )] -pub fn xsquare(x: u64) -> Result { - Ok(x.saturating_pow(2u32)) +pub fn xsquare(x: u64, context: TangleClient) -> Result { + Ok(x.saturating_pow(2)) } diff --git a/blueprints/incredible-squaring/src/main.rs b/blueprints/incredible-squaring/src/main.rs index 3d2aa096a..d62eb05cb 100644 --- a/blueprints/incredible-squaring/src/main.rs +++ b/blueprints/incredible-squaring/src/main.rs @@ -14,6 +14,7 @@ async fn main() { let x_square = blueprint::XsquareEventHandler { service_id: env.service_id.unwrap(), client: client.clone(), + context: client.clone(), signer, }; diff --git a/blueprints/periodic-web-poller/src/lib.rs b/blueprints/periodic-web-poller/src/lib.rs index 1a25e095b..7e538db48 100644 --- a/blueprints/periodic-web-poller/src/lib.rs +++ b/blueprints/periodic-web-poller/src/lib.rs @@ -10,7 +10,6 @@ use std::convert::Infallible; result(_), event_listener( listener = PeriodicEventListener<2000, WebPoller, serde_json::Value, reqwest::Client>, - event = serde_json::Value, pre_processor = pre_process, post_processor = post_process, ), diff --git a/blueprints/tangle-avs-blueprint/contracts/lib/forge-std b/blueprints/tangle-avs-blueprint/contracts/lib/forge-std index 8f24d6b04..035de35f5 160000 --- a/blueprints/tangle-avs-blueprint/contracts/lib/forge-std +++ b/blueprints/tangle-avs-blueprint/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 8f24d6b04c92975e0795b5868aa0d783251cdeaa +Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 diff --git a/cli/src/deploy.rs b/cli/src/deploy.rs index 08445303c..07ab9f727 100644 --- a/cli/src/deploy.rs +++ b/cli/src/deploy.rs @@ -52,10 +52,12 @@ pub async fn generate_service_blueprint, T: AsRef>( .exec() .context("Getting Metadata about the workspace")?; - let package = find_package(&metadata, pkg_name)?; - let mut blueprint = load_blueprint_metadata(package)?; - build_contracts_if_needed(package, &blueprint).context("Building contracts")?; - deploy_contracts_to_tangle(rpc_url.as_ref(), package, &mut blueprint, signer_evm).await?; + let package = find_package(&metadata, pkg_name)?.clone(); + let package_clone = &package.clone(); + let mut blueprint = + tokio::task::spawn_blocking(move || load_blueprint_metadata(&package)).await??; + build_contracts_if_needed(package_clone, &blueprint).context("Building contracts")?; + deploy_contracts_to_tangle(rpc_url.as_ref(), package_clone, &mut blueprint, signer_evm).await?; bake_blueprint(blueprint) } @@ -110,7 +112,9 @@ pub async fn deploy_to_tangle( Ok(event.blueprint_id) } -pub fn load_blueprint_metadata(package: &cargo_metadata::Package) -> Result { +pub fn load_blueprint_metadata( + package: &cargo_metadata::Package, +) -> Result> { let blueprint_json_path = package .manifest_path .parent() diff --git a/docs/event_listeners.md b/docs/event_listeners.md deleted file mode 100644 index b84af184e..000000000 --- a/docs/event_listeners.md +++ /dev/null @@ -1,256 +0,0 @@ -# Event Listeners - -When building a blueprint, your application may require to listen to events. Events can be of any type, and handling those events is entirely up to your discretion. - -In general, when defining your job, you must register any event listeners and provide a context as such: - -```rust -#[job( - id = 0, - params(x), - result(_), - event_listener(TangleEventListener, MyEventListener1, MyEventListener2, ...), // <-- Register all event listeners here - verifier(evm = "MyVerifier") -)] -pub fn hello_event_listener( - x: u64, - context: MyContext, // <-- The context type must be the first additional parameter - env: GadgetConfiguration, -) -> Result { - Ok(x.saturating_pow(2u32)) -} -``` - -In order to make these registered event listeners to work, we must define structs that implement `EventListener`. -## Creating Event Listeners - -To create an event listener, begin by defining a struct or enum which will listen to events: - -```rust -use gadget_sdk::event_listener::EventListener; -use async_trait::async_trait; - -/// Define a simple event listener that ticks every MS milliseconds. -pub struct Ticker { - additional_delay: u64, -} -``` - -Then, define a context for the event listener: - -```rust -#[derive(Copy, Clone)] -pub struct MyContext { - pub additional_delay: u64, -} -``` - -Next, implement `EventListener` for `Ticker`: - -```rust -/// Implement the [`EventListener`] trait for the Ticker struct. [`EventListener`] has two type parameters: -/// -/// - T: is the type of the event that the listener listens for. This can be anything that is Send + Sync + 'static. -/// In this case, we are using [`Instant`], which is a timestamp. -/// -/// - Ctx: is the context type that the listener receives when constructed. This can be anything that is Send + Sync + 'static, -/// with the special requirement that it is the *first* listed additional parameter in the [`job`] macro (i.e., a parameter not -/// in params(...)). In this case, we are using [`MyContext`]. -#[async_trait] -impl EventListener for Ticker { - /// Use a reference to the context, [`MyContext`], to construct the listener - async fn new(context: &MyContext) -> Result - where - Self: Sized, - { - Ok(Self { additional_delay: context.additional_delay }) - } - - /// Implement the logic that looks for events. In this case, we have a simple stream - /// that returns after MS milliseconds. - async fn next_event(&mut self) -> Option { - tokio::time::sleep(tokio::time::Duration::from_millis(MSEC as u64 + self.additional_delay)).await; - Some(Instant::now()) - } - - /// After next_event is called, the event gets passed here. This is where you would implement - /// listener-specific logic. - async fn handle_event(&mut self, _event: Instant) -> Result<(), Error> { - Ok(()) - } -} -``` - -Finally, register the event listener inside the `job` macro using `event_listener`: - -```rust -#[job( - id = 0, - params(x), - result(_), - event_listener(Ticker::<6000>), // <-- Register the event listener here - verifier(evm = "IncredibleSquaringBlueprint") -)] -pub fn hello_event_listener( - x: u64, - context: MyContext, // <-- The context type must be the first additional parameter - env: GadgetConfiguration, -) -> Result { - Ok(x.saturating_pow(2u32)) -} -``` - -## Primary Event Listeners -Primary event listeners are the most common type of event listener. They are easy to use, and require no context to work since this is done behind the scenes. - -### TangleEventListener -The `TangleEventListener` is a type that listens to the Tangle network for events. This is a required type if you expect your application to use the tangle network to listen to jobs. - -The `TangleEventListener` is already implemented and ready to use. Simply register it in the `job` macro, and your application -will automatically work with the Tangle network. - -```rust -/// Returns x^2 saturating to [`u64::MAX`] if overflow occurs. -#[job( - id = 0, - params(x), - result(_), - event_listener(TangleEventListener), - verifier(evm = "IncredibleSquaringBlueprint") -)] -pub fn xsquare( - x: u64, -) -> Result { - Ok(x.saturating_pow(2u32)) -} -``` - -### EvmContractEventListener -The `EvmContractEventListener` is a type that listens to the Ethereum Virtual Machine (EVM) for events. - -Like the `TangleEventListener`, the `EvmContractEventListener` is already implemented and ready to use. Simply register it in the `job` macro, and your application will automatically work with the EVM. - -```rust -/// Returns x^2 saturating to [`u64::MAX`] if overflow occurs. -#[job( - id = 0, - params(x), - result(_), - event_listener(EvmContractEventListener( - instance = IncredibleSquaringTaskManager, - event = IncredibleSquaringTaskManager::NewTaskCreated, - event_converter = convert_event_to_inputs, - callback = IncredibleSquaringTaskManager::IncredibleSquaringTaskManagerCalls::respondToTask - )), -)] -pub fn xsquare( - x: u64, -) -> Result { - Ok(x.saturating_pow(2u32)) -} -``` - -## Wrapper Event Listeners -Various wrappers exist to simplify how common operations are performed. - -### PeriodicEventListener -Some programs may only be interested in checking for events at regular intervals. In this case, the `PeriodicEventListener` can be used to simplify the process. - -A `PeriodicEventListener` is a wrapper that takes 4 type parameters: - -* `MSEC`: The number of milliseconds between each event check. -* `T`: The inner event listener type -* `Event`: The event type -* `Ctx`: The context type - -We can make a `PeriodicEventListener` that ticks every 5000ms to check the status of a web server using [reqwest](crates.io/crates/reqwest). - -```rust -use gadget_sdk::event_listener::periodic::PeriodicEventListener; - -/// Define an event listener that polls a webserver -pub struct WebPoller { - pub client: reqwest::Client, -} -``` - -Then, implement `EventListener` for `WebPoller`: - -```rust -#[async_trait::async_trait] -impl EventListener for WebPoller { - /// Build the event listener. Note that this time, we don't necessarily need the context - async fn new(_context: &MyContext) -> Result - where - Self: Sized, - { - let client = reqwest::Client::new(); - Ok(Self { client }) - } - - /// Implement the logic that polls the web server - async fn next_event(&mut self) -> Option { - // Send a GET request to the JSONPlaceholder API - let response = self.client - .get("https://jsonplaceholder.typicode.com/todos/1") - .send() - .await?; - - // Check if the request was successful - if response.status().is_success() { - // Parse the JSON response - let resp: serde_json::Value = response.json().await?; - Some(resp) - } else { - None - } - } - - /// Implement any handler logic when an event is received - async fn handle_event(&mut self, _event: serde_json::Value) -> Result<(), Error> { - Ok(()) - } -} -``` - -Finally, register the event listener inside the `job` macro using `event_listener`: - -```rust -#[job( - id = 0, - params(value), - result(_), - event_listener(PeriodicEventListener::<6000, WebPoller, serde_json::Value, MyContext>), // <-- Register the event listener here -)] -pub fn hello_event_listener( - value: serde_json::Value, - context: MyContext, -) -> Result { - let completed = value["completed"].as_bool().unwrap_or(false); - Ok(completed) -} -``` - -## Multiple Event Listeners -Arbitrarily many event listeners can be used in a single job. This is useful when you want to listen to multiple sources to handle job logic - -For example, using the example of the `PeriodicEventListener` above, we can add a `TangleEventListener` to the job: - -```rust -#[job( - id = 0, - params(x), - result(_), - event_listener(TangleEventListener, PeriodicEventListener::<6000, WebPoller, serde_json::Value, MyContext>), // <-- Register the event listeners here - verifier(evm = "IncredibleSquaringBlueprint") -)] -pub fn hello_event_listener( - x: u64, - context: MyContext, - env: GadgetConfiguration, -) -> Result { - Ok(x.saturating_pow(2u32)) -} -``` - -In this case, both the TangleEventListener, which is listening to the Tangle network, and the PeriodicEventListener, which is polling a web server, will be used in *parallel* to listen for events. \ No newline at end of file diff --git a/macros/blueprint-proc-macro-core/src/lib.rs b/macros/blueprint-proc-macro-core/src/lib.rs index 214ae5307..28a5faf9a 100644 --- a/macros/blueprint-proc-macro-core/src/lib.rs +++ b/macros/blueprint-proc-macro-core/src/lib.rs @@ -50,6 +50,30 @@ pub enum FieldType { AccountId, } +impl AsRef for FieldType { + fn as_ref(&self) -> &str { + match self { + FieldType::Uint8 => "u8", + FieldType::Uint16 => "u16", + FieldType::Uint32 => "u32", + FieldType::Uint64 => "u64", + FieldType::Int8 => "i8", + FieldType::Int16 => "i16", + FieldType::Int32 => "i32", + FieldType::Int64 => "i64", + FieldType::Uint128 => "u128", + FieldType::U256 => "U256", + FieldType::Int128 => "i128", + FieldType::Float64 => "f64", + FieldType::Bool => "bool", + FieldType::String => "String", + FieldType::Bytes => "Bytes", + FieldType::AccountId => "AccountId", + ty => unimplemented!("Unsupported FieldType {ty:?}"), + } + } +} + /// The main definition of a service. /// /// This contains the metadata of the service, the job definitions, and other hooks, along with the diff --git a/macros/blueprint-proc-macro/src/event_listener/evm.rs b/macros/blueprint-proc-macro/src/event_listener/evm.rs index 327a57d5c..c1eaa110a 100644 --- a/macros/blueprint-proc-macro/src/event_listener/evm.rs +++ b/macros/blueprint-proc-macro/src/event_listener/evm.rs @@ -28,10 +28,27 @@ pub(crate) fn generate_evm_event_handler( ) -> TokenStream { let (instance_base, instance_name, instance_wrapper_name, _instance) = get_evm_instance_data(event_handler); - let event = event_handler.event().unwrap(); - let event_converter = event_handler.event_converter().unwrap(); - let callback = event_handler.callback().unwrap(); - let abi_string = event_handler.abi().unwrap(); + let event = event_handler + .get_event_listener() + .event + .as_ref() + .expect("Event type must be specified"); + let event_converter = event_handler + .get_event_listener() + .pre_processor + .as_ref() + .unwrap(); + let callback = event_handler + .get_event_listener() + .post_processor + .as_ref() + .unwrap(); + let abi_string = event_handler + .get_event_listener() + .evm_args + .as_ref() + .and_then(|r| r.abi.clone()) + .expect("ABI String must exist"); quote! { #[derive(Debug, Clone)] diff --git a/macros/blueprint-proc-macro/src/event_listener/tangle.rs b/macros/blueprint-proc-macro/src/event_listener/tangle.rs index 5c40b58e9..1862ee2ae 100644 --- a/macros/blueprint-proc-macro/src/event_listener/tangle.rs +++ b/macros/blueprint-proc-macro/src/event_listener/tangle.rs @@ -1,47 +1,11 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Ident, LitInt}; +use syn::Ident; #[allow(clippy::too_many_arguments)] -/// This will run all event handlers at once once init is called on the special-case event handler for substrate -pub(crate) fn generate_tangle_event_handler( - struct_name: &Ident, - job_id: &LitInt, - params_tokens: &[TokenStream], - result_tokens: &[TokenStream], - fn_call: &TokenStream, -) -> TokenStream { +pub(crate) fn generate_additional_tangle_logic(struct_name: &Ident) -> TokenStream { quote! { #[automatically_derived] - #[async_trait::async_trait] - impl gadget_sdk::events_watcher::substrate::EventHandler for #struct_name { - async fn handle(&self, event: &gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobCalled) -> Result>, gadget_sdk::events_watcher::Error> { - use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field; - let mut args_iter = event.args.clone().into_iter(); - #(#params_tokens)* - #fn_call - - let mut result = Vec::new(); - #(#result_tokens)* - - Ok(result) - } - - /// Returns the job ID - fn job_id(&self) -> u8 { - #job_id - } - - /// Returns the service ID - fn service_id(&self) -> u64 { - self.service_id - } - - fn signer(&self) -> &gadget_sdk::keystore::TanglePairSigner { - &self.signer - } - } - impl gadget_sdk::event_listener::markers::IsTangle for #struct_name {} } } diff --git a/macros/blueprint-proc-macro/src/job.rs b/macros/blueprint-proc-macro/src/job.rs index 2d8788644..d876f8ce7 100644 --- a/macros/blueprint-proc-macro/src/job.rs +++ b/macros/blueprint-proc-macro/src/job.rs @@ -1,5 +1,5 @@ use crate::event_listener::evm::{generate_evm_event_handler, get_evm_instance_data}; -use crate::event_listener::tangle::generate_tangle_event_handler; +use crate::event_listener::tangle::generate_additional_tangle_logic; use crate::shared::{pascal_case, type_to_field_type}; use gadget_blueprint_proc_macro_core::{FieldType, JobDefinition, JobMetadata}; use indexmap::{IndexMap, IndexSet}; @@ -37,12 +37,81 @@ pub fn get_job_id_field_name(input: &ItemFn) -> (String, Ident, Ident) { (fn_name_string, job_def_name, job_id_name) } +pub fn get_current_call_id_field_name(input: &ItemFn) -> Ident { + let fn_name = &input.sig.ident; + format_ident!( + "{}_ACTIVE_CALL_ID", + fn_name.to_string().to_ascii_uppercase() + ) +} + /// Job Macro implementation pub(crate) fn job_impl(args: &JobArgs, input: &ItemFn) -> syn::Result { // Extract function name and arguments - let (fn_name_string, job_def_name, job_id_name) = get_job_id_field_name(input); + let (fn_name_string, _job_def_name, _job_id_name) = get_job_id_field_name(input); const SUFFIX: &str = "EventHandler"; + let result = get_result_type(input)?; + let param_map = param_types(input)?; + // Extracts Job ID and param/result types + let job_id = &args.id; + let params_type = declared_params_to_field_types(&args.params, ¶m_map)?; + let result_type = declared_result_type_to_field_types(&args.result, result)?; + + let (event_listener_gen, event_listener_calls) = generate_event_workflow_tokenstream( + input, + SUFFIX, + &fn_name_string, + &args.event_listener, + args.skip_codegen, + ¶m_map, + &args.params, + &result_type, + ); + + // Generate Event Workflow, if not being skipped + let additional_specific_logic = if args.skip_codegen { + proc_macro2::TokenStream::default() + } else { + // Specialized code for the event workflow or otherwise + generate_additional_logic(input, args, ¶m_map, ¶ms_type, SUFFIX) + }; + + let autogen_struct = if args.skip_codegen { + proc_macro2::TokenStream::default() + } else { + generate_autogen_struct( + input, + &args.event_listener, + &args.params, + ¶m_map, + SUFFIX, + &event_listener_calls, + ) + }; + + let job_const_block = generate_job_const_block(input, params_type, result_type, job_id)?; + + // Generates final TokenStream that will be returned + let gen = quote! { + #job_const_block + + #autogen_struct + + #(#event_listener_gen)* + + #[allow(unused_variables)] + #input + + #additional_specific_logic + }; + + // println!("{}", gen.to_string()); + + Ok(gen.into()) +} + +pub fn get_result_type(input: &ItemFn) -> syn::Result<&Type> { let syn::ReturnType::Type(_, result) = &input.sig.output else { return Err(syn::Error::new_spanned( &input.sig.output, @@ -69,43 +138,17 @@ pub(crate) fn job_impl(args: &JobArgs, input: &ItemFn) -> syn::Result, + result: Vec, + job_id: &LitInt, +) -> syn::Result { + let (fn_name_string, job_def_name, job_id_name) = get_job_id_field_name(input); // Creates Job Definition using input parameters let job_def = JobDefinition { metadata: JobMetadata { @@ -113,8 +156,8 @@ pub(crate) fn job_impl(args: &JobArgs, input: &ItemFn) -> syn::Result syn::Result syn::Result> { @@ -180,17 +212,19 @@ pub(crate) fn param_types(input: &ItemFn) -> syn::Result> } #[allow(clippy::too_many_arguments)] -pub(crate) fn generate_event_listener_tokenstream( +pub(crate) fn generate_event_workflow_tokenstream( input: &ItemFn, suffix: &str, - fn_name_string: &str, + _fn_name_string: &str, event_listeners: &EventListenerArgs, skip_codegen: bool, param_types: &IndexMap, params: &[Ident], + result_types: &[FieldType], ) -> (Vec, Vec) { let (event_handler_args, event_handler_arg_types) = get_event_handler_args(param_types, params); let (_, _, struct_name) = generate_fn_name_and_struct(input, suffix); + let (fn_name_string, _job_def_name, job_id_name) = get_job_id_field_name(input); // Generate Event Listener, if not being skipped let mut event_listener_calls = vec![]; let event_listener_gen = if skip_codegen { @@ -204,19 +238,17 @@ pub(crate) fn generate_event_listener_tokenstream( idx, suffix.to_lowercase() ); - let is_tangle = listener_meta.evm_args.is_none(); + let is_not_evm = !matches!(listener_meta.listener_type, ListenerType::Evm); // convert the listener var, which is just a struct name, to an ident let listener = listener_meta.listener.to_token_stream(); - // if Listener == TangleEventListener or EvmContractEventListener, we need to use defaults - let listener_str = listener.to_string(); - let type_args = if is_tangle { + let type_args = if is_not_evm { proc_macro2::TokenStream::default() } else { quote! { } }; - let bounded_type_args = if is_tangle { + let bounded_type_args = if is_not_evm { proc_macro2::TokenStream::default() } else { quote! { } @@ -225,28 +257,14 @@ pub(crate) fn generate_event_listener_tokenstream( let autogen_struct_name = quote! { #struct_name #type_args }; // Check for special cases - let next_listener = if listener_str.contains("TangleEventListener") - || listener_str.contains("EvmContractEventListener") - { + let next_listener = if matches!(listener_meta.listener_type, ListenerType::Evm) { // How to inject not just this event handler, but all event handlers here? - let wrapper = if is_tangle { - quote! { - gadget_sdk::event_listener::tangle_events::TangleEventWrapper<_> - } - } else { - quote! { - gadget_sdk::event_listener::evm_contracts::EthereumHandlerWrapper<#autogen_struct_name, _> - } + let wrapper = quote! { + gadget_sdk::event_listener::evm_contracts::EthereumHandlerWrapper<#autogen_struct_name, _> }; - let ctx_create = if is_tangle { - quote! { - (ctx.client.clone(), std::sync::Arc::new(ctx.clone()) as gadget_sdk::events_watcher::substrate::EventHandlerFor) - } - } else { - quote! { - (ctx.contract.clone(), std::sync::Arc::new(ctx.clone()) as std::sync::Arc<#autogen_struct_name>) - } + let ctx_create = quote! { + (ctx.contract.clone(), std::sync::Arc::new(ctx.clone()) as std::sync::Arc<#autogen_struct_name>) }; if event_listener_calls.is_empty() { @@ -256,11 +274,11 @@ pub(crate) fn generate_event_listener_tokenstream( } event_listener_calls.push(quote! { - listeners.push(#listener_function_name(&self).await.expect("Event listener already initialized")); - }); + listeners.push(#listener_function_name(&self).await.expect("Event listener already initialized")); + }); quote! { - async fn #listener_function_name #bounded_type_args(ctx: &#autogen_struct_name) -> Option>>{ + async fn #listener_function_name #bounded_type_args(ctx: &#autogen_struct_name) -> Option>> { static ONCE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); if !ONCE.load(std::sync::atomic::Ordering::Relaxed) { ONCE.store(true, std::sync::atomic::Ordering::Relaxed); @@ -281,14 +299,17 @@ pub(crate) fn generate_event_listener_tokenstream( } else { // Generate the variable that we are passing as the context into EventListener::create(&mut ctx) // We assume the first supplied event handler arg is the context we are injecting into the event listener + // Then, pass that into the EventFlowWrapper let (context, field_in_self) = event_handler_args .first() .map(|ctx| (quote! {self}, (*ctx).clone())) .expect("No context found"); + let autogen_struct_name = quote! { #struct_name #type_args }; + let context_ty = event_handler_arg_types .first() - .map(|_ty| quote! {#struct_name}) + .map(|ty| quote! {#ty}) .unwrap_or_default(); if event_listener_calls.is_empty() { @@ -301,50 +322,113 @@ pub(crate) fn generate_event_listener_tokenstream( listeners.push(#listener_function_name(&#context).await.expect("Event listener already initialized")); }); - let event_type = &listener_meta.event; - // The event type is what gets sent through the pre_processor_function. - let pre_processor_function = if let Some(preprocessor) = &listener_meta.pre_processor { quote! { #preprocessor } } else { // identity transformation - quote! { |evt| async move { Ok(evt) } } + get_preprocessor_default_identity_function(&listener_meta.listener_type) }; // The job_processor is just the job function. Since it may contain multiple params, we need a new function to call it. let fn_name_ident = &input.sig.ident; + let static_ctx_get_override = quote! { CTX.get().unwrap() }; let ordered_inputs = - get_fn_call_ordered(param_types, params, Some(quote! { CTX.get().unwrap() })); + get_fn_call_ordered(param_types, params, Some(static_ctx_get_override)); + let asyncness = get_asyncness(input); - // The below assumes param0 IS the event streamed from the event listener - let job_processor_wrapper = quote! { - move |param0| async move { - #fn_name_ident (#(#ordered_inputs)*) #asyncness .map_err(|err| gadget_sdk::Error::Other(err.to_string())) + let call_id_static_name = get_current_call_id_field_name(input); + let job_processor_wrapper = if matches!( + listener_meta.listener_type, + ListenerType::Tangle + ) { + let params = declared_params_to_field_types(params, param_types) + .expect("Failed to generate params"); + let params_tokens = event_listeners.get_param_name_tokenstream(¶ms, true); + quote! { + move |event: gadget_sdk::event_listener::tangle::jobs::TangleJobEvent| async move { + if let Some(call_id) = event.call_id { + #call_id_static_name.store(call_id, std::sync::atomic::Ordering::Relaxed); + } + + let mut args_iter = event.args.clone().into_iter(); + #(#params_tokens)* + #fn_name_ident (#(#ordered_inputs)*) #asyncness .map_err(|err| gadget_sdk::Error::Other(err.to_string())) + } + } + } else { + quote! { + move |param0| async move { + #fn_name_ident (#(#ordered_inputs)*) #asyncness .map_err(|err| gadget_sdk::Error::Other(err.to_string())) + } } }; - let post_processor_function = - if let Some(postprocessor) = &listener_meta.post_processor { - quote! { #postprocessor } + + let post_processor_function = if let Some(postprocessor) = + &listener_meta.post_processor + { + if matches!(listener_meta.listener_type, ListenerType::Tangle) { + let result_tokens = + event_listeners.get_param_result_tokenstream(result_types); + quote! { + |job_result| async move { + let ctx = CTX.get().unwrap(); + let mut result = Vec::new(); + // TODO: Will need to decouple this from jobs + #(#result_tokens)* + let tangle_job_result = gadget_sdk::event_listener::tangle::jobs::TangleJobResult { + results: result, + service_id: ctx.service_id, + call_id: #call_id_static_name.load(std::sync::atomic::Ordering::Relaxed), + client: ctx.client.clone(), + signer: ctx.signer.clone(), + }; + + #postprocessor(tangle_job_result).await.map_err(|err| gadget_sdk::Error::Other(err.to_string())) + } + } } else { - // no-op default - quote! { |_evt| async move { Ok(()) } } - }; + quote! { #postprocessor } + } + } else { + // no-op default + quote! { |_evt| async move { Ok(()) } } + }; + + let context_declaration = if matches!( + listener_meta.listener_type, + ListenerType::Tangle + ) { + quote! { + let context = gadget_sdk::event_listener::tangle::TangleListenerInput::<#context_ty> { + client: ctx.client.clone(), + signer: ctx.signer.clone(), + job_id: #job_id_name, + service_id: ctx.service_id, + context: ctx. #field_in_self .clone(), + }; + } + } else { + quote! { let context = ctx. #field_in_self .clone(); } + }; quote! { - async fn #listener_function_name(ctx: &#context_ty) -> Option>> { + async fn #listener_function_name #bounded_type_args(ctx: &#autogen_struct_name) -> Option>> { static ONCE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); if !ONCE.load(std::sync::atomic::Ordering::Relaxed) { ONCE.store(true, std::sync::atomic::Ordering::Relaxed); let (tx, rx) = gadget_sdk::tokio::sync::oneshot::channel(); - static CTX: gadget_sdk::tokio::sync::OnceCell<#context_ty> = gadget_sdk::tokio::sync::OnceCell::const_new(); + + static CTX: gadget_sdk::tokio::sync::OnceCell<#autogen_struct_name> = gadget_sdk::tokio::sync::OnceCell::const_new(); + #context_declaration + if let Err(_err) = CTX.set(ctx.clone()) { gadget_sdk::error!("Failed to set the context"); return None; } let job_processor = #job_processor_wrapper; - let listener = <#listener as gadget_sdk::event_listener::EventListener<#event_type, _>>::new(&ctx. #field_in_self).await.expect("Failed to create event listener"); + let listener = <#listener as gadget_sdk::event_listener::EventListener<_, _>>::new(&context).await.expect("Failed to create event listener"); let mut event_workflow = gadget_sdk::event_listener::executor::EventFlowWrapper::new( listener, #pre_processor_function, @@ -378,6 +462,31 @@ pub(crate) fn generate_event_listener_tokenstream( (event_listener_gen, event_listener_calls) } +fn get_preprocessor_default_identity_function( + listener_type: &ListenerType, +) -> proc_macro2::TokenStream { + match listener_type { + ListenerType::Evm => { + unimplemented!("EVM not implemented yet") + } + ListenerType::Tangle => { + quote! { + |evt| async move { + Ok(evt) + } + } + } + ListenerType::Custom => { + // Identity transform + quote! { + |evt| async move { + Ok(evt) + } + } + } + } +} + /// Get all the params names inside the param_types map /// and not in the params list to be added to the event handler. pub(crate) fn get_event_handler_args<'a>( @@ -404,14 +513,15 @@ fn generate_fn_name_and_struct<'a>(f: &'a ItemFn, suffix: &'a str) -> (&'a Ident #[allow(clippy::too_many_lines)] pub fn generate_autogen_struct( input: &ItemFn, - job_args: &JobArgs, + event_listener_args: &EventListenerArgs, + params: &[Ident], param_types: &IndexMap, suffix: &str, event_listener_calls: &[proc_macro2::TokenStream], ) -> proc_macro2::TokenStream { let (_fn_name, fn_name_string, struct_name) = generate_fn_name_and_struct(input, suffix); - let (event_handler_args, _) = get_event_handler_args(param_types, &job_args.params); + let (event_handler_args, _) = get_event_handler_args(param_types, params); let mut additional_var_indexes = vec![]; let additional_params = event_handler_args @@ -435,7 +545,7 @@ pub fn generate_autogen_struct( let mut type_params = proc_macro2::TokenStream::default(); // Even if multiple tangle listeners, we only need this once - if job_args.event_listener.has_tangle() { + if event_listener_args.has_tangle() { required_fields.push(quote! { pub service_id: u64, pub signer: gadget_sdk::keystore::TanglePairSigner, @@ -444,8 +554,8 @@ pub fn generate_autogen_struct( } // Even if multiple evm listeners, we only need this once - if job_args.event_listener.has_evm() { - let (_, _, instance_wrapper_name, _) = get_evm_instance_data(&job_args.event_listener); + if event_listener_args.has_evm() { + let (_, _, instance_wrapper_name, _) = get_evm_instance_data(event_listener_args); required_fields.push(quote! { pub contract: #instance_wrapper_name, @@ -541,13 +651,13 @@ pub fn generate_additional_logic( job_args: &JobArgs, param_types: &IndexMap, params: &[FieldType], - results: &[FieldType], suffix: &str, ) -> proc_macro2::TokenStream { let (fn_name, _fn_name_string, struct_name) = generate_fn_name_and_struct(input, suffix); - let job_id = &job_args.id; let event_listener_args = &job_args.event_listener; - let params_tokens = job_args.event_listener.get_param_name_tokenstream(params); + let params_tokens = job_args + .event_listener + .get_param_name_tokenstream(params, false); let fn_call_ordered = get_fn_call_ordered(param_types, &job_args.params, None); let asyncness = get_asyncness(input); @@ -565,22 +675,12 @@ pub fn generate_additional_logic( }; }; - let result_tokens = job_args - .event_listener - .get_param_result_tokenstream(results); - match job_args.event_listener.get_event_listener().listener_type { ListenerType::Evm => { generate_evm_event_handler(&struct_name, event_listener_args, ¶ms_tokens, &fn_call) } - ListenerType::Tangle => generate_tangle_event_handler( - &struct_name, - job_id, - ¶ms_tokens, - &result_tokens, - &fn_call, - ), + ListenerType::Tangle => generate_additional_tangle_logic(&struct_name), ListenerType::Custom => proc_macro2::TokenStream::default(), } @@ -692,37 +792,38 @@ impl Parse for Params { } } -impl JobArgs { - fn params_to_field_types( - &self, - param_types: &IndexMap, - ) -> syn::Result> { - let params = self - .params - .iter() - .map(|ident| { - param_types.get(ident).ok_or_else(|| { - syn::Error::new_spanned(ident, "parameter not declared in the function") - }) +pub(crate) fn declared_params_to_field_types( + params: &[Ident], + param_types: &IndexMap, +) -> syn::Result> { + let params = params + .iter() + .map(|ident| { + param_types.get(ident).ok_or_else(|| { + syn::Error::new_spanned(ident, "parameter not declared in the function") }) - .map(|ty| type_to_field_type(ty?)) - .collect::>>()?; - Ok(params) - } + }) + .map(|ty| type_to_field_type(ty?)) + .collect::>>()?; + Ok(params) +} - fn result_to_field_types(&self, result: &Type) -> syn::Result> { - match &self.result { - ResultsKind::Infered => type_to_field_type(result).map(|x| vec![x]), - ResultsKind::Types(types) => { - let xs = types - .iter() - .map(type_to_field_type) - .collect::>>()?; - Ok(xs) - } +pub(crate) fn declared_result_type_to_field_types( + kind: &ResultsKind, + result: &Type, +) -> syn::Result> { + match kind { + ResultsKind::Infered => type_to_field_type(result).map(|x| vec![x]), + ResultsKind::Types(types) => { + let xs = types + .iter() + .map(type_to_field_type) + .collect::>>()?; + Ok(xs) } } } + pub enum ResultsKind { Infered, Types(Vec), @@ -781,9 +882,9 @@ pub(crate) struct SingleListener { pub listener: Type, pub evm_args: Option, pub listener_type: ListenerType, - pub event: Type, - pub post_processor: Option, - pub pre_processor: Option, + pub event: Option, + pub post_processor: Option, + pub pre_processor: Option, } /// Extracts a value from form: "tag = value" @@ -814,6 +915,9 @@ fn extract_x_equals_y( Ok(Some(listener)) } +const EVM_EVENT_LISTENER_TAG: &str = "EvmContractEventListener"; +const TANGLE_EVENT_LISTENER_TAG: &str = "TangleEventListener"; + impl Parse for EventListenerArgs { fn parse(input: ParseStream) -> syn::Result { let _ = input.parse::()?; @@ -823,37 +927,63 @@ impl Parse for EventListenerArgs { let mut listeners = Vec::new(); // Parse a TypePath instead of a LitStr while !content.is_empty() { - let listener = extract_x_equals_y::(&content, true, "listener")? - .expect("No listener defined in listener block"); - - let ty_str = quote! { #listener }.to_string(); + let mut listener = None; + let mut event = None; + let mut pre_processor = None; + let mut post_processor = None; let mut evm_args = None; - if ty_str.contains("EvmContractEventListener") { - evm_args = Some(content.parse::()?); - } + // TODO: Get rid of the needless nesting, this is a mess + while !content.is_empty() { + if content.peek(kw::listener) { + let listener_found = + extract_x_equals_y::(&content, true, "listener")? + .expect("No listener defined in listener block"); - let event = extract_x_equals_y::(&content, true, "event")? - .expect("No event defined in listener block"); - let pre_processor = - extract_x_equals_y::(&content, false, "pre_processor")?; - let post_processor = - extract_x_equals_y::(&content, false, "post_processor")?; + let ty_str = quote! { #listener_found }.to_string(); + if ty_str.contains(EVM_EVENT_LISTENER_TAG) { + evm_args = Some(content.parse::()?); + } + + listener = Some(listener_found) + } else if content.peek(kw::event) { + event = extract_x_equals_y::(&content, false, "event")? + } else if content.peek(kw::pre_processor) { + pre_processor = extract_x_equals_y::( + &content, + false, + "pre_processor", + )?; + } else if content.peek(kw::post_processor) { + post_processor = extract_x_equals_y::( + &content, + false, + "post_processor", + )?; + } else if content.peek(Token![,]) { + let _ = content.parse::()?; + } else { + return Err(content.error( + "Expected one of `listener`, `event`, `pre_processor`, `post_processor`", + )); + } + } + + let listener = listener.expect("No `listener` defined in listener block"); // Create a listener. If this is an EvmContractEventListener, we need to specially parse the arguments // In the case of tangle and everything other listener type, we don't pass evm_args let ty_str = quote! { #listener }.to_string(); - let this_listener = if ty_str.contains("EvmContractEventListener") { - assert!(evm_args.is_some(), "EvmArgs must be passed"); + let this_listener = if let Some(evm_args) = evm_args { SingleListener { listener, - evm_args, + evm_args: Some(evm_args), listener_type: ListenerType::Evm, event, post_processor, pre_processor, } } else { - let listener_type = if ty_str.contains("TangleEventListener") { + let listener_type = if ty_str.contains(TANGLE_EVENT_LISTENER_TAG) { ListenerType::Tangle } else { ListenerType::Custom @@ -889,15 +1019,12 @@ impl Parse for EventListenerArgs { } pub(crate) struct EvmArgs { - instance: Option, - event: Option, - event_converter: Option, - callback: Option, - abi: Option, + pub instance: Option, + pub abi: Option, } impl EventListenerArgs { - fn get_event_listener(&self) -> &SingleListener { + pub fn get_event_listener(&self) -> &SingleListener { &self.listeners[0] } @@ -958,6 +1085,7 @@ impl EventListenerArgs { pub fn get_param_name_tokenstream( &self, params: &[FieldType], + panic_on_decode_fail: bool, ) -> Vec { params .iter() @@ -966,7 +1094,9 @@ impl EventListenerArgs { let ident = format_ident!("param{i}"); let index = Index::from(i); match self.get_event_listener().listener_type { - ListenerType::Tangle => crate::tangle::field_type_to_param_token(&ident, t), + ListenerType::Tangle => { + crate::tangle::field_type_to_param_token(&ident, t, panic_on_decode_fail) + } ListenerType::Evm => { quote! { let #ident = inputs.#index; @@ -983,11 +1113,6 @@ impl EventListenerArgs { .collect::>() } - /// Returns true if on EVM - pub fn get_evm(&self) -> Option<&EvmArgs> { - self.get_event_listener().evm_args.as_ref() - } - pub fn has_tangle(&self) -> bool { self.listeners .iter() @@ -1002,45 +1127,11 @@ impl EventListenerArgs { /// Returns the Event Handler's Contract Instance on the EVM. pub fn instance(&self) -> Option { - match self.get_evm() { + match self.get_event_listener().evm_args.as_ref() { Some(EvmArgs { instance, .. }) => instance.clone(), None => None, } } - - /// Returns the contract instance's event to watch for on the EVM. - pub fn event(&self) -> Option { - match self.get_evm() { - Some(EvmArgs { event, .. }) => event.clone(), - None => None, - } - } - - /// Returns the Event Handler's event converter to convert the event into function arguments - pub fn event_converter(&self) -> Option { - match self.get_evm() { - Some(EvmArgs { - event_converter, .. - }) => event_converter.clone(), - None => None, - } - } - - /// Returns the callback to submit the job function output to. - pub fn callback(&self) -> Option { - match self.get_evm() { - Some(EvmArgs { callback, .. }) => callback.clone(), - None => None, - } - } - - /// Returns the ABI for the contract instance. - pub fn abi(&self) -> Option { - match self.get_evm() { - Some(EvmArgs { abi, .. }) => abi.clone(), - None => None, - } - } } impl Parse for EvmArgs { @@ -1049,9 +1140,6 @@ impl Parse for EvmArgs { syn::parenthesized!(content in input); let mut instance = None; - let mut event = None; - let mut event_converter = None; - let mut callback = None; let mut abi = None; while !content.is_empty() { @@ -1059,36 +1147,18 @@ impl Parse for EvmArgs { let _ = content.parse::()?; let _ = content.parse::()?; instance = Some(content.parse::()?); - } else if content.peek(kw::event) { - let _ = content.parse::()?; - let _ = content.parse::()?; - event = Some(content.parse::()?); - } else if content.peek(kw::event_converter) { - let _ = content.parse::()?; - let _ = content.parse::()?; - event_converter = Some(content.parse::()?); - } else if content.peek(kw::callback) { - let _ = content.parse::()?; - let _ = content.parse::()?; - callback = Some(content.parse::()?); + } else if content.peek(Token![,]) { + let _ = content.parse::()?; } else if content.peek(kw::abi) { let _ = content.parse::()?; let _ = content.parse::()?; abi = Some(content.parse::()?); - } else if content.peek(Token![,]) { - let _ = content.parse::()?; } else { return Err(content.error("Unexpected token")); } } - Ok(EvmArgs { - instance, - event, - event_converter, - callback, - abi, - }) + Ok(EvmArgs { instance, abi }) } } diff --git a/macros/blueprint-proc-macro/src/report.rs b/macros/blueprint-proc-macro/src/report.rs index ff702a524..b587ab1a9 100644 --- a/macros/blueprint-proc-macro/src/report.rs +++ b/macros/blueprint-proc-macro/src/report.rs @@ -1,11 +1,14 @@ -use crate::job::EventListenerArgs; +use crate::job::{ + declared_params_to_field_types, generate_autogen_struct, get_current_call_id_field_name, + get_job_id_field_name, get_result_type, EventListenerArgs, ResultsKind, +}; use crate::shared::{pascal_case, type_to_field_type}; use gadget_blueprint_proc_macro_core::{ FieldType, ReportDefinition, ReportMetadata, ReportResultVerifier, ReportType, }; +use indexmap::IndexMap; use proc_macro::TokenStream; use quote::{format_ident, quote}; -use std::collections::BTreeMap; use syn::{ parenthesized, parse::{Parse, ParseStream}, @@ -39,11 +42,10 @@ mod kw { /// /// Returns a `Result` containing the generated `TokenStream` or a `syn::Error`. pub(crate) fn report_impl(args: &ReportArgs, input: &ItemFn) -> syn::Result { - let fn_name = &input.sig.ident; - let fn_name_string = fn_name.to_string(); + let (fn_name_string, _job_def_name, job_id_name) = get_job_id_field_name(input); let report_def_name = format_ident!("{}_REPORT_DEF", fn_name_string.to_ascii_uppercase()); - - let mut param_types = BTreeMap::new(); + let _ = get_result_type(input); + let mut param_types = IndexMap::new(); for input in &input.sig.inputs { if let syn::FnArg::Typed(arg) = input { if let syn::Pat::Ident(pat_ident) = &*arg.pat { @@ -57,7 +59,7 @@ pub(crate) fn report_impl(args: &ReportArgs, input: &ItemFn) -> syn::Result syn::Result syn::Result syn::Result "JobReportEventHandler", + ReportType::QoS => "QoSReportEventHandler", + }; + + let (event_listener_gen, event_listener_calls) = + crate::job::generate_event_workflow_tokenstream( + input, + suffix, + &fn_name_string, + &args.event_listeners, + args.skip_codegen, + ¶m_types, + &args.params, + &result_type, + ); + + let autogen_struct = generate_autogen_struct( + input, + &args.event_listeners, + &args.params, + ¶m_types, + suffix, + &event_listener_calls, + ); + /*let event_handler_gen = if args.skip_codegen { proc_macro2::TokenStream::new() } else { - match args.report_type { - ReportType::Job => { - generate_job_report_event_handler(args, input, ¶ms_type, &result_type) - } - ReportType::QoS => { - generate_qos_report_event_handler(args, input, ¶ms_type, &result_type) - } + if let ReportType::QoS = args.report_type { + generate_qos_report_event_handler(args, input, ¶ms_type, &result_type) + } else { + proc_macro2::TokenStream::new() } + };*/ + + let call_id_static_name = get_current_call_id_field_name(input); + let job_id = if let Some(job_id) = job_id { + quote! { #job_id } + } else { + quote! { 0 } }; - let gen = quote! { + let job_const_block = quote! { #[doc = "Report definition for the function "] #[doc = #fn_name_string] pub const #report_def_name: &str = #report_def_str; + #[doc = "Job ID for the function "] + #[doc = "[`"] + #[doc = #fn_name_string] + #[doc = "`]"] + #[automatically_derived] + pub const #job_id_name: u8 = #job_id; + + static #call_id_static_name: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); + }; + + let gen = quote! { + #job_const_block + + #autogen_struct + + #[allow(unused_variables)] #input - #event_handler_gen + #(#event_listener_gen)* }; Ok(gen.into()) @@ -286,23 +333,6 @@ impl Parse for ReportArgs { } impl ReportArgs { - fn params_to_field_types( - &self, - param_types: &BTreeMap, - ) -> syn::Result> { - let params = self - .params - .iter() - .map(|ident| { - param_types.get(ident).ok_or_else(|| { - syn::Error::new_spanned(ident, "parameter not declared in the function") - }) - }) - .map(|ty| type_to_field_type(ty?)) - .collect::>>()?; - Ok(params) - } - fn result_to_field_types(&self, result: &Type) -> syn::Result> { match &self.result { ResultsKind::Infered => type_to_field_type(result).map(|x| vec![x]), @@ -317,101 +347,6 @@ impl ReportArgs { } } -/// Generates an event handler for job reports. -/// -/// This function creates a struct that listens for job result submissions -/// and triggers the report function when necessary. -/// -/// # Arguments -/// -/// * `args` - The parsed arguments from the `report` attribute macro. -/// * `input` - The function item that the `report` attribute is attached to. -/// * `param_types` - A map of parameter names to their types. -/// * `params` - The list of parameter field types. -/// * `result` - The list of result field types. -/// -/// # Returns -/// -/// Returns a `TokenStream` containing the generated event handler code. -fn generate_job_report_event_handler( - args: &ReportArgs, - input: &ItemFn, - _params: &[FieldType], - _result: &[FieldType], -) -> proc_macro2::TokenStream { - let fn_name = &input.sig.ident; - let fn_name_string = fn_name.to_string(); - const SUFFIX: &str = "JobReportEventHandler"; - let struct_name = format_ident!("{}{SUFFIX}", pascal_case(&fn_name_string)); - let job_id = args - .job_id - .as_ref() - .expect("Job ID must be present for job reports"); - let param_types = - crate::job::param_types(input).expect("Failed to generate param types for job report"); - let event_type = quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobResultSubmitted }; - let (event_listener_gen, event_listener_calls) = - crate::job::generate_event_listener_tokenstream( - input, - SUFFIX, - &fn_name_string, - &args.event_listeners, - args.skip_codegen, - ¶m_types, - &args.params, - ); - - let combined_event_listener = - crate::job::generate_combined_event_listener_selector(&struct_name); - quote! { - #[derive(Clone)] - pub struct #struct_name { - pub service_id: u64, - pub signer: gadget_sdk::keystore::TanglePairSigner, - pub client: gadget_sdk::clients::tangle::runtime::TangleClient, - } - - #(#event_listener_gen)* - - #[automatically_derived] - #[async_trait::async_trait] - impl gadget_sdk::events_watcher::substrate::EventHandler for #struct_name { - async fn handle(&self, event: &#event_type) -> Result>, gadget_sdk::events_watcher::Error> { - use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field; - // TODO: Implement parameter extraction and report function call - // This part will depend on the specific structure of your JobResultSubmitted event - // and how you want to handle the report function call - - Ok(vec![]) - } - - /// Returns the job ID - fn job_id(&self) -> u8 { - #job_id - } - - /// Returns the service ID - fn service_id(&self) -> u64 { - self.service_id - } - - fn signer(&self) -> &gadget_sdk::keystore::TanglePairSigner { - &self.signer - } - } - - #[async_trait::async_trait] - impl gadget_sdk::events_watcher::InitializableEventHandler for #struct_name { - async fn init_event_handler(&self) -> Option>> { - #(#event_listener_calls)* - #combined_event_listener - } - } - - impl gadget_sdk::event_listener::markers::IsTangle for #struct_name {} - } -} - /// Generates an event handler for `QoS` reports. /// /// This function creates a struct that periodically collects `QoS` metrics @@ -428,6 +363,7 @@ fn generate_job_report_event_handler( /// # Returns /// /// Returns a `TokenStream` containing the generated event handler code. +#[allow(dead_code)] fn generate_qos_report_event_handler( args: &ReportArgs, input: &ItemFn, @@ -437,15 +373,23 @@ fn generate_qos_report_event_handler( let fn_name = &input.sig.ident; let fn_name_string = fn_name.to_string(); const SUFFIX: &str = "QoSReportEventHandler"; + + let syn::ReturnType::Type(_, result) = &input.sig.output else { + panic!("Report function must have a return type of Result where T is a tuple of the result fields"); + }; + let struct_name = format_ident!("{}{SUFFIX}", pascal_case(&fn_name_string)); let job_id = quote! { 0 }; // We don't care about job ID's for QOS let param_types = crate::job::param_types(input).expect("Failed to generate param types for job report"); // TODO: Allow passing all events, use a dummy value here that satisfies the trait bounds. For now QOS will // trigger only once a singular JobCalled event is received. - let event_type = quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobCalled }; + let event_type = quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobResultSubmitted }; + let result_type = crate::job::declared_result_type_to_field_types(&args.result, result) + .expect("Failed to generate result types for job report"); + let (event_listener_gen, event_listener_calls) = - crate::job::generate_event_listener_tokenstream( + crate::job::generate_event_workflow_tokenstream( input, SUFFIX, &fn_name_string, @@ -453,6 +397,7 @@ fn generate_qos_report_event_handler( args.skip_codegen, ¶m_types, &args.params, + &result_type, ); let interval = args @@ -464,13 +409,6 @@ fn generate_qos_report_event_handler( crate::job::generate_combined_event_listener_selector(&struct_name); quote! { - #[derive(Clone)] - pub struct #struct_name { - pub service_id: u64, - pub signer: gadget_sdk::keystore::TanglePairSigner, - pub client: gadget_sdk::clients::tangle::runtime::TangleClient, - } - #(#event_listener_gen)* #[automatically_derived] @@ -526,20 +464,6 @@ fn generate_qos_report_event_handler( } } -pub enum ResultsKind { - Infered, - Types(Vec), -} - -impl std::fmt::Debug for ResultsKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Infered => write!(f, "Infered"), - Self::Types(_) => write!(f, "Types"), - } - } -} - #[derive(Debug)] struct Results(ResultsKind); diff --git a/macros/blueprint-proc-macro/src/shared.rs b/macros/blueprint-proc-macro/src/shared.rs index da1c28461..01955928d 100644 --- a/macros/blueprint-proc-macro/src/shared.rs +++ b/macros/blueprint-proc-macro/src/shared.rs @@ -1,4 +1,5 @@ use gadget_blueprint_proc_macro_core::FieldType; +use quote::ToTokens; use syn::{Ident, Type}; /// Convert a `snake_case` string to `PascalCase` @@ -111,6 +112,20 @@ pub fn path_to_field_type(path: &syn::Path) -> syn::Result { args, "unsupported parenthesized arguments", )), + // Support for SomeConcreteType where T, V, K is a simple type + syn::PathArguments::AngleBracketed(inner) if !inner.args.is_empty() => { + let mut ret = vec![]; + for inner_arg in &inner.args { + if let syn::GenericArgument::Type(inner_ty) = inner_arg { + let inner_type = type_to_field_type(inner_ty)?; + ret.push((inner_ty.to_token_stream().to_string(), Box::new(inner_type))) + } else { + return Err(syn::Error::new_spanned(inner_arg, "unsupported type param")); + } + } + Ok(FieldType::Struct(ident.to_string(), ret)) + } + syn::PathArguments::AngleBracketed(_) => { Err(syn::Error::new_spanned(args, "unsupported complex type")) } diff --git a/macros/blueprint-proc-macro/src/tangle/mod.rs b/macros/blueprint-proc-macro/src/tangle/mod.rs index 8308480dd..73ba526b8 100644 --- a/macros/blueprint-proc-macro/src/tangle/mod.rs +++ b/macros/blueprint-proc-macro/src/tangle/mod.rs @@ -3,72 +3,84 @@ use quote::{format_ident, quote}; use syn::Ident; #[allow(clippy::too_many_lines)] -pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::TokenStream { +pub fn field_type_to_param_token( + ident: &Ident, + t: &FieldType, + panic_on_decode_fail: bool, +) -> proc_macro2::TokenStream { + let else_block = if panic_on_decode_fail { + quote! { + panic!("Failed to decode the field"); + } + } else { + quote! { + return Ok(vec![]); + } + }; match t { FieldType::Void => unreachable!("void type should not be in params"), FieldType::Bool => { - quote! { let Some(Field::Bool(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bool(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Uint8 => { - quote! { let Some(Field::Uint8(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint8(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Int8 => { - quote! { let Some(Field::Int8(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int8(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Uint16 => { - quote! { let Some(Field::Uint16(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint16(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Int16 => { - quote! { let Some(Field::Int16(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int16(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Uint32 => { - quote! { let Some(Field::Uint32(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint32(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Int32 => { - quote! { let Some(Field::Int32(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int32(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Uint64 => { - quote! { let Some(Field::Uint64(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint64(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Int64 => { - quote! { let Some(Field::Int64(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int64(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Uint128 => { - quote! { let Some(Field::Uint128(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint128(#ident)) = args_iter.next() else { #else_block }; } } FieldType::U256 => { - quote! { let Some(Field::U256(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::U256(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Int128 => { - quote! { let Some(Field::Int128(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int128(#ident)) = args_iter.next() else { #else_block }; } } FieldType::Float64 => { - quote! { let Some(Field::Float64(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Float64(#ident)) = args_iter.next() else { #else_block }; } } FieldType::String => { let inner_ident = format_ident!("{}_inner", ident); quote! { - let Some(Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident)))) = args_iter.next() else { return Ok(vec![]); }; + let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident)))) = args_iter.next() else { #else_block }; // Convert the BoundedVec to a String let #ident = match String::from_utf8(#inner_ident) { Ok(s) => s, Err(e) => { ::gadget_sdk::warn!("failed to convert bytes to a valid utf8 string: {e}"); - use gadget_sdk::events_watcher::Error; - return Err(Error::Handler(Box::new(e))); + return Err(gadget_sdk::Error::Other(e.to_string())); } }; } } FieldType::Bytes => { - quote! { let Some(Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident))) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident))) = args_iter.next() else { #else_block }; } } FieldType::Optional(t_x) => { let inner_ident = format_ident!("{}_inner", ident); let x_ident = format_ident!("{}_option", ident); - let x_inner = field_type_to_param_token(&x_ident, t_x); + let x_inner = field_type_to_param_token(&x_ident, t_x, panic_on_decode_fail); let inner = quote! { - let Some(#inner_ident) = args_iter.next() else { return Ok(vec![]); }; + let Some(#inner_ident) = args_iter.next() else { #else_block; }; }; quote! { #inner @@ -77,22 +89,28 @@ pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::T #x_inner Some(#x_ident) }, - Field::None => None, + gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::None => None, }; } } FieldType::Array(_, _) => todo!("Handle array"), - FieldType::List(_) => { + FieldType::List(ty) => { let inner_ident = format_ident!("{}_inner", ident); let inner = quote! { - let Some(Field::List(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident))) = args_iter.next() else { return Ok(vec![]); }; + let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::List(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident))) = args_iter.next() else { #else_block; }; }; + let ty_variant_ident = format_ident!("{ty:?}"); + quote! { #inner let #ident = #inner_ident .into_iter() - .map(|item| item.0) + .map(|item| if let gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::<>:: #ty_variant_ident(val) = item { + val.0 + } else { + panic!("Failed to decode the list"); + }) .collect::>(); } } @@ -103,7 +121,8 @@ pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::T .map(|(field_name, field_type)| { let field_ident = format_ident!("{}", field_name); let inner_ident = format_ident!("{}_{}", ident, field_name); - let inner_token = field_type_to_param_token(&inner_ident, field_type); + let inner_token = + field_type_to_param_token(&inner_ident, field_type, panic_on_decode_fail); quote! { #inner_token #field_ident: #inner_ident, @@ -112,7 +131,7 @@ pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::T .collect(); quote! { - let Some(Field::Struct(#ident)) = args_iter.next() else { return Ok(vec![]); }; + let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Struct(#ident)) = args_iter.next() else { #else_block }; let mut #ident = #ident.into_iter(); #(#field_tokens)* let #ident = #struct_ident { @@ -122,7 +141,7 @@ pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::T } FieldType::AccountId => { - quote! { let Some(Field::AccountId(#ident)) = args_iter.next() else { return Ok(vec![]); }; } + quote! { let Some(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::AccountId(#ident)) = args_iter.next() else { #else_block }; } } } } @@ -130,24 +149,50 @@ pub fn field_type_to_param_token(ident: &Ident, t: &FieldType) -> proc_macro2::T pub fn field_type_to_result_token(ident: &Ident, t: &FieldType) -> proc_macro2::TokenStream { match t { FieldType::Void => quote! {}, - FieldType::Bool => quote! { result.push(Field::Bool(#ident)); }, - FieldType::Uint8 => quote! { result.push(Field::Uint8(#ident)); }, - FieldType::Int8 => quote! { result.push(Field::Int8(#ident)); }, - FieldType::Uint16 => quote! { result.push(Field::Uint16(#ident)); }, - FieldType::Int16 => quote! { result.push(Field::Int16(#ident)); }, - FieldType::Uint32 => quote! { result.push(Field::Uint32(#ident)); }, - FieldType::Int32 => quote! { result.push(Field::Int32(#ident)); }, - FieldType::Uint64 => quote! { result.push(Field::Uint64(#ident)); }, - FieldType::Int64 => quote! { result.push(Field::Int64(#ident)); }, - FieldType::Uint128 => quote! { result.push(Field::Uint128(#ident)); }, - FieldType::U256 => quote! { result.push(Field::U256(#ident)); }, - FieldType::Int128 => quote! { result.push(Field::Int128(#ident)); }, - FieldType::Float64 => quote! { result.push(Field::Float64(#ident)); }, + FieldType::Bool => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bool(#ident)); } + } + FieldType::Uint8 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint8(#ident)); } + } + FieldType::Int8 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int8(#ident)); } + } + FieldType::Uint16 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint16(#ident)); } + } + FieldType::Int16 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int16(#ident)); } + } + FieldType::Uint32 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint32(#ident)); } + } + FieldType::Int32 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int32(#ident)); } + } + FieldType::Uint64 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint64(#ident)); } + } + FieldType::Int64 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int64(#ident)); } + } + FieldType::Uint128 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint128(#ident)); } + } + FieldType::U256 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::U256(#ident)); } + } + FieldType::Int128 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int128(#ident)); } + } + FieldType::Float64 => { + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Float64(#ident)); } + } FieldType::String => { - quote! { result.push(Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident.into_bytes())))); } + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident.into_bytes())))); } } FieldType::Bytes => { - quote! { result.push(Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident))); } + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#ident))); } } FieldType::Optional(t_x) => { let v_ident = format_ident!("v"); @@ -155,7 +200,7 @@ pub fn field_type_to_result_token(ident: &Ident, t: &FieldType) -> proc_macro2:: quote! { match #ident { Some(v) => #tokens, - None => result.push(Field::None), + None => result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::None), } } } @@ -164,30 +209,58 @@ pub fn field_type_to_result_token(ident: &Ident, t: &FieldType) -> proc_macro2:: let inner_ident = format_ident!("{}_inner", ident); let field = match **t_x { FieldType::Void => unreachable!(), - FieldType::Bool => quote! { Field::Bool(item) }, - FieldType::Uint8 => quote! { Field::Uint8(item) }, - FieldType::Int8 => quote! { Field::Int8(item) }, - FieldType::Uint16 => quote! { Field::Uint16(item) }, - FieldType::Int16 => quote! { Field::Int16(item) }, - FieldType::Uint32 => quote! { Field::Uint32(item) }, - FieldType::Int32 => quote! { Field::Int32(item) }, - FieldType::Uint64 => quote! { Field::Uint64(item) }, - FieldType::Int64 => quote! { Field::Int64(item) }, - FieldType::Uint128 => quote! { Field::Uint128(item) }, - FieldType::Int128 => quote! { Field::Int128(item) }, - FieldType::U256 => quote! { Field::U256(item) }, - FieldType::Float64 => quote! { Field::Float64(item) }, + FieldType::Bool => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bool(item) } + } + FieldType::Uint8 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint8(item) } + } + FieldType::Int8 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int8(item) } + } + FieldType::Uint16 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint16(item) } + } + FieldType::Int16 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int16(item) } + } + FieldType::Uint32 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint32(item) } + } + FieldType::Int32 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int32(item) } + } + FieldType::Uint64 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint64(item) } + } + FieldType::Int64 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int64(item) } + } + FieldType::Uint128 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Uint128(item) } + } + FieldType::Int128 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Int128(item) } + } + FieldType::U256 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::U256(item) } + } + FieldType::Float64 => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Float64(item) } + } FieldType::String => { - quote! { Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(item.into_bytes()))) } + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::String(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(item.into_bytes()))) } } FieldType::Bytes => { - quote! { Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(item)) } + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Bytes(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(item)) } } FieldType::Optional(_) => todo!("handle optionals into lists"), FieldType::Array(_, _) => todo!("handle arrays into lists"), FieldType::List(_) => todo!("handle nested lists"), FieldType::Struct(_, _) => todo!("handle nested structs"), - FieldType::AccountId => quote! { Field::AccountId(item) }, + FieldType::AccountId => { + quote! { gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::AccountId(item) } + } }; let inner = quote! { let #inner_ident = #ident.into_iter().map(|item| #field).collect::>(); @@ -195,7 +268,7 @@ pub fn field_type_to_result_token(ident: &Ident, t: &FieldType) -> proc_macro2:: quote! { #inner - result.push(Field::List(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident))); + result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::List(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(#inner_ident))); } } FieldType::Struct(name, fields) => { @@ -219,11 +292,11 @@ pub fn field_type_to_result_token(ident: &Ident, t: &FieldType) -> proc_macro2:: #(#field_tokens)* let struct_name = gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::BoundedString::::from(#name); let fields_vec = vec![#(#field_tokens),*]; - result.push(Field::Struct(struct_name, gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(fields_vec))); + result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::Struct(struct_name, gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec(fields_vec))); } } FieldType::AccountId => { - quote! { result.push(Field::AccountId(#ident)); } + quote! { result.push(gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field::AccountId(#ident)); } } } } diff --git a/macros/playground/Cargo.toml b/macros/playground/Cargo.toml index e9e44a666..a9037a5d8 100644 --- a/macros/playground/Cargo.toml +++ b/macros/playground/Cargo.toml @@ -25,5 +25,10 @@ async-trait = { workspace = true } tracing = { workspace = true } gadget-sdk = { workspace = true, features = ["default"] } +[dev-dependencies] +reqwest = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } + [build-dependencies] -blueprint-metadata = { workspace = true } +blueprint-metadata = { workspace = true } \ No newline at end of file diff --git a/macros/playground/src/lib.rs b/macros/playground/src/lib.rs index e8133c5ff..543eb8a17 100644 --- a/macros/playground/src/lib.rs +++ b/macros/playground/src/lib.rs @@ -1,4 +1,10 @@ #![allow(dead_code)] +use gadget_sdk::clients::tangle::runtime::TangleClient; +use gadget_sdk::event_listener::tangle::jobs::{services_post_processor, services_pre_processor}; +use gadget_sdk::event_listener::tangle::TangleEventListener; +use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::{ + JobCalled, JobResultSubmitted, +}; use gadget_sdk::{benchmark, job, registration_hook, report, request_hook}; #[derive(Debug, Clone, Copy)] @@ -28,9 +34,9 @@ pub struct MyContext; // ================== /// Simple Threshold (t) Keygen Job for n parties. -#[job(id = 0, params(n, t), event_listener(listener = TangleEventListener, event = JobCalled), result(_))] -pub fn keygen(ctx: &MyContext, n: u16, t: u16) -> Result, Error> { - let _ = (n, t, ctx); +#[job(id = 0, params(n, t), event_listener(listener = TangleEventListener, pre_processor = services_pre_processor), result(_))] +pub fn keygen(context: TangleClient, n: u16, t: u16) -> Result, Error> { + let _ = (n, t, context); Ok(vec![0; 33]) } @@ -38,10 +44,10 @@ pub fn keygen(ctx: &MyContext, n: u16, t: u16) -> Result, Error> { #[job( id = 1, params(keygen_id, data), - event_listener(listener = TangleEventListener, event = JobCalled), + event_listener(listener = TangleEventListener, pre_processor = services_pre_processor), result(_) )] -pub async fn sign(keygen_id: u64, data: Vec) -> Result, Error> { +pub async fn sign(context: TangleClient, keygen_id: u64, data: Vec) -> Result, Error> { let _ = (keygen_id, data); Ok(vec![0; 65]) } @@ -49,17 +55,31 @@ pub async fn sign(keygen_id: u64, data: Vec) -> Result, Error> { #[job( id = 2, params(keygen_id, new_t), - event_listener(listener = TangleEventListener, event = JobCalled), + event_listener( + listener = TangleEventListener, + pre_processor = services_pre_processor, + post_processor = services_post_processor, + ), result(_) )] -pub fn refresh(keygen_id: u64, new_t: Option) -> Result, Error> { +pub fn refresh( + context: TangleClient, + keygen_id: u64, + new_t: Option, +) -> Result, Error> { let _ = (keygen_id, new_t); Ok(vec![0; 33]) } /// Say hello to someone or the world. -#[job(id = 3, params(who), event_listener(listener = TangleEventListener, event = JobCalled), result(_))] -pub fn say_hello(who: Option) -> Result { +#[job(id = 3, params(who), + event_listener( + listener = TangleEventListener, + pre_processor = services_pre_processor, + post_processor = services_post_processor, + ), + result(_))] +pub fn say_hello(context: TangleClient, who: Option) -> Result { match who { Some(who) => Ok(format!("Hello, {}!", who)), None => Ok("Hello, World!".to_string()), @@ -84,36 +104,50 @@ pub fn on_request(nft_id: u64); #[report( job_id = 0, params(n, t, msgs), - event_listener(listener = TangleEventListener, event = JobCalled), + event_listener( + listener = TangleEventListener, + pre_processor = services_pre_processor, + post_processor = services_post_processor, + ), result(_), report_type = "job", verifier(evm = "KeygenContract") )] -fn report_keygen(n: u16, t: u16, msgs: Vec>) -> u32 { +fn report_keygen( + context: TangleClient, + n: u16, + t: u16, + msgs: Vec>, +) -> Result { let _ = (n, t, msgs); - 0 + Ok(0) } #[report( params(uptime, response_time, error_rate), - event_listener(listener = TangleEventListener, event = JobCalled), - result(Vec), + event_listener(listener = TangleEventListener, pre_processor = services_pre_processor,), + result(_), report_type = "qos", interval = 3600, metric_thresholds(uptime = 99, response_time = 1000, error_rate = 5) )] -fn report_service_health(uptime: f64, response_time: u64, error_rate: f64) -> Vec { +fn report_service_health( + context: TangleClient, + uptime: u64, + response_time: u64, + error_rate: u64, +) -> Result, gadget_sdk::Error> { let mut issues = Vec::new(); - if uptime < 99.0 { + if uptime < 99 { issues.push(b"Low uptime".to_vec()); } if response_time > 1000 { issues.push(b"High response time".to_vec()); } - if error_rate > 5.0 { + if error_rate > 5 { issues.push(b"High error rate".to_vec()); } - issues.concat() + Ok(issues.concat()) } // ================== @@ -123,13 +157,23 @@ fn report_service_health(uptime: f64, response_time: u64, error_rate: f64) -> Ve fn keygen_2_of_3() { let n = 3; let t = 2; - let result = keygen(&MyContext, n, t); + let result = keygen(&TangleClient, n, t); assert!(result.is_ok()); } #[cfg(test)] mod tests { + use async_trait::async_trait; use gadget_sdk as sdk; + use gadget_sdk::job_runner::MultiJobRunner; + use sdk::event_listener::periodic::PeriodicEventListener; + use sdk::event_listener::EventListener; + use sdk::job; + use std::convert::Infallible; + use std::sync::atomic::AtomicUsize; + use std::sync::Arc; + use std::time::Duration; + #[test] fn generated_blueprint() { eprintln!("{}", super::KEYGEN_JOB_DEF); @@ -187,11 +231,137 @@ mod tests { } } + #[job( + id = 0, + params(value), + result(_), + event_listener( + listener = PeriodicEventListener<1500, WebPoller, serde_json::Value, Arc>, + pre_processor = pre_process, + post_processor = post_process, + ), + )] + // Maps a boolean value obtained from pre-processing to a u8 value + pub async fn web_poller(value: bool, count: Arc) -> Result { + gadget_sdk::info!("Running web_poller on value: {value}"); + Ok(value as u8) + } + + async fn pre_process(event: serde_json::Value) -> Result { + gadget_sdk::info!("Running web_poller pre-processor on value: {event}"); + let completed = event["completed"].as_bool().unwrap_or(false); + Ok(completed) + } + + async fn post_process(job_output: u8) -> Result<(), gadget_sdk::Error> { + gadget_sdk::info!("Running web_poller post-processor on value: {job_output}"); + if job_output == 1 { + Ok(()) + } else { + Err(gadget_sdk::Error::Other( + "Job failed since query returned with a false status".to_string(), + )) + } + } + + /// Define an event listener that polls a webserver + pub struct WebPoller { + pub client: reqwest::Client, + pub count: Arc, + } + + #[async_trait] + impl EventListener> for WebPoller { + async fn new(context: &Arc) -> Result + where + Self: Sized, + { + let client = reqwest::Client::new(); + Ok(Self { + client, + count: context.clone(), + }) + } + + /// Implement the logic that polls the web server + async fn next_event(&mut self) -> Option { + // Send a GET request to the JSONPlaceholder API + let response = self + .client + .get("https://jsonplaceholder.typicode.com/todos/10") + .send() + .await + .ok()?; + + // Check if the request was successful + if response.status().is_success() { + // Parse the JSON response + let resp: serde_json::Value = response.json().await.ok()?; + self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Some(resp) + } else { + None + } + } + + /// Implement any handler logic when an event is received + async fn handle_event( + &mut self, + _event: serde_json::Value, + ) -> Result<(), gadget_sdk::Error> { + unreachable!("Not called here") + } + } + + #[tokio::test] + async fn test_web_poller_event_workflow_works() { + gadget_sdk::logging::setup_log(); + let count = &Arc::new(AtomicUsize::new(0)); + let job = WebPollerEventHandler { + count: count.clone(), + }; + + let task0 = async move { + MultiJobRunner::new(None) + .with_job() + .finish(job) + .run() + .await + .expect("Job failed"); + }; + + let periodic_poller = async move { + loop { + if count.load(std::sync::atomic::Ordering::SeqCst) > 3 { + break; + } + + tokio::time::sleep(Duration::from_millis(500)).await + } + }; + + tokio::select! { + _ = task0 => { + panic!("Should not occur") + }, + _ = periodic_poller => { + assert!(count.load(std::sync::atomic::Ordering::SeqCst) > 3); + }, + } + } + fn setup_env() { - // TODO: Add all GadgetContext vars into the env + // Add required env vars for all child processes/gadgets + let env_vars = [ + ("RPC_URL".to_string(), "ws://127.0.0.1".to_string()), + ("KEYSTORE_URI".to_string(), "/".to_string()), + ("BLUEPRINT_ID".to_string(), 0.to_string()), + ("SERVICE_ID".to_string(), 0.to_string()), + ("DATA_DIR".to_string(), "/".to_string()), + ]; + + for (key, value) in env_vars { + std::env::set_var(key, value); + } } - // #[test] - // fn example_benchmark() { - // super::keygen_2_of_3_benchmark(); - // } } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 30c4c86fa..cd716fa41 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -85,6 +85,7 @@ tokio-retry = { workspace = true } # Macros gadget-blueprint-proc-macro = { workspace = true, default-features = false } gadget-context-derive = { workspace = true, default-features = false } +gadget-blueprint-proc-macro-core = { workspace = true, default-features = false } # Benchmarking deps sysinfo = { workspace = true } diff --git a/sdk/src/error.rs b/sdk/src/error.rs index d613e73c3..cb5b92c46 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -54,6 +54,8 @@ pub enum Error { Metrics(#[from] crate::metrics::Error), #[error("Io error: {0}")] IoError(#[from] std::io::Error), + #[error("The type has been skipped in the preprocessor")] + SkipPreProcessedType, #[error("Other error: {0}")] Other(String), } diff --git a/sdk/src/event_listener/executor.rs b/sdk/src/event_listener/executor.rs index 0ab1ad772..fc8625019 100644 --- a/sdk/src/event_listener/executor.rs +++ b/sdk/src/event_listener/executor.rs @@ -58,9 +58,16 @@ where async fn event_loop(&mut self) -> Result<(), crate::Error> { // TODO: add exponential backoff logic here while let Some(event) = self.next_event().await { - let preprocessed_event = self.pre_process(event).await?; - let job_output = self.process(preprocessed_event).await?; - self.post_process(job_output).await?; + match self.pre_process(event).await { + Ok(preprocessed_event) => { + let job_output = self.process(preprocessed_event).await?; + self.post_process(job_output).await?; + } + Err(crate::Error::SkipPreProcessedType) => {} + Err(e) => { + return Err(e); + } + } } Err(crate::Error::Other( diff --git a/sdk/src/event_listener/mod.rs b/sdk/src/event_listener/mod.rs index 4e6e6fe3d..f8ab56b49 100644 --- a/sdk/src/event_listener/mod.rs +++ b/sdk/src/event_listener/mod.rs @@ -7,7 +7,7 @@ pub mod evm_contracts; pub mod executor; pub mod markers; pub mod periodic; -pub mod tangle_events; +pub mod tangle; /// The [`EventListener`] trait defines the interface for event listeners. #[async_trait] diff --git a/sdk/src/event_listener/tangle/jobs.rs b/sdk/src/event_listener/tangle/jobs.rs new file mode 100644 index 000000000..148681335 --- /dev/null +++ b/sdk/src/event_listener/tangle/jobs.rs @@ -0,0 +1,124 @@ +use crate::clients::tangle::runtime::TangleClient; +use crate::event_listener::tangle::{BlockNumber, TangleEvent}; +use crate::Error; +use subxt_core::events::StaticEvent; +use subxt_core::utils::AccountId32; +use tangle_subxt::tangle_testnet_runtime::api; +use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::field::Field; +use tangle_subxt::tangle_testnet_runtime::api::services::calls::types; +use tangle_subxt::tangle_testnet_runtime::api::services::calls::types::call::{Job, ServiceId}; +use tangle_subxt::tangle_testnet_runtime::api::services::events::{ + job_called, JobCalled, JobResultSubmitted, +}; + +pub trait ServicesJobPalletItem: StaticEvent { + fn call_id(&self) -> job_called::CallId; + fn job_id(&self) -> Job; + fn service_id(&self) -> ServiceId; + fn args(self) -> Option>> { + None + } +} + +impl ServicesJobPalletItem for JobCalled { + fn call_id(&self) -> job_called::CallId { + self.call_id + } + + fn job_id(&self) -> Job { + self.job + } + + fn service_id(&self) -> ServiceId { + self.service_id + } + + fn args(self) -> Option>> { + Some(self.args) + } +} + +impl ServicesJobPalletItem for JobResultSubmitted { + fn call_id(&self) -> job_called::CallId { + self.call_id + } + + fn job_id(&self) -> Job { + self.job + } + + fn service_id(&self) -> ServiceId { + self.service_id + } +} + +pub async fn services_pre_processor( + event: TangleEvent, +) -> Result, Error> { + let this_service_id = event.service_id; + let this_job_id = event.job_id; + crate::info!("Pre-processing event for sid/bid = {this_service_id}/{this_job_id} ..."); + if let Ok(Some(evt)) = event.evt.as_event::() { + let service_id = evt.service_id(); + let job = evt.job_id(); + let call_id = evt.call_id(); + let args = evt.args().unwrap_or_default(); + + if job == this_job_id && service_id == this_service_id { + crate::info!("Found actionable event for sid/bid = {service_id}/{job} ..."); + return Ok(TangleJobEvent { + args, + context: event.context, + block_number: event.block_number, + signer: event.signer, + client: event.client, + call_id: Some(call_id), + job_id: this_job_id, + service_id: this_service_id, + }); + } + } + + Err(Error::SkipPreProcessedType) +} + +/// By default, the tangle post-processor takes in a job result and submits the result on-chain +pub async fn services_post_processor( + TangleJobResult { + results, + service_id, + call_id, + client, + signer, + }: TangleJobResult, +) -> Result<(), Error> { + crate::info!("Submitting result on-chain for service {service_id} call_id {call_id} ..."); + let response = api::tx() + .services() + .submit_result(service_id, call_id, results); + let _ = crate::tx::tangle::send(&client, &signer, &response) + .await + .map_err(|err| Error::Client(err.to_string()))?; + crate::info!("Submitted result on-chain"); + Ok(()) +} + +pub struct TangleJobResult { + pub results: types::submit_result::Result, + pub service_id: ServiceId, + pub call_id: job_called::CallId, + pub client: TangleClient, + pub signer: crate::keystore::TanglePairSigner, +} + +// TODO: Move up this a module to make call_id generic +pub struct TangleJobEvent { + pub args: Vec>, + pub context: Ctx, + pub client: TangleClient, + pub signer: crate::keystore::TanglePairSigner, + pub block_number: BlockNumber, + pub call_id: Option, + pub job_id: Job, + pub service_id: ServiceId, +} diff --git a/sdk/src/event_listener/tangle/mod.rs b/sdk/src/event_listener/tangle/mod.rs new file mode 100644 index 000000000..4995ab285 --- /dev/null +++ b/sdk/src/event_listener/tangle/mod.rs @@ -0,0 +1,182 @@ +use crate::clients::tangle::runtime::{TangleClient, TangleConfig}; +use crate::event_listener::markers::IsTangle; +use crate::event_listener::EventListener; +use crate::Error; +use async_trait::async_trait; +use gadget_blueprint_proc_macro_core::FieldType; +use sp_core::crypto::AccountId32; +use std::collections::VecDeque; +use std::marker::PhantomData; +use subxt::backend::StreamOfResults; +use subxt_core::events::EventDetails; +use tangle_subxt::tangle_testnet_runtime::api::services::calls::types::call::{Job, ServiceId}; +use tokio::sync::Mutex; + +pub mod jobs; + +pub struct TangleEventListener { + current_block: Option, + job_id: Job, + service_id: ServiceId, + listener: Mutex>>, + context: Ctx, + signer: crate::keystore::TanglePairSigner, + client: TangleClient, + enqueued_events: VecDeque>, + _pd: PhantomData, +} + +pub type BlockNumber = u32; + +#[derive(Clone)] +pub struct TangleListenerInput { + pub client: TangleClient, + pub job_id: Job, + pub service_id: ServiceId, + pub signer: crate::keystore::TanglePairSigner, + pub context: Ctx, +} + +/// Emitted by the [`TangleEventListener`] when a new event is received. +/// +/// Root events are preferred to be used as the Evt, as then the application can +/// sort through a series of events to find the ones it is interested in for +/// pre-processing. +pub struct TangleEvent { + pub evt: EventDetails, + pub context: Ctx, + pub block_number: BlockNumber, + pub signer: crate::keystore::TanglePairSigner, + pub client: TangleClient, + pub job_id: Job, + pub service_id: ServiceId, + _pd: PhantomData, +} + +impl IsTangle for TangleEventListener {} + +#[async_trait] +impl + EventListener, TangleListenerInput> + for TangleEventListener +{ + async fn new(context: &TangleListenerInput) -> Result + where + Self: Sized, + { + let TangleListenerInput { + client, + job_id, + service_id, + context, + signer, + } = context; + + let listener = Mutex::new(client.blocks().subscribe_finalized().await?); + Ok(Self { + listener, + current_block: None, + job_id: *job_id, + service_id: *service_id, + context: context.clone(), + client: client.clone(), + signer: signer.clone(), + enqueued_events: VecDeque::new(), + _pd: PhantomData, + }) + } + + async fn next_event(&mut self) -> Option> { + loop { + if let Some(evt) = self.enqueued_events.pop_front() { + return Some(TangleEvent { + evt, + context: self.context.clone(), + signer: self.signer.clone(), + block_number: self.current_block?, + client: self.client.clone(), + job_id: self.job_id, + service_id: self.service_id, + _pd: PhantomData, + }); + } + + let next_events = self.listener.get_mut().next().await?.ok()?; + let block_number = next_events.number(); + self.current_block = Some(block_number); + + let next_events = next_events.events().await.ok()?; + let mut root_events = next_events.iter().flatten().collect::>(); + + crate::info!("Found {} possible events ...", root_events.len()); + + if let Some(evt) = root_events.pop_front() { + if !root_events.is_empty() { + // Store for the next iteration; we can override this since we know + // by this point in the code there are no more events to process in + // the queue + self.enqueued_events = root_events; + } + + return Some(TangleEvent { + evt, + context: self.context.clone(), + signer: self.signer.clone(), + block_number, + client: self.client.clone(), + job_id: self.job_id, + service_id: self.service_id, + _pd: PhantomData, + }); + } + } + } + + async fn handle_event(&mut self, _event: TangleEvent) -> Result<(), Error> { + unimplemented!("placeholder; will be removed") + } +} + +pub trait FieldTypeToValue: Sized { + fn to_value(&self, field_type: FieldType) -> Self; +} + +macro_rules! impl_field_type_to_value { + ($($t:ty => $f:pat),*) => { + $( + impl FieldTypeToValue for $t { + fn to_value(&self, field_type: FieldType) -> Self { + match field_type { + $f => self.clone(), + _ => panic!("Invalid field type!"), + } + } + } + )* + }; +} + +impl_field_type_to_value!( + u8 => FieldType::Uint8, + u16 => FieldType::Uint16, + u32 => FieldType::Uint32, + u64 => FieldType::Uint64, + i8 => FieldType::Int8, + i16 => FieldType::Int16, + i32 => FieldType::Int32, + i64 => FieldType::Int64, + u128 => FieldType::Uint128, + i128 => FieldType::Int128, + f64 => FieldType::Float64, + bool => FieldType::Bool, + AccountId32 => FieldType::AccountId +); + +impl FieldTypeToValue for String { + fn to_value(&self, field_type: FieldType) -> Self { + match field_type { + FieldType::String => self.clone(), + _ => panic!("Invalid field type!"), + } + } +} diff --git a/sdk/src/event_listener/tangle_events.rs b/sdk/src/event_listener/tangle_events.rs deleted file mode 100644 index 18604f8a9..000000000 --- a/sdk/src/event_listener/tangle_events.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::clients::tangle::runtime::{TangleClient, TangleConfig}; -use crate::events_watcher::substrate::EventHandlerFor; -use crate::Error; -use async_trait::async_trait; -use subxt::backend::StreamOfResults; -use subxt::OnlineClient; -use subxt_core::events::StaticEvent; -use tangle_subxt::tangle_testnet_runtime::api::services::events::{ - job_called, JobCalled, JobResultSubmitted, -}; -use tokio::sync::Mutex; -use tokio_retry::Retry; - -use crate::event_listener::get_exponential_backoff; -use crate::event_listener::EventListener; - -pub trait HasServiceAndJobId: StaticEvent + Send + Sync + 'static { - fn service_id(&self) -> u64; - fn job_id(&self) -> u8; - fn id_of_call(&self) -> job_called::CallId; -} - -impl HasServiceAndJobId for JobCalled { - fn service_id(&self) -> u64 { - self.service_id - } - - fn job_id(&self) -> u8 { - self.job - } - - fn id_of_call(&self) -> job_called::CallId { - self.call_id - } -} - -impl HasServiceAndJobId for JobResultSubmitted { - fn service_id(&self) -> u64 { - self.service_id - } - - fn job_id(&self) -> u8 { - self.job - } - - fn id_of_call(&self) -> job_called::CallId { - self.call_id - } -} - -pub struct TangleEventWrapper { - handler: EventHandlerFor, - client: OnlineClient, - current_block: Option, - listener: - Mutex>>>, -} - -pub type TangleEventListener = TangleEventWrapper; -pub type TangleJobEventListener = TangleEventWrapper; - -pub type TangleEventWrapperContext = (TangleClient, EventHandlerFor); -#[async_trait] -impl - EventListener, TangleEventWrapperContext> for TangleEventWrapper -{ - async fn new(ctx: &TangleEventWrapperContext) -> Result - where - Self: Sized, - { - let (client, handler) = ctx; - let listener = Mutex::new(client.blocks().subscribe_finalized().await?); - Ok(Self { - listener, - client: client.clone(), - current_block: None, - handler: handler.clone(), - }) - } - - async fn next_event(&mut self) -> Option> { - loop { - let next_block = self.listener.get_mut().next().await?.ok()?; - let block_id = next_block.number(); - self.current_block = Some(block_id); - let events = next_block.events().await.ok()?; - for _evt in events.iter().flatten() { - crate::info!( - "Event found || required: sid={}, jid={}", - self.handler.service_id(), - self.handler.job_id() - ); - } - - let events = events - .find::() - .flatten() - .filter(|event| { - event.service_id() == self.handler.service_id() - && event.job_id() == self.handler.job_id() - }) - .collect::>(); - - if !events.is_empty() { - return Some(events); - } - } - } - - async fn handle_event(&mut self, job_events: Vec) -> Result<(), Error> { - use crate::tangle_subxt::tangle_testnet_runtime::api as TangleApi; - const MAX_RETRY_COUNT: usize = 5; - crate::info!("Handling actionable events ..."); - crate::info!( - "Handling {} JobCalled Events @ block {}", - job_events.len(), - self.current_block.unwrap_or_default() - ); - - let mut tasks = Vec::new(); - for call in job_events.iter() { - let handler = &self.handler; - let client = &self.client; - let service_id = call.service_id(); - let call_id = call.id_of_call(); - let signer = handler.signer().clone(); - - let task = async move { - let backoff = get_exponential_backoff::(); - - Retry::spawn(backoff, || async { - let result = handler.handle(call).await?; - let response = TangleApi::tx() - .services() - .submit_result(service_id, call_id, result); - let _ = crate::tx::tangle::send(client, &signer, &response) - .await - .map_err(|err| Error::Client(err.to_string()))?; - Ok::<_, Error>(()) - }) - .await - }; - - tasks.push(task); - } - - let results = futures::future::join_all(tasks).await; - // this event will be marked as handled if at least one handler succeeded. - // this because, for the failed events, we already tried to handle them - // many times (at this point), and there is no point in trying again. - let mark_as_handled = results.iter().any(Result::is_ok); - // also, for all the failed event handlers, we should print what went - // wrong. - for r in &results { - if let Err(e) = r { - crate::error!("Error from result: {e:?}"); - } - } - - if mark_as_handled { - crate::info!( - "event handled successfully at block {}", - self.current_block.unwrap_or_default() - ); - } else { - crate::error!("Error while handling event, all handlers failed."); - crate::warn!("Restarting event watcher ..."); - // this a transient error, so we will retry again. - return Ok(()); - } - - Ok(()) - } - - async fn execute(&mut self) -> Result<(), Error> { - const MAX_RETRY_COUNT: usize = 10; - let mut backoff = get_exponential_backoff::(); - - let mut retry_count = 0; - loop { - match self.run_event_loop().await { - Ok(_) => break Ok(()), - Err(e) => { - if retry_count >= MAX_RETRY_COUNT { - break Err(e); - } - retry_count += 1; - tokio::time::sleep(backoff.nth(retry_count).unwrap()).await; - } - } - } - } -} - -impl TangleEventWrapper { - async fn run_event_loop(&mut self) -> Result<(), Error> { - while let Some(events) = self.next_event().await { - self.handle_event(events).await?; - } - - crate::warn!("Event listener has stopped"); - Err(Error::Other("Event listener has stopped".to_string())) - } -} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 0fd27cb4e..0964ed97a 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -78,6 +78,7 @@ pub use structopt; pub use subxt_core; pub use tangle_subxt; pub use tokio; +pub use uuid; // External modules usually used in proc-macro codegen. #[doc(hidden)] From bb80edb0c8e662f81b35b4303609c658317cc117 Mon Sep 17 00:00:00 2001 From: Alex <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:25:03 -0400 Subject: [PATCH 3/3] feat(cargo-tangle)!: rename "gadget" to "blueprint" (#384) * feat(cargo-tangle)!: rename "gadget" to "blueprint" * chore: update commands in README --- README.md | 6 +++--- cli/README.md | 15 +++++++++------ cli/src/main.rs | 9 ++++++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ed4cf0a62..e2eb7b35e 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ cargo install cargo-tangle --git https://github.com/tangle-network/gadget --forc To create a new blueprint/gadget using the Tangle CLI: ```bash -cargo tangle gadget create --name +cargo tangle blueprint create --name ``` ### Deploying a Blueprint @@ -62,7 +62,7 @@ Finally, the blueprint can be deployed to a local Tangle node using the followin ```bash export SIGNER="//Alice" # Substrate Signer account export EVM_SIGNER="0xcb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854" # EVM signer account -cargo tangle gadget deploy --rpc-url --package +cargo tangle blueprint deploy --rpc-url --package ``` More information on this process can be found in the [CLI documentation](./cli/README.md) @@ -83,7 +83,7 @@ cargo test --package blueprint-test-utils tests_standard::test_externalities_gad Since testing is in beta stage, each time the blueprint is run, you must cancel the testnet and restart it to ensure storage is reset. All these nuances and manual requirement of setting up a testnet will be resolved in the near future and will be -testable via `cargo tangle gadget test` +testable via `cargo tangle blueprint test` ## License diff --git a/cli/README.md b/cli/README.md index d943bf164..98e4f8f91 100644 --- a/cli/README.md +++ b/cli/README.md @@ -13,7 +13,9 @@ Create and Deploy blueprints on Tangle Network. ## Overview -The Tangle CLI is a command-line tool that allows you to create and deploy blueprints/gadgets on the Tangle network. It provides a simple and efficient way to manage your blueprints and gadgets, making it easy to get started with Tangle Blueprints. +The Tangle CLI is a command-line tool that allows you to create and deploy blueprints/gadgets on the Tangle network. It +provides a simple and efficient way to manage your blueprints and gadgets, making it easy to get started with Tangle +Blueprints. ## Installation @@ -36,7 +38,7 @@ cargo install cargo-tangle --git https://github.com/tangle-network/gadget --forc To create a new blueprint/gadget using the Tangle CLI, use the following command: ```bash -cargo tangle gadget create --name +cargo tangle blueprint create --name ``` Replace `` with the desired name for your blueprint. @@ -44,7 +46,7 @@ Replace `` with the desired name for your blueprint. ### Example ```bash -cargo tangle gadget create --name my_blueprint +cargo tangle blueprint create --name my_blueprint ``` ## Build The Blueprint and the Gadget @@ -70,15 +72,16 @@ To deploy the blueprint to a local Tangle node, use the following command: ```bash export SIGNER="//Alice" # Substrate Signer account export EVM_SIGNER="0xcb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854" # EVM signer account -cargo tangle gadget deploy --rpc-url --package +cargo tangle blueprint deploy --rpc-url --package ``` -Replace `` with the RPC URL of your local Tangle node and `` with the name of the package to deploy. +Replace `` with the RPC URL of your local Tangle node and `` with the name of the package to +deploy. ### Example ```bash -cargo tangle gadget deploy --rpc-url ws://localhost:9944 --package my_blueprint +cargo tangle blueprint deploy --rpc-url ws://localhost:9944 --package my_blueprint ``` Expected output: diff --git a/cli/src/main.rs b/cli/src/main.rs index a6f06689b..eaa2f4912 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -22,8 +22,9 @@ struct Cli { #[derive(Subcommand, Debug)] enum Commands { - /// Gadget subcommand - Gadget { + /// Blueprint subcommand + #[command(visible_alias = "bp")] + Blueprint { #[command(subcommand)] subcommand: GadgetCommands, }, @@ -32,6 +33,7 @@ enum Commands { #[derive(Subcommand, Debug)] enum GadgetCommands { /// Create a new blueprint + #[command(visible_alias = "c")] Create { /// The name of the blueprint #[arg(short, long)] @@ -42,6 +44,7 @@ enum GadgetCommands { }, /// Deploy a blueprint to the Tangle Network. + #[command(visible_alias = "d")] Deploy { /// Tangle RPC URL to use #[arg(long, value_name = "URL", default_value = "wss://rpc.tangle.tools")] @@ -73,7 +76,7 @@ async fn main() -> color_eyre::Result<()> { let cli = Cli::parse_from(args); match cli.command { - Commands::Gadget { subcommand } => match subcommand { + Commands::Blueprint { subcommand } => match subcommand { GadgetCommands::Create { name, source } => { create::new_blueprint(name, source); }