diff --git a/crates/sugarfunge-api-types/src/lib.rs b/crates/sugarfunge-api-types/src/lib.rs index 469cf9a..23416bd 100644 --- a/crates/sugarfunge-api-types/src/lib.rs +++ b/crates/sugarfunge-api-types/src/lib.rs @@ -1,9 +1,12 @@ #[subxt::subxt( runtime_metadata_path = "sugarfunge_metadata.scale", - derive_for_type(path = "frame_support::traits::tokens::misc::BalanceStatus", derive = "serde::Serialize"), + derive_for_type( + path = "frame_support::traits::tokens::misc::BalanceStatus", + derive = "serde::Serialize" + ), derive_for_type(path = "pallet_balances::pallet::Event", derive = "serde::Serialize"), derive_for_type(path = "sugarfunge_asset::pallet::Event", derive = "serde::Serialize"), - derive_for_type(path = "sugarfunge_bag::pallet::Event", derive = "serde::Serialize"), + derive_for_type(path = "sugarfunge_bag::pallet::Event", derive = "serde::Serialize") )] pub mod sugarfunge {} pub mod account; @@ -14,6 +17,7 @@ pub mod challenge; pub mod contract; pub mod fula; pub mod market; +pub mod online; pub mod pool; pub mod primitives; pub mod validator; diff --git a/crates/sugarfunge-api-types/src/online.rs b/crates/sugarfunge-api-types/src/online.rs new file mode 100644 index 0000000..6e9d19f --- /dev/null +++ b/crates/sugarfunge-api-types/src/online.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetAuthoredBlocks { + pub validators: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetHeartbeats { + pub validators: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct HeartbeatTime { + pub time: Option, +} diff --git a/crates/sugarfunge-api-types/src/pool.rs b/crates/sugarfunge-api-types/src/pool.rs index 9709fbd..5be5230 100644 --- a/crates/sugarfunge-api-types/src/pool.rs +++ b/crates/sugarfunge-api-types/src/pool.rs @@ -23,6 +23,7 @@ pub struct CreatePoolOutput { pub struct LeavePoolInput { pub seed: Seed, pub pool_id: PoolId, + pub target_account: Option, // Optional target account to be removed } #[derive(Serialize, Deserialize, Debug)] @@ -52,6 +53,7 @@ pub struct JoinPoolOutput { pub struct CancelJoinPoolInput { pub seed: Seed, pub pool_id: PoolId, + pub target_account: Option, // Optional target account to be removed } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/main.rs b/src/main.rs index bb430b3..4148f22 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ mod config; mod contract; mod fula; mod market; +mod online; mod pool; mod state; mod subscription; @@ -102,6 +103,10 @@ async fn main() -> std::io::Result<()> { "validator/add_validator", web::post().to(validator::add_validator), ) + .route( + "validator/activate", + web::post().to(validator::activate_validator), + ) .route("validator/set_keys", web::post().to(validator::set_keys)) .route( "validator/remove_validator", @@ -236,6 +241,18 @@ async fn main() -> std::io::Result<()> { "fula/mumbai/convert_tokens", web::post().to(contract::mumbai_convert_to_fula_endpoint), ) + .route( + "fula/online/get_heartbeats", + web::post().to(online::get_heartbeats), + ) + .route( + "fula/online/get_authored_blocks", + web::post().to(online::get_authored_blocks), + ) + .route( + "fula/online/get_heartbeat_time", + web::post().to(online::get_heartbeat_time), + ) }) .bind((args.listen.host_str().unwrap(), args.listen.port().unwrap()))? .run() diff --git a/src/online.rs b/src/online.rs new file mode 100644 index 0000000..70d23e4 --- /dev/null +++ b/src/online.rs @@ -0,0 +1,82 @@ +use crate::state::*; +use crate::util::*; +use actix_web::{error, web, HttpResponse}; +use futures::stream::StreamExt; +use sugarfunge_api_types::online::*; +use sugarfunge_api_types::sugarfunge; + +pub async fn get_authored_blocks(data: web::Data) -> error::Result { + let api = &data.api; + let result_array = Vec::new(); + + let query_key = sugarfunge::storage() + .im_online() + .authored_blocks_iter() + .to_root_bytes(); + + let storage = api.storage().at_latest().await.map_err(map_subxt_err)?; + + let keys_stream = storage + .fetch_raw_keys(query_key) + .await + .map_err(map_subxt_err)?; + + let keys: Vec> = keys_stream + .collect::>() // Collect into a Vec, Error>> + .await // Await the collection process + .into_iter() // Convert into an iterator + .filter_map(Result::ok) // Filter out Ok values, ignore errors + .collect(); // Collect into a Vec> + + println!("Obtained keys:"); + for key in keys.iter() { + println!("Key: len: {} 0x{}", key.len(), hex::encode(&key)); + } + Ok(HttpResponse::Ok().json(GetAuthoredBlocks { + validators: result_array, + })) +} + +pub async fn get_heartbeats(data: web::Data) -> error::Result { + let api = &data.api; + let result_array = Vec::new(); + + let query_key = sugarfunge::storage() + .im_online() + .received_heartbeats_iter() + .to_root_bytes(); + + let storage = api.storage().at_latest().await.map_err(map_subxt_err)?; + + let keys_stream = storage + .fetch_raw_keys(query_key) + .await + .map_err(map_subxt_err)?; + + let keys: Vec> = keys_stream + .collect::>() // Collect into a Vec, Error>> + .await // Await the collection process + .into_iter() // Convert into an iterator + .filter_map(Result::ok) // Filter out Ok values, ignore errors + .collect(); // Collect into a Vec> + + // println!("Obtained keys:"); + for key in keys.iter() { + println!("Key: len: {} 0x{}", key.len(), hex::encode(&key)); + } + Ok(HttpResponse::Ok().json(GetHeartbeats { + validators: result_array, + })) +} + +pub async fn get_heartbeat_time(data: web::Data) -> error::Result { + let api = &data.api; + + let query_key = sugarfunge::storage().im_online().heartbeat_after(); + + let storage = api.storage().at_latest().await.map_err(map_subxt_err)?; + + let keys_stream = storage.fetch(&query_key).await.map_err(map_subxt_err)?; + + Ok(HttpResponse::Ok().json(HeartbeatTime { time: keys_stream })) +} diff --git a/src/pool.rs b/src/pool.rs index 88f03ae..b9be811 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -73,9 +73,15 @@ pub async fn leave_pool( let pair = get_pair_from_seed(&req.seed)?; let signer = PairSigner::new(pair); + let mut target_account: Option = None::; + if let Some(value) = req.target_account.clone() { + target_account = Some(AccountId32::try_from(&value).map_err(map_account_err)?); + } let api = &data.api; - let call = sugarfunge::tx().pool().leave_pool(req.pool_id.into()); + let call = sugarfunge::tx() + .pool() + .leave_pool(req.pool_id.into(), target_account); let set_balance = get_balance(&req.seed).await; let result = api .tx() @@ -154,9 +160,15 @@ pub async fn cancel_join_pool( let pair = get_pair_from_seed(&req.seed)?; let signer = PairSigner::new(pair); + let mut target_account: Option = None::; + if let Some(value) = req.target_account.clone() { + target_account = Some(AccountId32::try_from(&value).map_err(map_account_err)?); + } let api = &data.api; - let call = sugarfunge::tx().pool().cancel_join(req.pool_id.into()); + let call = sugarfunge::tx() + .pool() + .cancel_join(req.pool_id.into(), target_account); let set_balance = get_balance(&req.seed).await; let result = api .tx() diff --git a/src/validator.rs b/src/validator.rs index 10dd5ab..6278a03 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -54,6 +54,44 @@ pub async fn add_validator( } } +pub async fn activate_validator( + data: web::Data, + req: web::Json, +) -> error::Result { + let pair = get_pair_from_seed(&req.seed)?; + let signer = PairSigner::new(pair); + let validator_public = + sp_core::sr25519::Public::from_str(req.validator_id.as_str()).map_err(map_account_err)?; + let validator_bytes: [u8; 32] = validator_public.0; // Convert Public key to a byte array + let validator_id = subxt::utils::AccountId32::from(validator_bytes); // Create AccountId32 from the byte array + let api = &data.api; + + let call = sugarfunge::tx().validator_set().add_validator_again(validator_id); + + let result = api + .tx() + .sign_and_submit_then_watch(&call, &signer, Default::default()) + .await + .map_err(map_subxt_err)? + .wait_for_finalized_success() + .await + .map_err(map_sf_err)?; + + let result = result + .find_first::() + .map_err(map_subxt_err)?; + + match result { + Some(event) => Ok(HttpResponse::Ok().json(AddValidatorOutput { + validator_id: ValidatorId::from(event.0.to_string()), + })), + None => Ok(HttpResponse::BadRequest().json(RequestError { + message: json!("Failed to find sugarfunge::validator::events::ActivateValidator"), + description: String::new(), + })), + } +} + pub async fn remove_validator( data: web::Data, req: web::Json,