Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 73 additions & 4 deletions packages/rs-context-provider/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dpp::data_contract::TokenConfiguration;
use dpp::prelude::{CoreBlockHeight, DataContract, Identifier};
use dpp::version::PlatformVersion;
use drive::{error::proof::ProofError, query::ContractLookupFn};
use std::{ops::Deref, sync::Arc};
use std::{future::Future, ops::Deref, pin::Pin, sync::Arc};

#[cfg(feature = "mocks")]
use {
Expand Down Expand Up @@ -62,7 +62,34 @@ pub trait ContextProvider: Send + Sync {
token_id: &Identifier,
) -> Result<Option<TokenConfiguration>, ContextProviderError>;

/// Fetches the public key for a specified quorum.
/// Fetches the public key for a specified quorum asynchronously.
///
/// This is the primary method that should be implemented. The synchronous version
/// [get_quorum_public_key](ContextProvider::get_quorum_public_key) should typically
/// wrap this method using `dash_sdk::sync::block_on()`.
///
/// # Arguments
///
/// * `quorum_type`: The type of the quorum.
/// * `quorum_hash`: The hash of the quorum. This is used to determine which quorum's public key to fetch.
/// * `core_chain_locked_height`: Core chain locked height for which the quorum must be valid
///
/// # Returns
///
/// * `Ok([u8; 48])`: On success, returns a 48-byte array representing the public key of the quorum.
/// * `Err(Error)`: On failure, returns an error indicating why the operation failed.
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> Pin<Box<dyn Future<Output = Result<[u8; 48], ContextProviderError>> + Send + 'static>>;

/// Fetches the public key for a specified quorum synchronously.
///
/// This method is typically implemented as a wrapper around
/// [get_quorum_public_key_async](ContextProvider::get_quorum_public_key_async)
/// using `dash_sdk::sync::block_on()`.
///
/// # Arguments
///
Expand All @@ -72,7 +99,7 @@ pub trait ContextProvider: Send + Sync {
///
/// # Returns
///
/// * `Ok(Vec<u8>)`: On success, returns a byte vector representing the public key of the quorum.
/// * `Ok([u8; 48])`: On success, returns a 48-byte array representing the public key of the quorum.
/// * `Err(Error)`: On failure, returns an error indicating why the operation failed.
fn get_quorum_public_key(
&self,
Expand Down Expand Up @@ -106,6 +133,20 @@ impl<C: AsRef<dyn ContextProvider> + Send + Sync> ContextProvider for C {
self.as_ref().get_token_configuration(token_id)
}

fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> Pin<Box<dyn Future<Output = Result<[u8; 48], ContextProviderError>> + Send + 'static>>
{
self.as_ref().get_quorum_public_key_async(
quorum_type,
quorum_hash,
core_chain_locked_height,
)
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
Expand Down Expand Up @@ -142,6 +183,19 @@ where
lock.get_token_configuration(token_id)
}

fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> Pin<Box<dyn Future<Output = Result<[u8; 48], ContextProviderError>> + Send + 'static>>
{
// Compute synchronously under the lock, then return a ready future.
// This avoids holding the MutexGuard across await points which would be unsound.
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
Expand Down Expand Up @@ -227,7 +281,22 @@ impl Default for MockContextProvider {

#[cfg(feature = "mocks")]
impl ContextProvider for MockContextProvider {
/// Mock implementation of [ContextProvider] that returns keys from files saved on disk.
/// Mock implementation of async [ContextProvider] that returns keys from files saved on disk.
///
/// Use [dash_sdk::SdkBuilder::with_dump_dir()] to generate quorum keys files.
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> Pin<Box<dyn Future<Output = Result<[u8; 48], ContextProviderError>> + Send + 'static>>
{
// For the mock implementation, we just wrap the sync version
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

/// Mock implementation of sync [ContextProvider] that returns keys from files saved on disk.
///
/// Use [dash_sdk::SdkBuilder::with_dump_dir()] to generate quorum keys files.
fn get_quorum_public_key(
Expand Down
17 changes: 17 additions & 0 deletions packages/rs-sdk-ffi/src/context_callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ unsafe impl Send for CallbackContextProvider {}
unsafe impl Sync for CallbackContextProvider {}

impl ContextProvider for CallbackContextProvider {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
// Wrap sync version in async block
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ use std::sync::Arc;
struct MyFallbackProvider;

impl ContextProvider for MyFallbackProvider {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
// Wrap the sync version in an async block
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
_quorum_type: u32,
Expand Down
18 changes: 18 additions & 0 deletions packages/rs-sdk-trusted-context-provider/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,24 @@ impl TrustedHttpContextProvider {
}

impl ContextProvider for TrustedHttpContextProvider {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: QuorumHash,
core_chain_locked_height: CoreBlockHeight,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
// For now, wrap the sync version in an async block
// This could be made truly async in the future
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
Expand Down
64 changes: 48 additions & 16 deletions packages/rs-sdk/src/mock/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,31 @@ impl GrpcContextProvider {
self.dump_dir = dump_dir;
}

/// Internal implementation of get_quorum_public_key (used by both sync and async versions)
fn get_quorum_public_key_sync_impl(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> Result<[u8; 48], ContextProviderError> {
if let Some(key) = self
.quorum_public_keys_cache
.get(&(quorum_hash, quorum_type))
{
return Ok(*key);
};

let key = self.core.get_quorum_public_key(quorum_type, quorum_hash)?;

self.quorum_public_keys_cache
.put((quorum_hash, quorum_type), key);

#[cfg(feature = "mocks")]
self.dump_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height, &key);

Ok(key)
}

/// Save quorum public key to disk.
///
/// Files are named: `quorum_pubkey-<int_quorum_type>-<hex_quorum_hash>.json`
Expand Down Expand Up @@ -162,28 +187,35 @@ impl GrpcContextProvider {
}

impl ContextProvider for GrpcContextProvider {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
// For now, we just wrap the synchronous version in an async block
// In the future, this can be made truly async if the core client supports it
let result = self.get_quorum_public_key_sync_impl(
quorum_type,
quorum_hash,
core_chain_locked_height,
);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
quorum_hash: [u8; 32], // quorum hash is 32 bytes
core_chain_locked_height: u32,
) -> Result<[u8; 48], ContextProviderError> {
if let Some(key) = self
.quorum_public_keys_cache
.get(&(quorum_hash, quorum_type))
{
return Ok(*key);
};

let key = self.core.get_quorum_public_key(quorum_type, quorum_hash)?;

self.quorum_public_keys_cache
.put((quorum_hash, quorum_type), key);

#[cfg(feature = "mocks")]
self.dump_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height, &key);

Ok(key)
self.get_quorum_public_key_sync_impl(quorum_type, quorum_hash, core_chain_locked_height)
}

fn get_data_contract(
Expand Down
33 changes: 33 additions & 0 deletions packages/wasm-sdk/src/context_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ pub struct WasmTrustedContext {
}

impl ContextProvider for WasmContext {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
let result = self.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height);
Box::pin(async move { result })
}

fn get_quorum_public_key(
&self,
_quorum_type: u32,
Expand Down Expand Up @@ -67,6 +83,23 @@ impl ContextProvider for WasmContext {
}

impl ContextProvider for WasmTrustedContext {
fn get_quorum_public_key_async(
&self,
quorum_type: u32,
quorum_hash: [u8; 32],
core_chain_locked_height: u32,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<[u8; 48], ContextProviderError>>
+ Send
+ 'static,
>,
> {
// Delegate to the inner provider
self.inner
.get_quorum_public_key_async(quorum_type, quorum_hash, core_chain_locked_height)
}

fn get_quorum_public_key(
&self,
quorum_type: u32,
Expand Down
Loading