Skip to content

Commit

Permalink
plugin subsystem
Browse files Browse the repository at this point in the history
  • Loading branch information
aspect committed Nov 18, 2023
1 parent 9b0d1cb commit 12338ea
Show file tree
Hide file tree
Showing 19 changed files with 797 additions and 60 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ egui-notify = "0.10.0"
# | \ |__| ___] | | | \_ | | ___] | | |
# _______________________________________________________

kaspa-addresses = { path = "../rusty-kaspa/crypto/addresses" }
kaspa-bip32 = { path = "../rusty-kaspa/wallet/bip32" }
kaspa-cli = { path = "../rusty-kaspa/cli" }
kaspa-consensus-core = { path = "../rusty-kaspa/consensus/core" }
Expand All @@ -70,6 +71,7 @@ kaspa-wrpc-client = { path = "../rusty-kaspa/rpc/wrpc/client", features=["no-uns
kaspa-wrpc-server = { path = "../rusty-kaspa/rpc/wrpc/server" }
kaspad = { path = "../rusty-kaspa/kaspad" }

# kaspa-addresses = { git = "https://github.com/kaspanet/rusty-kaspa.git", branch = "kng" }
# kaspa-bip32 = { git = "https://github.com/kaspanet/rusty-kaspa.git", branch = "kng" }
# kaspa-cli = { git = "https://github.com/kaspanet/rusty-kaspa.git", branch = "kng" }
# kaspa-consensus-core = { git = "https://github.com/kaspanet/rusty-kaspa.git", branch = "kng" }
Expand Down Expand Up @@ -136,6 +138,7 @@ qrcode = "0.12.0"
rand = "0.8"
separator = "0.4.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.107"
slug = "0.1.4"
sysinfo = "0.29.10"
thiserror = "1.0.47"
Expand Down
3 changes: 2 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ vergen = { version = "8.2.6", features = ["build", "cargo", "git", "gitcl", "rus

[dependencies]
kaspa-ng-macros.workspace = true

kaspa-addresses.workspace = true
kaspa-bip32.workspace = true
kaspa-cli.workspace = true
kaspa-consensus-core.workspace = true
Expand Down Expand Up @@ -48,6 +48,7 @@ pad.workspace = true
qrcode.workspace = true
separator.workspace = true
serde.workspace = true
serde_json.workspace = true
slug.workspace = true
thiserror.workspace = true
wasm-bindgen.workspace = true
Expand Down
15 changes: 15 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ pub enum Error {

#[error("Account descriptors are not valid during the wallet open operation")]
WalletOpenAccountDescriptors,

#[error(transparent)]
AddressError(#[from] kaspa_addresses::AddressError),

#[error("Invalid network type")]
InvalidNetworkType,

#[error("Http error: {0}")]
HttpError(#[from] workflow_http::error::Error),

#[error("Invalid JSON: {0}")]
SerdeJson(#[from] serde_json::Error),

#[error(transparent)]
NetworkType(#[from] kaspa_consensus_core::network::NetworkTypeError),
}

impl Error {
Expand Down
4 changes: 2 additions & 2 deletions core/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ pub use crate::result::Result;
pub use crate::runtime;
pub use crate::runtime::{spawn, spawn_with_result, Payload, Runtime, Service};
pub use crate::settings::{
KaspadNodeKind, NetworkInterfaceConfig, NetworkInterfaceKind, NodeSettings, RpcConfig,
Settings, UxSettings,
KaspadNodeKind, NetworkInterfaceConfig, NetworkInterfaceKind, NodeSettings, PluginSettings,
PluginSettingsMap, RpcConfig, Settings, UxSettings,
};
pub use crate::utils::spawn;
// #[macro_use]
Expand Down
21 changes: 16 additions & 5 deletions core/src/modules/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub struct AccountManager {

selected: Option<Account>,
state: State,
send_address : String,
destination_address_string : String,
send_amount_text: String,
send_amount_sompi : u64,
send_info: Option<String>,
Expand All @@ -61,7 +61,7 @@ impl AccountManager {
runtime,
selected: None,
state: State::Select,
send_address : String::new(),
destination_address_string : String::new(),
send_amount_text: String::new(),
send_amount_sompi : 0,
send_info : None,
Expand Down Expand Up @@ -317,14 +317,25 @@ impl AccountManager {

let mut proceed_with_estimate = false;


let mut destination_address_string = self.destination_address_string.clone();
ui.label(egui::RichText::new("Enter address").size(12.).raised());
ui.add_sized(

// TODO - address processing...
let _response = ui.add_sized(
size,
TextEdit::singleline(&mut self.send_address)
TextEdit::singleline(&mut destination_address_string)
// .hint_text("Payment password...")
.vertical_align(Align::Center),
);
if destination_address_string != self.destination_address_string {
self.destination_address_string = destination_address_string;
match try_user_string_to_address(self.destination_address_string.as_str(), &network_type) {
Ok(_address) => {},
Err(err) => {
self.send_info = Some(err.to_string());
}
}
}

ui.label(egui::RichText::new("Enter amount to send").size(12.).raised());
let mut send_amount_text = self.send_amount_text.clone();
Expand Down
2 changes: 1 addition & 1 deletion core/src/modules/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::imports::*;
use crate::runtime::services::metrics::MAX_METRICS_SAMPLES;
use crate::runtime::services::metrics_monitor::MAX_METRICS_SAMPLES;
use egui_extras::{StripBuilder, Size};
use kaspa_metrics::{Metric,MetricGroup, MetricsSnapshot};
use chrono::DateTime;
Expand Down
2 changes: 1 addition & 1 deletion core/src/modules/overview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Overview {
if let Some(metric) = metric_iter.next() {
// let duration = 60 * 10; // 15 min (testing)
// let duration = core.settings.ux.metrics.graph_duration;
let value = snapshot.get(&metric);
let value = snapshot.get(metric);
self.render_graph(ui, *metric, value, theme);
} else {
draw = false;
Expand Down
42 changes: 20 additions & 22 deletions core/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,20 @@ cfg_if! {
}
}

pub mod services;
use services::*;

pub mod payload;
pub mod plugins;
pub mod services;
pub use payload::Payload;

#[async_trait]
pub trait Service: Sync + Send {
async fn spawn(self: Arc<Self>) -> Result<()>;
async fn join(self: Arc<Self>) -> Result<()>;
fn terminate(self: Arc<Self>);
// --
async fn attach_rpc(self: Arc<Self>, _rpc_api: Arc<dyn RpcApi>) -> Result<()> {
Ok(())
}
async fn detach_rpc(self: Arc<Self>) -> Result<()> {
Ok(())
}
}
pub use services::Service;
use services::*;

pub struct Inner {
// services: Mutex<Vec<Arc<dyn Service + Send + Sync + 'static>>>,
services: Mutex<Vec<Arc<dyn Service>>>,
kaspa: Arc<KaspaService>,
peer_monitor: Arc<PeerMonitorService>,
peer_monitor_service: Arc<PeerMonitorService>,
metrics_service: Arc<MetricsService>,
plugin_manager_service: Arc<PluginManagerService>,
application_events: ApplicationEventsChannel,
egui_ctx: egui::Context,
is_running: Arc<AtomicBool>,
Expand All @@ -53,25 +41,31 @@ impl Runtime {
pub fn new(egui_ctx: &egui::Context, settings: &Settings) -> Self {
let application_events = ApplicationEventsChannel::unbounded(Some(egui_ctx.clone()));
let kaspa = Arc::new(KaspaService::new(application_events.clone(), settings));
let peer_monitor = Arc::new(PeerMonitorService::new(
let peer_monitor_service = Arc::new(PeerMonitorService::new(
application_events.clone(),
settings,
));
let metrics_service = Arc::new(MetricsService::new(application_events.clone(), settings));
let plugin_manager_service = Arc::new(PluginManagerService::new(
application_events.clone(),
settings,
));
// let runtime = Runtime::new(&[kaspa.clone(), peer_monitor.clone(), metrics_service.clone()]);

let services: Mutex<Vec<Arc<dyn Service>>> = Mutex::new(vec![
kaspa.clone(),
peer_monitor.clone(),
peer_monitor_service.clone(),
metrics_service.clone(),
plugin_manager_service.clone(),
]);

let runtime = Self {
inner: Arc::new(Inner {
services,
application_events,
kaspa,
peer_monitor,
peer_monitor_service,
plugin_manager_service,
metrics_service,
egui_ctx: egui_ctx.clone(),
is_running: Arc::new(AtomicBool::new(false)),
Expand Down Expand Up @@ -145,13 +139,17 @@ impl Runtime {
}

pub fn peer_monitor_service(&self) -> &Arc<PeerMonitorService> {
&self.inner.peer_monitor
&self.inner.peer_monitor_service
}

pub fn metrics_service(&self) -> &Arc<MetricsService> {
&self.inner.metrics_service
}

pub fn plugin_manager_service(&self) -> &Arc<PluginManagerService> {
&self.inner.plugin_manager_service
}

/// Returns the reference to the application events channel.
pub fn application_events(&self) -> &ApplicationEventsChannel {
&self.inner.application_events
Expand Down
84 changes: 84 additions & 0 deletions core/src/runtime/plugins/market_monitor/coingecko.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use super::*;
// use crate::imports::*;
use workflow_http::get_json;

// https://api.coingecko.com/api/v3/simple/price?ids=kaspa&vs_currencies=usd%2Ccny&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true
// {
// "kaspa": {
// "usd": 0.137395,
// "usd_market_cap": 2954668910.049152,
// "usd_24h_vol": 138844602.78193888,
// "usd_24h_change": 16.77712212157855,
// "cny": 0.990717,
// "cny_market_cap": 21305231109.691406,
// "cny_24h_vol": 1001166777.2797261,
// "cny_24h_change": 16.744741148538786
// }
// }

// https://api.coingecko.com/api/v3/coins/list
// [
// {
// "id": "01coin",
// "symbol": "zoc",
// "name": "01coin"
// },

#[derive(Default, Serialize, Deserialize)]
struct CoinGeckoSimplePrice {
kaspa: Option<HashMap<String, f64>>,
}

impl CoinGeckoSimplePrice {
pub async fn get(currencies: &[&str]) -> Result<Self> {
let ids = "kaspa";
let currencies = currencies
.iter()
.map(|currency| currency.to_lowercase())
.collect::<Vec<_>>()
.join("%2C");
let url = format!("https://api.coingecko.com/api/v3/simple/price?ids={ids}&vs_currencies={currencies}&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true");
Ok(get_json::<Self>(url).await?)
}
}

impl From<CoinGeckoSimplePrice> for MarketPriceMap {
fn from(data: CoinGeckoSimplePrice) -> Self {
let mut prices = HashMap::new();
if let Some(kaspa) = data.kaspa {
prices = group_by_currency_prefix(&kaspa);
}
prices
}
}

pub async fn fetch_available_currencies() -> Result<CurrencyDescriptorList> {
let url = "https://api.coingecko.com/api/v3/coins/list";
let available_currencies = get_json::<CurrencyDescriptorList>(url).await?;
Ok(available_currencies)
}

pub async fn fetch_market_price_list(currencies: &[&str]) -> Result<MarketPriceMap> {
let market_data = CoinGeckoSimplePrice::get(currencies).await?;
Ok(market_data.into())
}

fn group_by_currency_prefix(data: &HashMap<String, f64>) -> MarketPriceMap {
let mut grouped_data: MarketPriceMap = HashMap::new();

for (coin, info) in data.iter() {
let parts: Vec<&str> = coin.split('_').collect();
let currency_prefix = parts[0].to_lowercase();
let suffix = parts.last().map(|suffix| suffix.to_lowercase());
let existing_data = grouped_data.entry(currency_prefix.clone()).or_default();
match suffix.as_deref() {
None => existing_data.price = Some(*info),
Some("market_cap") => existing_data.market_cap = Some(*info),
Some("24h_vol") => existing_data.volume = Some(*info),
Some("24h_change") => existing_data.change = Some(*info),
_ => (),
}
}

grouped_data
}
61 changes: 61 additions & 0 deletions core/src/runtime/plugins/market_monitor/coinmarketcap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::*;
use workflow_http::get_json;

#[derive(Default, Serialize, Deserialize)]
struct CoinGeckoSimplePrice {
kaspa: Option<HashMap<String, f64>>,
}

impl CoinGeckoSimplePrice {
pub async fn get(currencies: &[&str]) -> Result<Self> {
let ids = "kaspa";
let currencies = currencies
.iter()
.map(|currency| currency.to_lowercase())
.collect::<Vec<_>>()
.join("%2C");
let url = format!("https://api.coingecko.com/api/v3/simple/price?ids={ids}&vs_currencies={currencies}&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true");
Ok(get_json::<Self>(url).await?)
}
}

impl From<CoinGeckoSimplePrice> for MarketPriceMap {
fn from(data: CoinGeckoSimplePrice) -> Self {
let mut prices = HashMap::new();
if let Some(kaspa) = data.kaspa {
prices = group_by_currency_prefix(&kaspa);
}
prices
}
}

pub async fn fetch_available_currencies() -> Result<CurrencyDescriptorList> {
let url = "https://api.coingecko.com/api/v3/coins/list";
let available_currencies = get_json::<CurrencyDescriptorList>(url).await?;
Ok(available_currencies)
}

pub async fn fetch_market_price_list(currencies: &[&str]) -> Result<MarketPriceMap> {
let market_data = CoinGeckoSimplePrice::get(currencies).await?;
Ok(market_data.into())
}

fn group_by_currency_prefix(data: &HashMap<String, f64>) -> MarketPriceMap {
let mut grouped_data: MarketPriceMap = HashMap::new();

for (coin, info) in data.iter() {
let parts: Vec<&str> = coin.split('_').collect();
let currency_prefix = parts[0].to_lowercase();
let suffix = parts.last().map(|suffix| suffix.to_lowercase());
let existing_data = grouped_data.entry(currency_prefix.clone()).or_default();
match suffix.as_deref() {
None => existing_data.price = Some(*info),
Some("market_cap") => existing_data.market_cap = Some(*info),
Some("24h_vol") => existing_data.volume = Some(*info),
Some("24h_change") => existing_data.change = Some(*info),
_ => (),
}
}

grouped_data
}
Loading

0 comments on commit 12338ea

Please sign in to comment.