From 0688696168c7e947bd6432802f65166c4c092040 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:42:16 +0200 Subject: [PATCH 1/2] feat: impl context provider --- Cargo.toml | 13 +++-- src/app.rs | 2 +- src/context.rs | 14 +++-- src/context_provider.rs | 98 +++++++++++++++++++++++++++++++++ src/logging.rs | 7 ++- src/main.rs | 12 ++-- src/platform/contested_names.rs | 36 ++---------- src/platform/mod.rs | 2 +- src/sdk_wrapper.rs | 10 +--- src/ui/add_identity_screen.rs | 17 ++---- 10 files changed, 142 insertions(+), 69 deletions(-) create mode 100644 src/context_provider.rs diff --git a/Cargo.toml b/Cargo.toml index 5f70fc19b..541f80c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ rust-version = "1.81" [dependencies] accesskit = "=0.16.1" -egui = { version = "0.29.1"} +egui = { version = "0.29.1" } egui_extras = "0.29.1" -eframe = { version = "0.29.1" , features = ["persistence"]} +eframe = { version = "0.29.1", features = ["persistence"] } strum = { version = "0.26.1", features = ["derive"] } bs58 = "0.5.0" base64 = "0.22.1" @@ -35,7 +35,7 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } dotenvy = "0.15.7" envy = "0.4.2" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } governor = "0.6.3" tokio-util = "0.7.10" chrono = "0.4" @@ -43,8 +43,9 @@ chrono-humanize = "0.2.3" dashmap = "5.5.3" grovedb-version = "2.0.3" sha2 = "0.10.8" -arboard = { version = "3.4.0", default-features = false, features = ["windows-sys"] } +arboard = { version = "3.4.0", default-features = false, features = [ + "windows-sys", +] } hdrhistogram = "7.5.4" -rusqlite = { version = "0.32.1"} - +rusqlite = { version = "0.32.1" } diff --git a/src/app.rs b/src/app.rs index e2ef577fb..201990aa5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -84,7 +84,7 @@ impl BitOrAssign for AppAction { } impl AppState { pub fn new() -> Self { - let app_context = Arc::new(AppContext::new()); + let app_context = AppContext::new(); let identities_screen = IdentitiesScreen::new(&app_context); let dpns_contested_names_screen = DPNSContestedNamesScreen::new(&app_context); diff --git a/src/context.rs b/src/context.rs index b41eac0a5..2255516d9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,5 @@ use crate::config::Config; +use crate::context_provider::Provider; use crate::database::Database; use crate::model::contested_name::ContestedName; use crate::model::qualified_identity::QualifiedIdentity; @@ -9,28 +10,31 @@ use dash_sdk::dpp::version::PlatformVersion; use dash_sdk::platform::DataContract; use dash_sdk::Sdk; use rusqlite::Result; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; #[derive(Debug)] pub struct AppContext { pub(crate) network: Network, pub(crate) devnet_name: Option, pub(crate) db: Arc, - pub(crate) sdk: Arc>, + pub(crate) sdk: Sdk, pub(crate) config: Config, pub(crate) dpns_contract: Arc>, pub(crate) platform_version: &'static PlatformVersion, } impl AppContext { - pub fn new() -> Self { + pub fn new() -> Arc { let db = Arc::new(Database::new("identities.db").unwrap()); let config = Config::load(); db.initialize().unwrap(); - let sdk = Arc::new(RwLock::new(initialize_sdk(&config))); + // we create provider, but we need to set app context to it later, as we have a circular dependency + let provider = Provider::new(db.clone(), &config).expect("Failed to initialize SDK"); + + let sdk = initialize_sdk(&config, provider.clone()); let mut app_context = AppContext { network: config.core_network(), @@ -50,6 +54,8 @@ impl AppContext { if let Some(contract) = contract { app_context.dpns_contract = Arc::new(Some(contract)); } + let app_context = Arc::new(app_context); + provider.bind_app_context(app_context.clone()); app_context } diff --git a/src/context_provider.rs b/src/context_provider.rs new file mode 100644 index 000000000..ecd8f9038 --- /dev/null +++ b/src/context_provider.rs @@ -0,0 +1,98 @@ +use crate::config::Config; +use crate::context::AppContext; +use crate::database::Database; +use dash_sdk::core_client::CoreClient; +use dash_sdk::error::ContextProviderError; +use dash_sdk::platform::ContextProvider; +use dash_sdk::platform::DataContract; +use rusqlite::Result; +use std::sync::{Arc, Mutex}; + +pub(crate) struct Provider { + db: Arc, + app_context: Mutex>>, + core: CoreClient, +} + +impl Provider { + /// Create new ContextProvider. + /// + /// Note that you have to bind it to app context using [Provider::set_app_context()]. + pub fn new(db: Arc, config: &Config) -> Result { + let core_client = CoreClient::new( + &config.core_host, + config.core_rpc_port, + &config.core_rpc_user, + &config.core_rpc_password, + ) + .map_err(|e| e.to_string())?; + + Ok(Self { + db, + core: core_client, + app_context: Default::default(), + }) + } + /// Set app context to the provider. + pub fn bind_app_context(&self, app_context: Arc) { + // order matters - can cause deadlock + let cloned = app_context.clone(); + let mut ac = self.app_context.lock().expect("lock poisoned"); + ac.replace(cloned); + drop(ac); + + app_context.sdk.set_context_provider(self.clone()); + } +} + +impl ContextProvider for Provider { + fn get_data_contract( + &self, + data_contract_id: &dash_sdk::platform::Identifier, + ) -> Result>, dash_sdk::error::ContextProviderError> { + let app_ctx_guard = self.app_context.lock().expect("lock poisoned"); + let app_ctx = app_ctx_guard + .as_ref() + .ok_or(ContextProviderError::Config("no app context".to_string()))?; + + let dc = self + .db + .get_contract_by_id(data_contract_id.clone(), app_ctx.as_ref()) + .map_err(|e| dash_sdk::error::ContextProviderError::Generic(e.to_string()))?; + + drop(app_ctx_guard); + + Ok(dc.map(Arc::new)) + } + + fn get_quorum_public_key( + &self, + quorum_type: u32, + quorum_hash: [u8; 32], // quorum hash is 32 bytes + _core_chain_locked_height: u32, + ) -> std::result::Result<[u8; 48], dash_sdk::error::ContextProviderError> { + let key = self.core.get_quorum_public_key(quorum_type, quorum_hash)?; + + Ok(key) + } + + fn get_platform_activation_height( + &self, + ) -> std::result::Result< + dash_sdk::dpp::prelude::CoreBlockHeight, + dash_sdk::error::ContextProviderError, + > { + Ok(1) + } +} + +impl Clone for Provider { + fn clone(&self) -> Self { + let app_guard = self.app_context.lock().expect("lock poisoned"); + Self { + core: self.core.clone(), + db: self.db.clone(), + app_context: Mutex::new(app_guard.clone()), + } + } +} diff --git a/src/logging.rs b/src/logging.rs index 6b545c050..31b765d4b 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -6,9 +6,10 @@ pub fn initialize_logger() { // Initialize logger let log_file = std::fs::File::create("explorer.log").expect("Failed to create log file"); - let filter = EnvFilter::try_new("trace") - .unwrap() - .add_directive("rs_dapi_client=off".parse().unwrap()); + let filter = EnvFilter::try_new( + "debug,dash_sdk=trace,tenderdash_abci=trace,drive=trace,drive_proof_verifier=trace,rs_dapi_client=debug", + ) + .unwrap(); let subscriber = tracing_subscriber::fmt() .with_env_filter(filter) diff --git a/src/main.rs b/src/main.rs index 578b67cf0..dbcd4d66a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use tokio::runtime::Runtime; - mod app; mod config; mod database; @@ -8,13 +6,19 @@ mod sdk_wrapper; mod ui; mod context; +mod context_provider; mod model; mod platform; fn main() -> eframe::Result<()> { // Initialize the Tokio runtime - let runtime = Runtime::new().unwrap(); + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(40) + .enable_all() + .build() + .expect("multi-threading runtime cannot be initialized"); + // Run the native application runtime.block_on(async { let native_options = eframe::NativeOptions { persist_window: true, // Persist window size and position @@ -24,7 +28,7 @@ fn main() -> eframe::Result<()> { eframe::run_native( "Identity Manager", native_options, - Box::new(|cc| Ok(Box::new(app::AppState::new()))), + Box::new(|_cc| Ok(Box::new(app::AppState::new()))), ) }) } diff --git a/src/platform/contested_names.rs b/src/platform/contested_names.rs index e29af15b8..bf23537eb 100644 --- a/src/platform/contested_names.rs +++ b/src/platform/contested_names.rs @@ -1,35 +1,9 @@ -use dash_sdk::platform::transition::vote::PutVote; - use crate::context::AppContext; use crate::platform::contract::ContractTask; -use crate::platform::BackendTask; +use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; use dash_sdk::dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dash_sdk::dpp::data_contract::DataContract; -use dash_sdk::dpp::{ - data_contract::{accessors::v0::DataContractV0Getters, document_type::DocumentType}, - identifier::Identifier, - platform_value::{string_encoding::Encoding, Value}, - voting::{ - contender_structs::ContenderWithSerializedDocument, - vote_choices::resource_vote_choice::ResourceVoteChoice, - vote_polls::{ - contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll, VotePoll, - }, - votes::{resource_vote::ResourceVote, Vote}, - }, -}; -use dash_sdk::drive::query::{ - vote_poll_vote_state_query::{ - ContestedDocumentVotePollDriveQuery, ContestedDocumentVotePollDriveQueryResultType, - }, - vote_polls_by_document_type_query::VotePollsByDocumentTypeQuery, - VotePollsByEndDateDriveQuery, -}; -use dash_sdk::{ - platform::{DocumentQuery, FetchMany}, - query_types::ContestedResource, - Sdk, -}; +use dash_sdk::drive::query::vote_polls_by_document_type_query::VotePollsByDocumentTypeQuery; +use dash_sdk::{platform::FetchMany, query_types::ContestedResource, Sdk}; #[derive(Debug, Clone, PartialEq)] pub(crate) enum ContestedResourceTask { @@ -44,10 +18,11 @@ impl AppContext { task: ContestedResourceTask, sdk: &Sdk, ) -> Result<(), String> { + let sdk = sdk.clone(); match &task { ContestedResourceTask::QueryDPNSContestedResources => { if self.dpns_contract.is_none() { - self.run_contract_task(ContractTask::FetchDPNSContract, sdk) + self.run_contract_task(ContractTask::FetchDPNSContract, &sdk) .await?; } let Some(data_contract) = self.dpns_contract.as_ref() else { @@ -71,6 +46,7 @@ impl AppContext { let contested_resources = ContestedResource::fetch_many(&sdk, query) .await .map_err(|e| { + tracing::error!("error fetching contested resources: {}", e); format!("error fetching contested resources: {}", e.to_string()) })?; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 84ed4233a..3b2d8be15 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -20,7 +20,7 @@ impl AppContext { } pub async fn run_backend_task(&self, task: BackendTask) -> Result<(), String> { - let sdk = self.sdk.read().expect("expected to get sdk").clone(); + let sdk = self.sdk.clone(); match task { BackendTask::ContractTask(contract_task) => { self.run_contract_task(contract_task, &sdk).await diff --git a/src/sdk_wrapper.rs b/src/sdk_wrapper.rs index a027436a8..05a7ea01e 100644 --- a/src/sdk_wrapper.rs +++ b/src/sdk_wrapper.rs @@ -1,11 +1,12 @@ use crate::config::Config; use crate::logging::initialize_logger; use dash_sdk::dpp::version::PlatformVersion; +use dash_sdk::platform::ContextProvider; use dash_sdk::{RequestSettings, Sdk, SdkBuilder}; // Adjust imports use std::time::Duration; use tracing::info; -pub fn initialize_sdk(config: &Config) -> Sdk { +pub fn initialize_sdk(config: &Config, context_provider: P) -> Sdk { initialize_logger(); // Setup Platform SDK @@ -20,12 +21,7 @@ pub fn initialize_sdk(config: &Config) -> Sdk { let sdk = SdkBuilder::new(address_list) .with_version(PlatformVersion::get(1).unwrap()) .with_network(config.core_network()) - .with_core( - &config.core_host, - config.core_rpc_port, - &config.core_rpc_user, - &config.core_rpc_password, - ) + .with_context_provider(context_provider) .with_settings(request_settings) .build() .expect("Failed to build SDK"); diff --git a/src/ui/add_identity_screen.rs b/src/ui/add_identity_screen.rs index 3904ad05e..1521b69cf 100644 --- a/src/ui/add_identity_screen.rs +++ b/src/ui/add_identity_screen.rs @@ -5,7 +5,7 @@ use crate::model::qualified_identity::EncryptedPrivateKeyTarget::{ }; use crate::model::qualified_identity::{IdentityType, QualifiedIdentity}; use crate::ui::components::top_panel::add_top_panel; -use crate::ui::{MessageType, ScreenLike}; +use crate::ui::ScreenLike; use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; use dash_sdk::dpp::dashcore::PrivateKey; use dash_sdk::dpp::identifier::{Identifier, MasternodeIdentifiers}; @@ -202,21 +202,14 @@ impl ScreenLike for AddIdentityScreen { }; if !error_out { - let sdk_clone = Arc::clone(&self.app_context.sdk); + let sdk = self.app_context.sdk.clone(); // Create the shared state let identity_result = Arc::new(Mutex::new(None)); let identity_result_clone = Arc::clone(&identity_result); - - // Lock the mutex and clone the Sdk - let sdk_instance = { - let sdk = sdk_clone.read().unwrap(); - sdk.clone() // Assuming Sdk implements Clone - }; // Now sdk_instance is owned and can be moved into the async block let network = self.app_context.network; - // Spawn the async task tokio::spawn(async move { // Parse the identity ID @@ -240,8 +233,7 @@ impl ScreenLike for AddIdentityScreen { }; // Fetch the identity using the cloned sdk_instance - let fetch_result = - Identity::fetch_by_identifier(&sdk_instance, identity_id).await; + let fetch_result = Identity::fetch_by_identifier(&sdk, identity_id).await; let mut result = match fetch_result { Ok(Some(identity)) => Ok(identity), @@ -267,8 +259,7 @@ impl ScreenLike for AddIdentityScreen { address.as_ref(), ); let fetch_voter_result = - Identity::fetch_by_identifier(&sdk_instance, voter_identifier) - .await; + Identity::fetch_by_identifier(&sdk, voter_identifier).await; let voter_result = match fetch_voter_result { Ok(Some(identity)) => Ok(identity), From 82186c440cf37f2b4d6d1fd9100cf24ef0cde3ef Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:28:18 +0200 Subject: [PATCH 2/2] chore: minor improvements --- Cargo.toml | 6 +++--- src/context_provider.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 541f80c5a..28e7d7ba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ strum = { version = "0.26.1", features = ["derive"] } bs58 = "0.5.0" base64 = "0.22.1" copypasta = "0.10.1" -platform-serialization-derive = { git = "https://github.com/dashpay/platform", branch = "feat/sdkContestedResourcesFixes2" } -dash-sdk = { git = "https://github.com/dashpay/platform", branch = "feat/sdkContestedResourcesFixes2" } -simple-signer = { git = "https://github.com/dashpay/platform", branch = "feat/sdkContestedResourcesFixes2" } +platform-serialization-derive = { git = "https://github.com/dashpay/platform", branch = "feat/set-context-provider-no-mut" } +dash-sdk = { git = "https://github.com/dashpay/platform", branch = "feat/set-context-provider-no-mut" } +simple-signer = { git = "https://github.com/dashpay/platform", branch = "feat/set-context-provider-no-mut" } thiserror = "1" serde = "1.0.197" serde_json = "1.0.120" diff --git a/src/context_provider.rs b/src/context_provider.rs index ecd8f9038..324a1a667 100644 --- a/src/context_provider.rs +++ b/src/context_provider.rs @@ -1,7 +1,7 @@ use crate::config::Config; use crate::context::AppContext; use crate::database::Database; -use dash_sdk::core_client::CoreClient; +use dash_sdk::core::LowLevelDashCoreClient as CoreClient; use dash_sdk::error::ContextProviderError; use dash_sdk::platform::ContextProvider; use dash_sdk::platform::DataContract;