Skip to content
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! server derives the outer by-ids query from the inner results, and the
//! verifier re-derives it from the PROVEN inner results, so the join can
//! never be steered by the responding node. See
//! `drive::query::drive_chained_document_query` for the trust model.
//! `drive::query::chained_document_query` for the trust model.

use crate::documents::document_query::DocumentQuery;
use crate::error::Error;
Expand All @@ -20,7 +20,6 @@ use dpp::dashcore::Network;
use dpp::data_contract::accessors::v0::DataContractV0Getters;
use dpp::version::{PlatformVersion, TryFromPlatformVersioned};
use dpp::ProtocolError;
use drive::query::drive_chained_document_query::DriveChainedDocumentQuery;
use drive::query::DriveDocumentQuery;
use drive_proof_verifier::{
verify_chained_documents_tenderdash_proof, ChainedDocuments, FromProof,
Expand Down Expand Up @@ -122,7 +121,7 @@ impl TryFromPlatformVersioned<ChainedDocumentQuery> for GetDocumentsRequest {
}
}

impl<'a> TryFrom<&'a ChainedDocumentQuery> for DriveChainedDocumentQuery<'a> {
impl<'a> TryFrom<&'a ChainedDocumentQuery> for DriveDocumentQuery<'a> {
type Error = Error;

fn try_from(request: &'a ChainedDocumentQuery) -> Result<Self, Self::Error> {
Expand All @@ -132,11 +131,7 @@ impl<'a> TryFrom<&'a ChainedDocumentQuery> for DriveChainedDocumentQuery<'a> {
.data_contract
.document_type_for_name(&request.outer_document_type_name)
.map_err(|e| Error::Protocol(ProtocolError::DataContractError(e)))?;
Ok(DriveChainedDocumentQuery {
inner,
join_property: request.join_property.clone(),
outer_document_type,
})
Ok(inner.with_by_id_join(request.join_property.clone(), outer_document_type))
}
}

Expand All @@ -157,7 +152,7 @@ impl FromProof<ChainedDocumentQuery> for ChainedDocuments {
let request: Self::Request = request.into();
let response: Self::Response = response.into();

let query: DriveChainedDocumentQuery = (&request).try_into().map_err(|e: Error| {
let query: DriveDocumentQuery = (&request).try_into().map_err(|e: Error| {
drive_proof_verifier::Error::RequestError {
error: e.to_string(),
}
Expand Down Expand Up @@ -202,7 +197,9 @@ mod tests {
use dpp::data_contract::DataContract;
use dpp::platform_value::Value;
use dpp::tests::json_document::json_document_to_contract;
use drive::query::{WhereClause, WhereOperator};
use drive::query::{
BindingSource, DriveSubQuery, SubQueryBinding, SubQueryKind, WhereClause, WhereOperator,
};
use std::sync::Arc;

const YAPPR_CONTRACT_PATH: &str =
Expand Down Expand Up @@ -276,13 +273,80 @@ mod tests {
#[test]
fn converts_to_a_valid_drive_query() {
let query = posts_i_liked(10);
let drive_query: DriveChainedDocumentQuery =
let drive_query: DriveDocumentQuery =
(&query).try_into().expect("converts to a drive query");
drive_query
.validate(platform_version())
.validate_chained(platform_version())
.expect("the byLiker shape validates");
assert_eq!(drive_query.join_property, "postId");
assert_eq!(drive_query.inner.limit, Some(10));
assert_eq!(
drive_query.sub_queries[0]
.binding
.as_ref()
.expect("the join is bound")
.source_property,
"postId"
);
assert_eq!(drive_query.limit, Some(10));
}

fn assert_plain_conversions_refuse(query: &DriveDocumentQuery) {
for result in [
DocumentQuery::try_from(query),
DocumentQuery::try_from(query.clone()),
DocumentQuery::new_with_drive_query(query),
] {
assert!(
matches!(&result, Err(Error::Config(message)) if message.contains("sub-queries")),
"a plain conversion must refuse the composition, got {result:?}"
);
}
}

#[test]
fn should_refuse_dropping_a_drive_join_during_plain_query_conversion() {
let query = posts_i_liked(10);
let drive_query: DriveDocumentQuery = (&query).try_into().expect("drive query");
drive_query
.validate_chained(platform_version())
.expect("valid chained shape");
assert_plain_conversions_refuse(&drive_query);
}

#[test]
fn should_refuse_dropping_a_composite_count_during_plain_query_conversion() {
let query = posts_i_liked(10);
let page: DriveDocumentQuery = (&query.inner).try_into().expect("drive page");
let count = DriveSubQuery {
contract: page.contract,
document_type: page.document_type,
kind: SubQueryKind::Count,
where_clauses: vec![],
order_by: vec![],
limit: None,
binding: Some(SubQueryBinding {
source: BindingSource::Page,
source_property: "postId".into(),
field: "postId".into(),
}),
};
let composite = page.with_sub_queries(vec![count]);
composite
.validate_composite(platform_version())
.expect("valid count composition");
assert_plain_conversions_refuse(&composite);
}

#[test]
fn should_preserve_plain_drive_query_conversion() {
let query = posts_i_liked(10).inner;
let drive_query: DriveDocumentQuery = (&query).try_into().expect("drive page");
for result in [
DocumentQuery::try_from(&drive_query),
DocumentQuery::try_from(drive_query.clone()),
DocumentQuery::new_with_drive_query(&drive_query),
] {
assert_eq!(result.expect("plain conversion succeeds"), query);
}
}

#[test]
Expand All @@ -294,9 +358,9 @@ mod tests {
"hashtag",
"post",
);
let drive_query: DriveChainedDocumentQuery =
let drive_query: DriveDocumentQuery =
(&query).try_into().expect("conversion itself succeeds");
let refused = drive_query.validate(platform_version());
let refused = drive_query.validate_chained(platform_version());
assert!(
refused.is_err(),
"a non-refersTo join property must fail validation"
Expand Down
19 changes: 18 additions & 1 deletion packages/dash-platform-queries/src/documents/document_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ impl DocumentQuery {

/// Create new document query based on a [DriveDocumentQuery].
///
/// Fails when the drive query carries sub-queries, which this plain
/// query cannot preserve — use the chained or composite surface.
///
/// Fails when the drive query carries time-range resolution provenance
/// (`resolved_time_ranges`): the resolved bucket equality cannot be
/// represented without it — see the `TryFrom` impl. Build the query
Expand Down Expand Up @@ -973,6 +976,9 @@ fn encode_v0(
impl<'a> TryFrom<&'a DriveDocumentQuery<'a>> for DocumentQuery {
type Error = crate::error::Error;

/// Refuses sub-queries: a plain `DocumentQuery` cannot carry their
/// selections through SDK request construction and proof verification.
///
/// Fallible by necessity: a drive query carrying `resolved_time_ranges`
/// holds bucket-start equalities whose meaning lives in the provenance,
/// and `DocumentQuery` has no field to carry it — the original
Expand All @@ -982,6 +988,14 @@ impl<'a> TryFrom<&'a DriveDocumentQuery<'a>> for DocumentQuery {
/// contract then rejects the request, while a contract with a competing
/// plain index returns a different — but validly proven — result.
fn try_from(value: &'a DriveDocumentQuery<'a>) -> Result<Self, Self::Error> {
if !value.sub_queries.is_empty() {
return Err(Error::Config(
"a drive query carrying sub-queries cannot be converted to a plain \
DocumentQuery: its sub-queries would be discarded. Use the chained or \
composite query surface instead"
.to_string(),
));
}
if !value.resolved_time_ranges.is_empty() {
return Err(Error::Config(
"a drive query carrying time-range resolution provenance cannot be \
Expand Down Expand Up @@ -1031,7 +1045,7 @@ impl<'a> TryFrom<DriveDocumentQuery<'a>> for DocumentQuery {
type Error = crate::error::Error;

/// By-value twin of the by-reference conversion above — same
/// provenance rejection, same rationale.
/// sub-query and provenance rejections, same rationale.
fn try_from(value: DriveDocumentQuery<'a>) -> Result<Self, Self::Error> {
DocumentQuery::try_from(&value)
}
Expand Down Expand Up @@ -1155,6 +1169,9 @@ impl<'a> TryFrom<&'a DocumentQuery> for DriveDocumentQuery<'a> {
// selections assign the fields they resolved onto the returned
// query; everything else is a raw query.
resolved_time_ranges: vec![],
// Composite sub-queries have no wire format yet: a query
// parsed from a request is always a plain page.
sub_queries: vec![],
};

Ok(query)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl<C> Platform<C> {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ pub(super) fn create_domain_data_trigger_v0(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down Expand Up @@ -341,6 +342,7 @@ pub(super) fn create_domain_data_trigger_v0(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ pub(super) fn create_domain_data_trigger_v1(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// Diff vs `_v0` (parent-domain query):
Expand Down Expand Up @@ -356,6 +357,7 @@ pub(super) fn create_domain_data_trigger_v1(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// Diff vs `_v0` (preorder query): same change as above. `_v0`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub(super) fn delete_withdrawal_data_trigger_v0(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub(super) fn delete_withdrawal_data_trigger_v1(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// Diff vs `_v0` (withdrawal-document lookup):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ fn fetch_documents_for_transitions_knowing_contract_and_document_type_v0(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down Expand Up @@ -184,6 +185,7 @@ fn fetch_documents_for_transitions_knowing_contract_and_document_type_v1(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// Diff vs `_v0`: epoch is `Some(...)` and the cost is billed via
Expand Down Expand Up @@ -310,6 +312,7 @@ fn fetch_document_with_id_v0(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// todo: deal with cost of this operation
Expand Down Expand Up @@ -373,6 +376,7 @@ fn fetch_document_with_id_v1(
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

// Diff vs `_v0`: epoch is `Some(...)` and the cost is billed via
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ mod dpns_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

let documents = platform
Expand Down Expand Up @@ -505,6 +506,7 @@ mod dpns_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

let documents = platform
Expand Down Expand Up @@ -914,6 +916,7 @@ mod dpns_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

let documents = platform
Expand Down Expand Up @@ -949,6 +952,7 @@ mod dpns_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

let documents = platform
Expand Down Expand Up @@ -1183,6 +1187,7 @@ mod dpns_username_transfer_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

platform
Expand Down Expand Up @@ -1435,6 +1440,7 @@ mod dpns_username_transfer_tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};

platform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2601,6 +2601,7 @@ mod tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};
query.internal_clauses.equal_clauses.insert(
"contractId".to_string(),
Expand Down Expand Up @@ -2984,6 +2985,7 @@ mod tests {
start_at_included: false,
block_time_ms: None,
resolved_time_ranges: vec![],
sub_queries: vec![],
};
query.internal_clauses.equal_clauses.insert(
"contractId".to_string(),
Expand Down
Loading
Loading