Skip to content

Commit

Permalink
fix(sdk): panic GrpcContextProvider on async call inside sync code (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lklimek authored Jul 3, 2024
1 parent 815e747 commit 868be18
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 32 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/rs-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "1.0.0-dev.16"
edition = "2021"

[dependencies]
arc-swap = { version = "1.7.1" }
dpp = { path = "../rs-dpp", default-features = false, features = [
"dash-sdk-features",
] }
Expand Down Expand Up @@ -34,7 +35,7 @@ derive_more = { version = "0.99.17" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore-rpc", tag = "v0.15.2" }
lru = { version = "0.12.3", optional = true }
bip37-bloom-filter = { git = "https://github.com/dashpay/rs-bip37-bloom-filter", branch = "develop" }

pollster = { version = "0.3.0" }

[dev-dependencies]
tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] }
Expand Down
45 changes: 19 additions & 26 deletions packages/rs-sdk/src/mock/provider.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//! Example ContextProvider that uses the Core gRPC API to fetch data from the platform.

use std::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::Arc;

use dpp::prelude::{DataContract, Identifier};
use drive_proof_verifier::error::ContextProviderError;
use drive_proof_verifier::ContextProvider;

use crate::core_client::CoreClient;
use crate::platform::Fetch;
use crate::{Error, Sdk};
use arc_swap::ArcSwapAny;
use dpp::prelude::{DataContract, Identifier};
use drive_proof_verifier::error::ContextProviderError;
use drive_proof_verifier::ContextProvider;
use pollster::FutureExt;
use std::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::Arc;

/// Context provider that uses the Core gRPC API to fetch data from the platform.
///
Expand All @@ -24,7 +24,7 @@ pub struct GrpcContextProvider {
/// values set by the user in the caches: `data_contracts_cache`, `quorum_public_keys_cache`.
///
/// We use [Arc] as we have circular dependencies between Sdk and ContextProvider.
sdk: Option<Sdk>,
sdk: ArcSwapAny<Arc<Option<Sdk>>>,

/// Data contracts cache.
///
Expand Down Expand Up @@ -65,7 +65,7 @@ impl GrpcContextProvider {
let core_client = CoreClient::new(core_ip, core_port, core_user, core_password)?;
Ok(Self {
core: core_client,
sdk,
sdk: ArcSwapAny::new(Arc::new(sdk)),
data_contracts_cache: Cache::new(data_contracts_cache_size),
quorum_public_keys_cache: Cache::new(quorum_public_keys_cache_size),
#[cfg(feature = "mocks")]
Expand All @@ -78,8 +78,8 @@ impl GrpcContextProvider {
///
/// Note that if the `sdk` is `None`, the context provider will not be able to fetch data itself and will rely on
/// values set by the user in the caches: `data_contracts_cache`, `quorum_public_keys_cache`.
pub fn set_sdk(&mut self, sdk: Option<Sdk>) {
self.sdk = sdk;
pub fn set_sdk(&self, sdk: Option<Sdk>) {
self.sdk.store(Arc::new(sdk));
}
/// Set the directory where to store dumped data.
///
Expand Down Expand Up @@ -157,29 +157,22 @@ impl ContextProvider for GrpcContextProvider {
if let Some(contract) = self.data_contracts_cache.get(data_contract_id) {
return Ok(Some(contract));
};
// let sdk_guard = self.sdk.read().expect("lock poisoned");
let sdk_guard = self.sdk.load();

let sdk = match &self.sdk {
let sdk = match sdk_guard.as_ref() {
Some(sdk) => sdk,
None => {
tracing::warn!("data contract cache miss and no sdk provided, skipping fetch");
return Ok(None);
}
};

let handle = match tokio::runtime::Handle::try_current() {
Ok(handle) => handle,
// not an error, we rely on the caller to provide a data contract using
Err(e) => {
tracing::warn!(
error = e.to_string(),
"data contract cache miss and no tokio runtime detected, skipping fetch"
);
return Ok(None);
}
};
let contract_id = *data_contract_id;
let sdk_cloned = sdk.clone();

let data_contract = handle
.block_on(DataContract::fetch(sdk, *data_contract_id))
let data_contract: Option<DataContract> = DataContract::fetch(&sdk_cloned, contract_id)
.block_on()
.map_err(|e| ContextProviderError::InvalidDataContract(e.to_string()))?;

if let Some(ref dc) = data_contract {
Expand Down
13 changes: 8 additions & 5 deletions packages/rs-sdk/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,15 +707,18 @@ impl SdkBuilder {
tracing::warn!("ContextProvider not set; mocking with Dash Core. \
Please provide your own ContextProvider with SdkBuilder::with_context_provider().");

let mut context_provider = GrpcContextProvider::new(Some(sdk.clone()),
&self.core_ip, self.core_port, &self.core_user, &self.core_password,
self.data_contract_cache_size, self.quorum_public_keys_cache_size)?;
let mut context_provider = GrpcContextProvider::new(None,
&self.core_ip, self.core_port, &self.core_user, &self.core_password,
self.data_contract_cache_size, self.quorum_public_keys_cache_size)?;
#[cfg(feature = "mocks")]
if sdk.dump_dir.is_some() {
context_provider.set_dump_dir(sdk.dump_dir.clone());
}

sdk.context_provider.replace(Arc::new(Box::new(context_provider)));
// We have cyclical dependency Sdk <-> GrpcContextProvider, so we just do some
// workaround using additional Arc.
let context_provider=Arc::new(context_provider);
sdk.context_provider.replace(Arc::new(Box::new(context_provider.clone())));
context_provider.set_sdk(Some(sdk.clone()));
} else{
tracing::warn!(
"Configure ContextProvider with Sdk::with_context_provider(); otherwise Sdk will fail");
Expand Down
6 changes: 6 additions & 0 deletions packages/wasm-dpp/src/errors/consensus/consensus_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ pub fn from_consensus_error_ref(e: &DPPConsensusError) -> JsValue {
DPPConsensusError::StateError(state_error) => from_state_error(state_error),
DPPConsensusError::BasicError(basic_error) => from_basic_error(basic_error),
DPPConsensusError::DefaultError => JsError::new("DefaultError").into(),
#[cfg(test)]
e => todo!(
"ConsensusError {} not implemented: {}",
std::any::type_name_of_val(e),
e
),
}
}

Expand Down

0 comments on commit 868be18

Please sign in to comment.