From 033025e4bee66840d143e33efed8d16b60c5fed7 Mon Sep 17 00:00:00 2001 From: MauricinhoMorales Date: Wed, 28 Feb 2024 14:52:04 -0500 Subject: [PATCH 1/5] Added the online endpoint [WIP] --- crates/sugarfunge-api-types/src/lib.rs | 8 ++- crates/sugarfunge-api-types/src/online.rs | 16 +++++ src/main.rs | 18 +++-- src/online.rs | 82 +++++++++++++++++++++++ 4 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 crates/sugarfunge-api-types/src/online.rs create mode 100644 src/online.rs 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/src/main.rs b/src/main.rs index db89757..04b32a5 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; @@ -74,10 +75,7 @@ async fn main() -> std::io::Result<()> { .route("account/exists", web::post().to(account::exists)) .route("account/create", web::post().to(account::create)) .route("account/fund", web::post().to(account::fund)) - .route( - "account/set_balance", - web::post().to(account::set_balance), - ) + .route("account/set_balance", web::post().to(account::set_balance)) .route("account/balance", web::post().to(account::balance)) .route("asset/create_class", web::post().to(asset::create_class)) .route("asset/class_info", web::post().to(asset::class_info)) @@ -235,6 +233,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 })) +} From 8f0b0cbc62033a41cee5d5ef0d2a531bd6631f72 Mon Sep 17 00:00:00 2001 From: Ehsan Sh Date: Wed, 6 Mar 2024 17:07:10 -0500 Subject: [PATCH 2/5] Added feature in fula-pool to give control to creator on leave nad requests --- crates/sugarfunge-api-types/src/pool.rs | 2 ++ src/pool.rs | 33 +++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) 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/pool.rs b/src/pool.rs index 88f03ae..dcd3af5 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -75,7 +75,21 @@ pub async fn leave_pool( let api = &data.api; - let call = sugarfunge::tx().pool().leave_pool(req.pool_id.into()); + // Convert target_account to AccountId32 if provided + let target_account_id = match &req.target_account { + Some(account_str) => { + let account_id = AccountId32::from_str(account_str) + .map_err(|_| error::ErrorBadRequest("Invalid target account"))?; + Some(account_id) + }, + None => None, + }; + + let call = if let Some(account_id) = target_account_id { + sugarfunge::tx().pool().leave_pool(req.pool_id.into(), Some(account_id)) + } else { + sugarfunge::tx().pool().leave_pool(req.pool_id.into(), None) + }; let set_balance = get_balance(&req.seed).await; let result = api .tx() @@ -156,7 +170,22 @@ pub async fn cancel_join_pool( let api = &data.api; - let call = sugarfunge::tx().pool().cancel_join(req.pool_id.into()); + // Convert target_account to AccountId32 if provided + let target_account_id = match &req.target_account { + Some(account_str) => { + let account_id = AccountId32::from_str(account_str) + .map_err(|_| error::ErrorBadRequest("Invalid target account"))?; + Some(account_id) + }, + None => None, + }; + + // Adjust the call based on whether target_account is provided + let call = if let Some(account_id) = target_account_id { + sugarfunge::tx().pool().cancel_join(req.pool_id.into(), Some(account_id)) + } else { + sugarfunge::tx().pool().cancel_join(req.pool_id.into(), None) + }; let set_balance = get_balance(&req.seed).await; let result = api .tx() From a15c9c92d1f678dff5c2e449b4edbfda05754288 Mon Sep 17 00:00:00 2001 From: Ehsan Sh Date: Wed, 6 Mar 2024 17:09:27 -0500 Subject: [PATCH 3/5] updated metadata for changes in fula-pool --- .../sugarfunge_metadata.scale | Bin 79391 -> 79539 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/crates/sugarfunge-api-types/sugarfunge_metadata.scale b/crates/sugarfunge-api-types/sugarfunge_metadata.scale index 26c63477175c5b1d8444af9a111e4392778c813d..d4fd80ccd85512948e67549604dfe444e6857df5 100644 GIT binary patch delta 603 zcma)$KWGzS7{Ax&zVT6%3v5RIwm zkfx>;0eXW^zzIfOR)CC8vqOc{WOMwEVdsjft!)4Iq8 zeG<8yd}oRz5A)7{eV=*L1%?3*#osZ1en zz<*ApGkJy+rEGD0tdz;-a%OqDM0Zbj)8NLV|2_7%K5x90;cl(J*-Rk4Mi}v?`jX|+ zgju{iQJBF$nfrJ5j4{r5KDavt&5OZYxmX;VE(J%Y%-p4j;7qc=dhy)}hxMfklNvXY zzv@ANL$y90KoU-S|3R!uaM|xdI4;3$yL*vvpv^uR#xoAh`We9v wJl3};4#8^$Vt4}GdLF|I7}i`I{b;wA<9H9Bb^QpM=&%O{k(SVD-AdrWZ;?>33;+NC delta 478 zcmXxeKS*0q7zOZqUhaJ_i7)THB(z3p9jw+Gs7d^jE?pE6yA-KXD2OEzS`;BoA(R@T zbZCr5UA~q(sfY*;BEg5ec4~!|4xNfFI@(ny2PqErw%|7%_&EHId&bvJdF=y-Mjv?! zKKj91An26$L#LEzgF(+k3Vy1J#|Y4|XhD#!L=ttB)bGQjF})ojs^|$=^hsr#PE>X% zVk8izKBEH>${Tlil(7-QaJ{>0{FzbOc6m-|-E*aoV9szMo8xRk!zO(#k5XgBR&KoV zPPyBal5*qoCFO3-&noxc!dug1W`9Oi_B5>juLP==nh~oOO^ca*nU|a$UB52X%ckIB zQLQR37DHj?3>2m^oSj{JlS{T*Ilc5osFit*YPTC)d)ZEd)79V&8vV8w<+$}Kmz#P% zk#!IoQt46FO@IF-5ODoJk9#n*_ZwrD_uj^ZADS%1FlqXn-oon)`*++sk6~Oucu$+q z$f3)8GkRfoZZmc@_`Mgcc%UKRt+t`ZhoE;D$7hZ@xtzcyOu3N69fagg5*x7OlN7$d emXS`h!;ud=ae%ONx==&J+wa1NhN!%m#>!t-SB9Mc From 538716b1f2d798976bd3f1cedd4a5b03b8344e67 Mon Sep 17 00:00:00 2001 From: MauricinhoMorales Date: Wed, 6 Mar 2024 18:02:26 -0500 Subject: [PATCH 4/5] Updated the pool endpoints for cancel_join and leave pools --- src/pool.rs | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/pool.rs b/src/pool.rs index dcd3af5..b9be811 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -73,23 +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; - // Convert target_account to AccountId32 if provided - let target_account_id = match &req.target_account { - Some(account_str) => { - let account_id = AccountId32::from_str(account_str) - .map_err(|_| error::ErrorBadRequest("Invalid target account"))?; - Some(account_id) - }, - None => None, - }; - - let call = if let Some(account_id) = target_account_id { - sugarfunge::tx().pool().leave_pool(req.pool_id.into(), Some(account_id)) - } else { - sugarfunge::tx().pool().leave_pool(req.pool_id.into(), None) - }; + 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() @@ -168,24 +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; - // Convert target_account to AccountId32 if provided - let target_account_id = match &req.target_account { - Some(account_str) => { - let account_id = AccountId32::from_str(account_str) - .map_err(|_| error::ErrorBadRequest("Invalid target account"))?; - Some(account_id) - }, - None => None, - }; - - // Adjust the call based on whether target_account is provided - let call = if let Some(account_id) = target_account_id { - sugarfunge::tx().pool().cancel_join(req.pool_id.into(), Some(account_id)) - } else { - sugarfunge::tx().pool().cancel_join(req.pool_id.into(), None) - }; + 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() From 13acf6d4a914984abff070652a36ba90399d105e Mon Sep 17 00:00:00 2001 From: MauricinhoMorales Date: Thu, 7 Mar 2024 14:50:51 -0500 Subject: [PATCH 5/5] Added a new endpoint to Activate an account that was removed by being offline --- src/main.rs | 4 ++++ src/validator.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/main.rs b/src/main.rs index 04b32a5..6797e00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -103,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", diff --git a/src/validator.rs b/src/validator.rs index d7204c2..65074c9 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,