Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sdk)!: mock sdk cannot find quorum keys in offline mode #2061

Merged
merged 4 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
71 changes: 44 additions & 27 deletions packages/rs-sdk/src/mock/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
//! See [MockDashPlatformSdk] for more details.
use crate::{
platform::{types::identity::IdentityRequest, DocumentQuery, Fetch, FetchMany, Query},
Error,
Error, Sdk,
};
use arc_swap::ArcSwapOption;
use dapi_grpc::platform::v0::{Proof, ResponseMetadata};
use dapi_grpc::{
mock::Mockable,
platform::v0::{self as proto},
};
use dpp::dashcore::Network;
use dpp::version::PlatformVersion;
use drive_proof_verifier::{error::ContextProviderError, FromProof, MockContextProvider};
use drive_proof_verifier::{error::ContextProviderError, ContextProvider, FromProof};
use rs_dapi_client::mock::MockError;
use rs_dapi_client::{
mock::{Key, MockDapiClient},
Expand All @@ -34,47 +35,55 @@ use super::MockResponse;
/// ## Panics
///
/// Can panic on errors.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct MockDashPlatformSdk {
from_proof_expectations: BTreeMap<Key, Vec<u8>>,
platform_version: &'static PlatformVersion,
dapi: Arc<Mutex<MockDapiClient>>,
prove: bool,
quorum_provider: Option<Arc<MockContextProvider>>,
sdk: ArcSwapOption<Sdk>,
}

impl MockDashPlatformSdk {
pub(crate) fn new(
version: &'static PlatformVersion,
dapi: Arc<Mutex<MockDapiClient>>,
prove: bool,
) -> Self {
/// Returns true when requests should use proofs.
///
/// ## Panics
///
/// Panics when sdk is not set during initialization.
pub fn prove(&self) -> bool {
if let Some(sdk) = self.sdk.load().as_ref() {
sdk.prove()
} else {
panic!("sdk must be set when creating mock ")
}
}

/// Create new mock SDK.
///
/// ## Note
///
/// You have to call [MockDashPlatformSdk::with_sdk()] to set sdk, otherwise Mock SDK will panic.
pub(crate) fn new(version: &'static PlatformVersion, dapi: Arc<Mutex<MockDapiClient>>) -> Self {
Self {
from_proof_expectations: Default::default(),
platform_version: version,
dapi,
prove,
quorum_provider: None,
sdk: ArcSwapOption::new(None),
}
}

pub(crate) fn version<'v>(&self) -> &'v PlatformVersion {
self.platform_version
pub(crate) fn set_sdk(&mut self, sdk: Sdk) {
self.sdk.store(Some(Arc::new(sdk)));
}
/// Define a directory where files containing quorum information, like quorum public keys, are stored.
///
/// This directory will be used to load quorum information from files.
/// You can use [SdkBuilder::with_dump_dir()](crate::SdkBuilder::with_dump_dir()) to generate these files.
pub fn quorum_info_dir<P: AsRef<std::path::Path>>(&mut self, dir: P) -> &mut Self {
let mut provider = MockContextProvider::new();
provider.quorum_keys_dir(Some(dir.as_ref().to_path_buf()));
self.quorum_provider = Some(Arc::new(provider));

self
pub(crate) fn version<'v>(&self) -> &'v PlatformVersion {
self.platform_version
}

/// Load all expectations from files in a directory.
///
///
/// By default, mock expectations are loaded when Sdk is built with [SdkBuilder::build()](crate::SdkBuilder::build()).
/// This function can be used to load expectations after the Sdk is created, or use alternative location.
/// Expectation files must be prefixed with [DapiClient::DUMP_FILE_PREFIX] and
/// have `.json` extension.
pub async fn load_expectations<P: AsRef<std::path::Path>>(
Expand Down Expand Up @@ -278,7 +287,7 @@ impl MockDashPlatformSdk {
where
<<O as Fetch>::Request as TransportRequest>::Response: Default,
{
let grpc_request = query.query(self.prove).expect("query must be correct");
let grpc_request = query.query(self.prove()).expect("query must be correct");
self.expect(grpc_request, object).await?;

Ok(self)
Expand Down Expand Up @@ -332,7 +341,7 @@ impl MockDashPlatformSdk {
Response = <<O as FetchMany<K, R>>::Request as TransportRequest>::Response,
> + Sync,
{
let grpc_request = query.query(self.prove).expect("query must be correct");
let grpc_request = query.query(self.prove()).expect("query must be correct");
self.expect(grpc_request, objects).await?;

Ok(self)
Expand Down Expand Up @@ -393,7 +402,7 @@ impl MockDashPlatformSdk {
),
None => {
let version = self.version();
let provider = self.quorum_provider.as_ref()
let provider = self.context_provider()
.ok_or(ContextProviderError::InvalidQuorum(
"expectation not found and quorum info provider not initialized with sdk.mock().quorum_info_dir()".to_string()
))?;
Expand All @@ -402,11 +411,19 @@ impl MockDashPlatformSdk {
response,
Network::Regtest,
version,
provider,
&provider,
)?
}
};

Ok(data)
}
/// Return context provider implementation defined for upstreeam Sdk object.
fn context_provider(&self) -> Option<impl ContextProvider> {
if let Some(sdk) = self.sdk.load_full() {
sdk.clone().context_provider()
} else {
None
}
}
}
28 changes: 22 additions & 6 deletions packages/rs-sdk/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,21 +796,37 @@ impl SdkBuilder {
None => {
let dapi =Arc::new(tokio::sync::Mutex::new( MockDapiClient::new()));
// We create mock context provider that will use the mock DAPI client to retrieve data contracts.
let context_provider = self.context_provider.unwrap_or(Box::new(MockContextProvider::new()));

Sdk {
let context_provider = self.context_provider.unwrap_or_else(||{
let mut cp=MockContextProvider::new();
if let Some(ref dump_dir) = self.dump_dir {
cp.quorum_keys_dir(Some(dump_dir.clone()));
}
Box::new(cp)
}
);
let mock_sdk = MockDashPlatformSdk::new(self.version, Arc::clone(&dapi));
let mock_sdk = Arc::new(Mutex::new(mock_sdk));
let sdk= Sdk {
network: self.network,
inner:SdkInstance::Mock {
mock:Arc::new(Mutex::new( MockDashPlatformSdk::new(self.version, Arc::clone(&dapi), self.proofs))),
mock:mock_sdk.clone(),
dapi,
version:self.version,

},
dump_dir: self.dump_dir,
dump_dir: self.dump_dir.clone(),
proofs:self.proofs,
internal_cache: Default::default(),
context_provider:Some(Arc::new(context_provider)),
cancel_token: self.cancel_token,
}
};
let mut guard = mock_sdk.try_lock().expect("mock sdk is in use by another thread and connot be reconfigured");
guard.set_sdk(sdk.clone());
if let Some(ref dump_dir) = self.dump_dir {
pollster::block_on( guard.load_expectations(dump_dir))?;
};

sdk
},
#[cfg(not(feature = "mocks"))]
None => return Err(Error::Config("Mock mode is not available. Please enable `mocks` feature or provide address list.".to_string())),
Expand Down
28 changes: 13 additions & 15 deletions packages/rs-sdk/tests/fetch/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub struct Config {
#[serde(default = "Config::default_document_id")]
pub existing_document_id: Identifier,
// Hex-encoded ProTxHash of the existing HP masternode
#[serde(default)]
#[serde(default = "Config::default_protxhash")]
pub masternode_owner_pro_reg_tx_hash: String,
}

Expand Down Expand Up @@ -208,18 +208,10 @@ impl Config {
// offline testing takes precedence over network testing
#[cfg(feature = "offline-testing")]
let sdk = {
let mut mock_sdk = dash_sdk::SdkBuilder::new_mock()
dash_sdk::SdkBuilder::new_mock()
.with_dump_dir(&dump_dir)
.build()
.expect("initialize api");

mock_sdk
.mock()
.quorum_info_dir(&dump_dir)
.load_expectations(&dump_dir)
.await
.expect("load expectations");

mock_sdk
.expect("initialize api")
};

sdk
Expand All @@ -231,11 +223,10 @@ impl Config {
// Next time we need to do it again and update this value :(. This is terrible.
// We should automate creation of identity for SDK tests when we have time.
Identifier::from_string(
"J2aTnrrc8eea3pQBY91QisM3QH5FM9JK11mQCVwxeMqj",
Encoding::Base58,
"a1534e47f60be71e823a9dbc9ceb6d3ea9f1ebde7a3773f03e49ef31c7d9c044",
Encoding::Hex,
)
.unwrap()
.into()
}

fn default_data_contract_id() -> Identifier {
Expand All @@ -255,6 +246,13 @@ impl Config {
.join("vectors")
}

/// Existing masternode proTxHash. Must be updated every time test vectors are regenerated.
///
/// See documentation of [contested_resource_identity_votes_ok](super::contested_resource_identity_votes::contested_resource_identity_votes_ok).
fn default_protxhash() -> String {
String::from("d10bf435af7c75f5b07b09486af1212469d69fdc787589548e315776bc1052a1")
}

/// Return ProTxHash of an existing evo node, or None if not set
pub fn existing_protxhash(&self) -> Result<ProTxHash, String> {
hex::decode(&self.masternode_owner_pro_reg_tx_hash)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ async fn contested_resource_identity_votes_not_found() {
/// 0. Ensure you have at least 1 contested DPNS name in the system.
/// See [check_mn_voting_prerequisities](super::contested_resource::check_mn_voting_prerequisities) for more details.
///
/// 1. Grep log output of `yarn setup` to find `ProRegTx transaction ID` and `Owner Private Key`.
/// Use `ProRegTx transaction ID` to set `DASH_SDK_MASTERNODE_OWNER_PRO_REG_TX_HASH` in `packages/rs-sdk/tests/.env`.
/// 1. Grep log output of `yarn setup` (see logs/setup.log) to find `ProRegTx transaction ID` and `Owner Private Key`:
/// ```bash
/// egrep '(ProRegTx transaction ID|Owner Private Key)' logs/setup.log|head -n2
/// ```
/// Hardcode `ProRegTx transaction ID` in [Config::default_protxhash].
///
/// 2. Load masternode identity into [rs-platform-explorer](https://github.com/dashpay/rs-platform-explorer/):
///
Expand All @@ -78,7 +81,7 @@ async fn contested_resource_identity_votes_not_found() {
ignore = "requires manual DPNS names setup for masternode voting tests; see docs of contested_resource_identity_votes_ok()"
)]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn contested_resource_identity_votes_ok() {
pub(super) async fn contested_resource_identity_votes_ok() {
setup_logs();

let cfg = Config::new();
Expand Down
3 changes: 1 addition & 2 deletions packages/rs-sdk/tests/fetch/data_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,10 @@ async fn test_data_contracts_2_nx() {
not(feature = "offline-testing"),
ignore = "This test requires special procedure to regenerate test vectors"
)]
#[ignore = "todo: Lukazs to re-enable"]
async fn test_data_contract_history_read() {
let cfg = Config::new();
let id = Identifier::from_string(
"df73e40f62ab71d99254037226093f38f683b28cf552f3459d2bdc28614191e0",
"20d16030541c0494e84064e2e72b5ec620546305849a2f9d5893a5e65072364d",
Encoding::Hex,
)
.unwrap();
Expand Down
Loading
Loading