Skip to content

Commit

Permalink
Multi job runner + SDK main macro (#346)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbraun96 authored Oct 16, 2024
1 parent ff53746 commit 0ff2b64
Show file tree
Hide file tree
Showing 24 changed files with 720 additions and 403 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cargo tangle gadget deploy --rpc-url <rpc_url> --package <package_name>

More information on this process can be found in the [CLI documentation](./cli/README.md)

### Testing a blueprint (alpha)
### Testing a blueprint (beta)

In order to test a blueprint, you must first have a local Tangle node running. When setting up a local testnet for
integration testing, we recommend running this script for
Expand Down
12 changes: 0 additions & 12 deletions blueprint-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,18 +578,6 @@ mod tests_standard {
#[allow(clippy::needless_return)]
async fn test_eigenlayer_incredible_squaring_blueprint() {
setup_log();
// let mut base_path = std::env::current_dir().expect("Failed to get current directory");
//
// base_path.push("../blueprints/incredible-squaring");
// base_path
// .canonicalize()
// .expect("File could not be normalized");

// let manifest_path = base_path.join("Cargo.toml");

// const INPUT: u64 = 2;
// const OUTPUT: u64 = INPUT.pow(2);

let (_container, http_endpoint, ws_endpoint) = anvil::start_anvil_container(true).await;

// let http_endpoint = "http://127.0.0.1:8545".to_string();
Expand Down
2 changes: 1 addition & 1 deletion blueprints/incredible-squaring/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::convert::Infallible;
id = 0,
params(x),
result(_),
event_listener(TangleEventListener),
event_listener(TangleEventListener::<JobCalled>),
verifier(evm = "IncredibleSquaringBlueprint")
)]
pub fn xsquare(x: u64) -> Result<u64, Infallible> {
Expand Down
154 changes: 19 additions & 135 deletions blueprints/incredible-squaring/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,146 +1,30 @@
use color_eyre::{eyre::eyre, Result};
use gadget_sdk::config::{ContextConfig, GadgetCLICoreSettings, GadgetConfiguration, StdGadgetConfiguration};
use gadget_sdk::{
tangle_subxt::tangle_testnet_runtime::api::{
self,
runtime_types::{sp_core::ecdsa, tangle_primitives::services},
},
tx,
info
};
use std::io::Write;
use incredible_squaring_blueprint as blueprint;
use structopt::StructOpt;
use gadget_sdk::events_watcher::InitializableEventHandler;
use gadget_sdk::keystore::KeystoreUriSanitizer;
use gadget_sdk::keystore::sp_core_subxt::Pair;
use gadget_sdk::run::GadgetRunner;
use gadget_sdk::info;
use gadget_sdk::job_runner::MultiJobRunner;
use gadget_sdk::tangle_subxt::subxt::tx::Signer;
use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::PriceTargets;
struct TangleGadgetRunner {
env: GadgetConfiguration<parking_lot::RawRwLock>,
}

#[async_trait::async_trait]
impl GadgetRunner for TangleGadgetRunner {
type Error = color_eyre::eyre::Report;

fn config(&self) -> &StdGadgetConfiguration {
todo!()
}

async fn register(&mut self) -> Result<()> {
// TODO: Use the function in blueprint-test-utils
if self.env.test_mode {
info!("Skipping registration in test mode");
return Ok(());
}

let client = self.env.client().await.map_err(|e| eyre!(e))?;
let signer = self
.env
.first_sr25519_signer()
.map_err(|e| eyre!(e))
.map_err(|e| eyre!(e))?;
let ecdsa_pair = self.env.first_ecdsa_signer().map_err(|e| eyre!(e))?;

let xt = api::tx().services().register(
self.env.blueprint_id,
services::OperatorPreferences {
key: ecdsa::Public(ecdsa_pair.signer().public().0),
approval: services::ApprovalPrefrence::None,
// TODO: Set the price targets
price_targets: PriceTargets {
cpu: 0,
mem: 0,
storage_hdd: 0,
storage_ssd: 0,
storage_nvme: 0,
},
},
Default::default(),
);

// send the tx to the tangle and exit.
let result = tx::tangle::send(&client, &signer, &xt).await?;
info!("Registered operator with hash: {:?}", result);
Ok(())
}

async fn benchmark(&self) -> std::result::Result<(), Self::Error> {
todo!()
}

async fn run(&mut self) -> Result<()> {
let client = self.env.client().await.map_err(|e| eyre!(e))?;
let signer = self.env.first_sr25519_signer().map_err(|e| eyre!(e))?;

info!("Starting the event watcher for {} ...", signer.account_id());

let x_square = blueprint::XsquareEventHandler {
service_id: self.env.service_id.unwrap(),
client: client.clone(),
signer,
};
use incredible_squaring_blueprint as blueprint;

let finished_rx = x_square
.init_event_handler()
.await
.expect("Event Listener init already called");
let res = finished_rx.await;
gadget_sdk::error!("Event handler finished with {res:?}");
#[gadget_sdk::main(env)]
async fn main() {
let client = env.client().await.map_err(|e| eyre!(e))?;
let signer = env.first_sr25519_signer().map_err(|e| eyre!(e))?;

Ok(())
}
}
info!("Starting the event watcher for {} ...", signer.account_id());

#[tokio::main]
#[allow(clippy::needless_return)]
async fn main() -> Result<()> {
gadget_sdk::logging::setup_log();
// Load the environment and create the gadget runner
let config = ContextConfig::from_args();
let env = gadget_sdk::config::load(config.clone()).expect("Failed to load environment");
let mut runner = Box::new(TangleGadgetRunner { env: env.clone() });
let x_square = blueprint::XsquareEventHandler {
service_id: env.service_id.unwrap(),
client: client.clone(),
signer,
};

info!("~~~ Executing the incredible squaring blueprint ~~~");

check_for_test(&env, &config)?;

info!("Registering...");
// Register the operator if needed
if env.should_run_registration() {
runner.register().await?;
}

info!("Running...");
// Run the gadget / AVS
runner.run().await?;
MultiJobRunner::new(&env)
.with_job()
.with_default_price_targets()
.finish(x_square)
.run()
.await?;

info!("Exiting...");
Ok(())
}

#[allow(irrefutable_let_patterns)]
fn check_for_test(
_env: &GadgetConfiguration<parking_lot::RawRwLock>,
config: &ContextConfig,
) -> Result<()> {
// create a file to denote we have started
if let GadgetCLICoreSettings::Run {
keystore_uri: base_path,
test_mode,
..
} = &config.gadget_core_settings
{
if !*test_mode {
return Ok(());
}
let path = base_path.sanitize_file_path().join("test_started.tmp");
let mut file = std::fs::File::create(&path)?;
file.write_all(b"test_started")?;
info!("Successfully wrote test file to {}", path.display())
}

Ok(())
}
67 changes: 60 additions & 7 deletions macros/blueprint-proc-macro-playground/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![allow(dead_code)]

use gadget_sdk::{benchmark, job, registration_hook, report, request_hook};

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -29,7 +28,7 @@ pub struct MyContext;
// ==================

/// Simple Threshold (t) Keygen Job for n parties.
#[job(id = 0, params(n, t), event_listener(TangleEventListener), result(_))]
#[job(id = 0, params(n, t), event_listener(TangleEventListener<JobCalled>), result(_))]
pub fn keygen(ctx: &MyContext, n: u16, t: u16) -> Result<Vec<u8>, Error> {
let _ = (n, t, ctx);
Ok(vec![0; 33])
Expand All @@ -39,7 +38,7 @@ pub fn keygen(ctx: &MyContext, n: u16, t: u16) -> Result<Vec<u8>, Error> {
#[job(
id = 1,
params(keygen_id, data),
event_listener(TangleEventListener),
event_listener(TangleEventListener<JobCalled>),
result(_)
)]
pub async fn sign(keygen_id: u64, data: Vec<u8>) -> Result<Vec<u8>, Error> {
Expand All @@ -50,7 +49,7 @@ pub async fn sign(keygen_id: u64, data: Vec<u8>) -> Result<Vec<u8>, Error> {
#[job(
id = 2,
params(keygen_id, new_t),
event_listener(TangleEventListener),
event_listener(TangleEventListener<JobCalled>),
result(_)
)]
pub fn refresh(keygen_id: u64, new_t: Option<u8>) -> Result<Vec<u64>, Error> {
Expand All @@ -59,7 +58,7 @@ pub fn refresh(keygen_id: u64, new_t: Option<u8>) -> Result<Vec<u64>, Error> {
}

/// Say hello to someone or the world.
#[job(id = 3, params(who), event_listener(TangleEventListener), result(_))]
#[job(id = 3, params(who), event_listener(TangleEventListener<JobCalled>), result(_))]
pub fn say_hello(who: Option<String>) -> Result<String, Error> {
match who {
Some(who) => Ok(format!("Hello, {}!", who)),
Expand All @@ -85,7 +84,7 @@ pub fn on_request(nft_id: u64);
#[report(
job_id = 0,
params(n, t, msgs),
event_listener(TangleEventListener),
event_listener(TangleEventListener<JobCalled>),
result(_),
report_type = "job",
verifier(evm = "KeygenContract")
Expand All @@ -97,7 +96,7 @@ fn report_keygen(n: u16, t: u16, msgs: Vec<Vec<u8>>) -> u32 {

#[report(
params(uptime, response_time, error_rate),
event_listener(TangleEventListener),
event_listener(TangleEventListener<JobCalled>),
result(Vec<u8>),
report_type = "qos",
interval = 3600,
Expand Down Expand Up @@ -130,13 +129,67 @@ fn keygen_2_of_3() {

#[cfg(test)]
mod tests {
use gadget_sdk as sdk;
#[test]
fn generated_blueprint() {
eprintln!("{}", super::KEYGEN_JOB_DEF);
assert_eq!(super::KEYGEN_JOB_ID, 0);
eprintln!("{}", super::REGISTRATION_HOOK);
}

#[test]
fn sdk_main() {
setup_env();

#[sdk::main]
async fn main() {
Ok(())
}
}

#[test]
fn sdk_main_with_env() {
setup_env();

#[sdk::main(env)]
async fn main() {
Ok(())
}
}

#[test]
fn sdk_main_with_tokio_params_1() {
setup_env();

#[sdk::main(env, flavor = "multi_thread")]
async fn main() {
Ok(())
}
}

#[test]
fn sdk_main_with_tokio_params_2() {
setup_env();

#[sdk::main(env, flavor = "multi_thread", worker_threads = 4)]
async fn main() {
Ok(())
}
}

#[test]
fn sdk_main_with_tokio_params_mixed_order() {
setup_env();

#[sdk::main(flavor = "multi_thread", env, worker_threads = 4)]
async fn main() {
Ok(())
}
}

fn setup_env() {
// TODO: Add all GadgetContext vars into the env
}
// #[test]
// fn example_benchmark() {
// super::keygen_2_of_3_benchmark();
Expand Down
Loading

0 comments on commit 0ff2b64

Please sign in to comment.