diff --git a/Cargo.toml b/Cargo.toml index 3f7c7fe89..0085b0405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,16 +8,16 @@ 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" 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" @@ -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,9 +43,10 @@ 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" } serde_yaml = "0.9.34+deprecated" - diff --git a/src/app.rs b/src/app.rs index d1d7ff18e..8fcd016a6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -93,8 +93,8 @@ impl AppState { pub fn new() -> Self { initialize_logger(); let mainnet_app_context = - Arc::new(AppContext::new(Network::Dash).expect("expected Dash config for mainnet")); - let testnet_app_context = AppContext::new(Network::Testnet).map(Arc::new); + AppContext::new(Network::Dash).expect("expected Dash config for mainnet"); + let testnet_app_context = AppContext::new(Network::Testnet); let identities_screen = IdentitiesScreen::new(&mainnet_app_context); let dpns_contested_names_screen = DPNSContestedNamesScreen::new(&mainnet_app_context); diff --git a/src/context.rs b/src/context.rs index 1c8fd45c1..8febad789 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,5 @@ use crate::config::{Config, NetworkConfig}; +use crate::context_provider::Provider; use crate::database::Database; use crate::model::contested_name::ContestedName; use crate::model::qualified_identity::QualifiedIdentity; @@ -9,21 +10,21 @@ 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: NetworkConfig, pub(crate) dpns_contract: Arc>, pub(crate) platform_version: &'static PlatformVersion, } impl AppContext { - pub fn new(network: Network) -> Option { + pub fn new(network: Network) -> Option> { let db = Arc::new(Database::new("identities.db").unwrap()); let config = Config::load(); @@ -32,7 +33,11 @@ impl AppContext { let network_config = config.config_for_network(network).clone()?; - let sdk = Arc::new(RwLock::new(initialize_sdk(&network_config, network))); + // 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(), &network_config).expect("Failed to initialize SDK"); + + let sdk = initialize_sdk(&network_config, network, provider.clone()); let mut app_context = AppContext { network, @@ -52,6 +57,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()); Some(app_context) } diff --git a/src/context_provider.rs b/src/context_provider.rs new file mode 100644 index 000000000..8ae2ff05c --- /dev/null +++ b/src/context_provider.rs @@ -0,0 +1,98 @@ +use crate::config::NetworkConfig; +use crate::context::AppContext; +use crate::database::Database; +use dash_sdk::core::LowLevelDashCoreClient as 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: &NetworkConfig) -> 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 84ab5df9f..956c36ace 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -23,7 +23,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 9cc2aaf2d..b47f25679 100644 --- a/src/sdk_wrapper.rs +++ b/src/sdk_wrapper.rs @@ -2,11 +2,16 @@ use crate::config::{Config, NetworkConfig}; use crate::logging::initialize_logger; use dash_sdk::dpp::dashcore::Network; 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: &NetworkConfig, network: Network) -> Sdk { +pub fn initialize_sdk( + config: &NetworkConfig, + network: Network, + context_provider: P, +) -> Sdk { // Setup Platform SDK let address_list = config.dapi_address_list(); let request_settings = RequestSettings { @@ -19,12 +24,7 @@ pub fn initialize_sdk(config: &NetworkConfig, network: Network) -> Sdk { let sdk = SdkBuilder::new(address_list) .with_version(PlatformVersion::get(1).unwrap()) .with_network(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");