Skip to content

feat(sdk): query the elements of typed arrays over the wire (PV14) - #5372

Merged
QuantumExplorer merged 37 commits into
v5.0-devfrom
claude/typed-array-element-indexes-sdk
Oct 12, 2026
Merged

QuantumExplorer merged 37 commits into
v5.0-devfrom
claude/typed-array-element-indexes-sdk

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Basic explanation

#5370 lets Platform index the items of a list, such as a post's hashtags, and query them with two new operators: contains (one item) and containsAny (any of several). This PR carries those operators over the network and through every SDK, so an app can ask for "posts tagged #dash" from JavaScript, Rust, Swift or Kotlin and check the proof it gets back.

#5370 is merged; this PR now targets v5.0-dev directly.

Issue being fixed or feature implemented

The wire and SDK half of #3696. Until this lands, the SDK refuses to encode contains and containsAny (the placeholder in #5370), so they reach Drive only through the CBOR v0 request.

Before and after

Before After
where: [['hashtags', 'contains', 'dash']] in evo-sdk Unknown operator: contains the posts tagged dash, proved
where: [['hashtags', 'containsAny', ['dash', 'news']]], orderBy: [['hashtags', 'asc']] Unknown operator: containsAny each matching post once, proved
count grouped by hashtags with containsAny refused one verified count per element
"contains" in the FFI (Swift, Kotlin) Operator 'contains' is not supported passed to Drive

What was done?

  • Proto: GetDocumentsRequest.WhereOperator gains CONTAINS = 13 and CONTAINS_ANY = 14, documented. The JS, web, Objective-C and Python clients are regenerated (yarn workspace @dashevo/dapi-grpc build). The only differences are the two values, apart from the Python descriptor bytes.
  • drive-abci: where_operator_from_proto maps the two values. Aggregate routing v2, which only PV14 selects, routes a containsAny as the in it keys like, so a count, sum or average grouped by the array gives one entry per element. It routes a copy of the clauses; Drive keys the ones sent. The PV13 table (v0) is untouched.
  • dash-platform-queries:
    • where_operator_to_proto maps the two operators (no longer fallible).
    • The encoder refuses them below protocol version 14, judged by contract grammar generation, as it does for the window selections. The check reads the page's clauses and every composite sub-query's, which ride the same wire mapping.
    • Before verifying, the count, sum, average, ranked and having proof helpers turn element clauses into equality and in clauses, as the server does. Otherwise mode detection and the rebuilt path query would differ from the prover's. Documents proofs already go through Drive's query, which drops repeats.
    • They apply the server's rule for reading several elements, not a blanket allowance. Count, sum and average read several only when grouped by the array alone with no range clause (grouped_by_in_field, the SDK's copy of drive-abci's routing check, handed to Drive's key_array_element_clauses, which holds the rule); ranked and having read one. So the SDK refuses to verify a proof of a total over several elements, which a dishonest node could build from per-element counts and which would count a post holding two of them twice.
    • The single-total views (DocumentCount, DocumentSum, DocumentAverage) add the verified entries up, so they refuse several elements even grouped by the array; the split views keep one entry per element.
    • One helper, aggregate_where_clauses, keys and canonicalizes for the count, sum and average verifiers, on the document type each already resolved. key_element_clauses does it for the ranked and having verifiers and the wasm-sdk pre-checks.
  • wasm-sdk: parse_where_clause accepts contains/Contains and containsAny/ContainsAny, and the DocumentWhereOperator TypeScript type lists them. The ranked and having pre-checks key a contains pin as the server does, through the shared helper.
  • FFI (search.rs and count.rs): contains and containsAny map to the operators. Swift and Kotlin pass operator strings through, so they need nothing else.
  • wasm-drive-verify: the three document proof verifiers accept Contains and ContainsAny.
  • evo-sdk README: a section on querying the elements of a list, with the rules for each operator.

How Has This Been Tested?

  • drive-abci array_element_wire_tests:
    • CONTAINS documents query, with and without proof;
    • CONTAINS_ANY returning each post once;
    • CONTAINS_ANY grouped count per element;
    • an EQUAL on the array refused on the wire;
    • at protocol version 13, a CONTAINS documents read refused, and a CONTAINS_ANY grouped count refused by routing v0 as before, with a limit and without.
  • drive-abci array_element_proof_round_trips: proofs from the v1 handler verified through the SDK's FromProof<DocumentQuery> entry points:
    • CONTAINS_ANY documents, each post once though one holds two of the elements (also checked on Drive's verified page, a list, since the SDK's Documents map would hide a repeat);
    • per-element counts (DocumentSplitCounts) and sums (DocumentSplitSums);
    • a pinned element's average (DocumentAverage);
    • a ranking and a having bound within one element (DocumentRankedEntries, DocumentHavingEntries);
    • a grouped count proof offered for the same request ungrouped, refused by the SDK. With the earlier blanket allowance it verified a count of 5 for 4 posts;
    • the grouped per-element count, sum and average proofs offered to DocumentCount, DocumentSum and DocumentAverage, all refused.
  • dash-platform-queries: the two operators encode to 13 and 14 at protocol version 14, and are refused with a version message at 13, in the page or only in a sub-query. The verifier rule answers several elements only grouped by the array alone without a range, refuses an ungrouped containsAny total, and keys containsAny as in and contains as an equality.
  • Suites run, after merging v5.0-dev (with feat(platform)!: index the elements of typed arrays (PV14) #5370): cargo test -p drive-abci --lib -- query::document_query::v1 (135 passed), -p dash-platform-queries (63), -p rs-sdk-ffi --lib -- document::queries (22). cargo clippy --tests is clean on drive-abci and dash-platform-queries.
  • cargo check passes for wasm-sdk (wasm32), wasm-drive-verify, rs-sdk-ffi, dash-sdk, drive-abci and dash-platform-queries; cargo clippy is clean on dash-platform-queries (with tests) and wasm-sdk (wasm32).

In-place changes to shipped generations

Each of these is reached at protocol versions 13 and earlier, and none changes what those versions do:

  • where_operator_from_proto maps CONTAINS and CONTAINS_ANY at every protocol version. Before PV14 both are refused, where the conversion refused them before: by grouping v0 for documents, and for aggregates by routing v0 (a containsAny is no in there) or Drive's keying step (no type has a typed array). Queries only; no state. should_refuse_element_reads_at_protocol_version_13 pins it.

Breaking Changes

  • The GetDocumentsRequest.WhereOperator wire enum gains two values. A node before this change refuses them as an unknown discriminant, and the SDK doesn't send them below protocol version 14.
  • Rust callers matching exhaustively on the proto WhereOperator must cover them.
  • dash-platform-queries: key_element_clauses takes grouped_by_in (whether the request is grouped by its In field) instead of several_elements_allowed, and aggregate_answers_per_element is replaced by grouped_by_in_field. None of this changes consensus, so the title carries no !.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 5327f7b

  • Bots — coderabbitai not yet · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed
  • Reviewer requests paused — your 5 review slots are occupied; this PR is excluded from reviewers' queues. Required approvals still count without a slot.
  • Build running
  • Approvals
    • files with no dedicated owner — you own it
    • js-wasm-sdk (packages/js-evo-sdk/README.md, packages/wasm-sdk/src/queries/document.rs, packages/wasm-sdk/src/queries/document_ranked.rs) — shumkov
    • rs-drive-abci — you own it
    • rust-sdk-ffi (packages/rs-sdk-ffi/src/document/queries/count.rs, packages/rs-sdk-ffi/src/document/queries/search.rs) — lklimek or shumkov

When every merge requirement is met, the PR Hygiene check passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.

QuantumExplorer and others added 4 commits October 11, 2026 16:12
An index may name a top-level typed array, at any position and one per
index: the document is entered once under each distinct element, keyed as a
property of the element's type is. An empty or absent list is entered
nowhere (the array is always in the index's skip set). A replace diffs the
old and new entries of an index naming an array or bucketing its first
property, inserting first and deleting last; delete removes every element's
entry. Queries read the elements with `contains` (one element, cursors
allowed) and `containsAny` (several, repeats dropped on the server and in
verification, no cursor); counts, sums and averages read several elements
only grouped per element, and a ranking of the array ranks elements by the
documents holding them.

Limits (SYSTEM_LIMITS_V4): an indexed array holds at most 32 elements, or 8
under a timeRange or integerRange window, and a document of such a type
writes at most 256 index entries. Unique, contested, indexOnly,
nullSearchable false and rankings above the array are refused.

Refs #3696

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es are

A typed array's `contains` and `containsAny` values were checked against
the element type's in-memory value kinds, which refused an identifier
element given as base58 text (or a byte array as base64), the way clients
send them and scalar index values are accepted. A value is now judged by
whether it serializes as an element key, through one check shared by
documents queries and the aggregate keying
(`WhereClause::validate_array_element_clause`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 41 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: dashpay/platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: eede98e0-011d-4c69-b023-039ecdb41b8c

📥 Commits

Reviewing files that changed from the base of the PR and between 8834108 and 5327f7b.


📒 Files selected for processing (34)
  • packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m
  • packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.js
  • packages/dapi-grpc/protos/platform/v0/platform.proto
  • packages/dash-platform-queries/src/documents/array_element_clauses.rs
  • packages/dash-platform-queries/src/documents/average_proof_helpers.rs
  • packages/dash-platform-queries/src/documents/count_proof_helpers.rs
  • packages/dash-platform-queries/src/documents/document_average.rs
  • packages/dash-platform-queries/src/documents/document_count.rs
  • packages/dash-platform-queries/src/documents/document_query.rs
  • packages/dash-platform-queries/src/documents/document_split_averages.rs
  • packages/dash-platform-queries/src/documents/document_split_counts.rs
  • packages/dash-platform-queries/src/documents/document_split_sums.rs
  • packages/dash-platform-queries/src/documents/document_sum.rs
  • packages/dash-platform-queries/src/documents/having_proof_helpers.rs
  • packages/dash-platform-queries/src/documents/mod.rs
  • packages/dash-platform-queries/src/documents/ranked_proof_helpers.rs
  • packages/dash-platform-queries/src/documents/sum_proof_helpers.rs
  • packages/js-evo-sdk/README.md
  • packages/rs-drive-abci/src/query/document_query/v1/compute_aggregate_mode_and_check_limit/v2/mod.rs
  • packages/rs-drive-abci/src/query/document_query/v1/conversions.rs
  • packages/rs-drive-abci/src/query/document_query/v1/tests.rs
  • packages/rs-sdk-ffi/src/document/queries/count.rs
  • packages/rs-sdk-ffi/src/document/queries/search.rs
  • packages/wasm-drive-verify/src/document/verify_proof.rs
  • packages/wasm-drive-verify/src/document/verify_proof_keep_serialized.rs
  • packages/wasm-drive-verify/src/document/verify_start_at_document_in_proof.rs
  • packages/wasm-sdk/src/queries/document.rs
  • packages/wasm-sdk/src/queries/document_ranked.rs

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…inct

A count, sum or average keys `contains ""` as an equality, whose skip
binding counts an empty string as possibly missing, so the array's index
was refused. An array is now bound by any constraint on it
(`SkipIfAbsentBinding::constrains`); an ordering alone still binds nothing.

The worst-case document stamped string elements from 26 letters, so a
one-character element repeated past the 26th and the estimate priced fewer
entries than a document of 32 distinct elements writes. It now uses the 94
printable ASCII characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer force-pushed the claude/typed-array-element-indexes-sdk branch from 3b93235 to 2050c6d Compare October 11, 2026 14:07
@thepastaclaw

thepastaclaw commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Review not started yet because the new head is waiting for the 30-minute push debounce.

  • Request normal review — click when the PR is ready for review.
  • Request priority review — click to move this review to the front of the queue.

Commit 5327f7b. Normal review starts when eligible; priority review starts as soon as a slot is available.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

The aggregate proof helpers have a blocking verification flaw: a valid per-element proof can be accepted as an unsupported, duplicate-counted total. Four additional suggestions address binary binding inputs, nested version gating, historical-generation documentation, and SDK proof round-trip coverage. Verification was static against the exact head; no builds or tests were run, and the supplied CI snapshot reports successful completed checks.

🔴 1 blocking | 🟡 4 suggestion(s)

1 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Preserve binary operands in element-query bindings
packages/wasm-sdk/src/queries/document.rs:533-545

The newly documented identifier query does not work when identityId is a Uint8Array. DocumentsFacade.query forwards it unchanged, but DocumentsQueryInput.where_clauses deserializes into serde_json::Value. deserialize_required_query uses wasm_dpp2::from_object, which preserves the typed array as platform bytes; the platform-value deserializer then calls visit_bytes, which serde_json's value visitor does not support. Both a binary contains operand and containsAny candidates supplied as typed arrays fail before this parser runs.

Using plain numeric arrays instead does not preserve binary semantics either: this conversion produces Value::Array containing numeric values, and a containsAny candidate list becomes nested arrays that value_to_proto_at_depth rejects. The JSON-backed FFI search/count parsers have that same nested-list problem. These numeric arrays are not already valid byte operands—the converters produce I64 values, while Drive's array-to-bytes conversion expects U8.

Preserve platform byte values through the WASM input boundary and normalize supported JSON binary representations using the element schema before wire encoding. Keep the wire's flat-list restriction and add binding coverage for binary contains and containsAny operands.

source: gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This cross-cutting change modifies peer-facing wire deserialization in rs-drive-abci's where_operator_from_proto and proof-query reconstruction in dash-platform-queries' aggregate proof helpers, combining substantial integration complexity with critical network and proof-verification surfaces.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/dash-platform-queries/src/documents/count_proof_helpers.rs`:
- [BLOCKING] packages/dash-platform-queries/src/documents/count_proof_helpers.rs:181-189: Enforce per-element aggregate restrictions before verifying proofs
  Passing `true` unconditionally bypasses the restriction that Drive derives from the original grouping and range shape. Drive permits `containsAny` aggregates only for `GroupByIn` without a range, but this helper also lowers an ungrouped request to ordinary `In`. Both ungrouped `Aggregate` and grouped `GroupByIn` then select `PointLookupProof`, and `point_lookup_count_path_query` does not bind the grouping mode.

  A malicious responder can therefore return a genuine proof for `COUNT(*) WHERE hashtags containsAny ['dash', 'news'] GROUP BY hashtags` to an otherwise identical ungrouped request. The reconstructed lookup query and signed state root match, so verification succeeds. `DocumentCount` sums the authenticated branch counters and returns 2 for one document holding both tags, although Drive deliberately refuses that total. The signature authenticates the counters, not that their groups are disjoint. The same unconditional allowance in the sum and average helpers permits duplicate-counted sums and multiplicity-weighted averages.

  Derive the allowance from the original request shape before lowering erases the element operator, matching Drive's grouping/range restriction in all three helpers. Add a regression that supplies a valid grouped proof to an ungrouped request and requires rejection.

In `packages/wasm-sdk/src/queries/document.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document.rs:533-545: Preserve binary operands in element-query bindings
  The newly documented identifier query does not work when `identityId` is a `Uint8Array`. `DocumentsFacade.query` forwards it unchanged, but `DocumentsQueryInput.where_clauses` deserializes into `serde_json::Value`. `deserialize_required_query` uses `wasm_dpp2::from_object`, which preserves the typed array as platform bytes; the platform-value deserializer then calls `visit_bytes`, which serde_json's value visitor does not support. Both a binary `contains` operand and `containsAny` candidates supplied as typed arrays fail before this parser runs.

  Using plain numeric arrays instead does not preserve binary semantics either: this conversion produces `Value::Array` containing numeric values, and a `containsAny` candidate list becomes nested arrays that `value_to_proto_at_depth` rejects. The JSON-backed FFI search/count parsers have that same nested-list problem. These numeric arrays are not already valid byte operands—the converters produce `I64` values, while Drive's array-to-bytes conversion expects `U8`.

  Preserve platform byte values through the WASM input boundary and normalize supported JSON binary representations using the element schema before wire encoding. Keep the wire's flat-list restriction and add binding coverage for binary `contains` and `containsAny` operands.

In `packages/dash-platform-queries/src/documents/document_query.rs`:
- [SUGGESTION] packages/dash-platform-queries/src/documents/document_query.rs:819-821: Apply the element-operator version gate to composite sub-queries
  The activation check scans only the page's `where_clauses`. `check_composite_shape` does not reject element operators in sub-queries, and `sub_queries_to_proto` subsequently encodes their clauses through the mapper that this PR changes to accept `Contains` and `ContainsAny`. An ordinary page with either operator only in a sub-query therefore emits discriminant 13 or 14 for PV12/PV13, which already use the V1 wire, instead of returning the SDK's PV14 configuration error.

  Older servers remain fail-closed, so this is not a consensus or proof-soundness blocker. Extend the existing grammar-generation check to every composite sub-query's clauses and add pre-PV14 encoding tests for both nested operators.

In `packages/rs-drive-abci/src/query/document_query/v1/compute_aggregate_mode_and_check_limit/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/query/document_query/v1/compute_aggregate_mode_and_check_limit/v0/mod.rs:53-59: Record the historical safety argument for the in-place routing edit
  Historical query tables still select this v0 implementation, while PV14 reaches it through v2 → v1 delegation. The edit does not establish a historical consensus break: pre-PV14 contract tables set `parse_typed_array` to `None`, so the typed-array state needed for a successful element query cannot exist, and element validation rejects these operators on other property types.

  The repository's coding conventions explicitly allow such consensus-inert in-place edits, but require the safety argument at the edited lines and an 'In-place changes to shipped generations' section in the PR description. The current comment explains only the new semantics, and the supplied description lacks that section. Document the historical parser/validation invariant and the versions selecting this generation; a regression for historical rejection would strengthen the justification. A new generation is not required solely because this file is named v0.

In `packages/rs-drive-abci/src/query/document_query/v1/tests.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/query/document_query/v1/tests.rs:7455-7457: Round-trip element-query proofs through the SDK adapters
  The new proof assertion checks only that the server produced nonempty bytes. The `ContainsAny` document and grouped-count cases use `prove: false`, so these tests do not exercise the SDK normalization calls added by this PR. They would pass even if an aggregate proof helper reconstructed a different query from the prover.

  Add offline signed-response round trips through the relevant `FromProof<DocumentQuery>` entry points: overlapping `ContainsAny` document results and grouped counts, plus representative SUM/AVG and pinned `Contains` ranked/having cases. The neighboring `time_range_proof_verification` module already provides the prover/verifier setup. Keep the grouped-proof substitution rejection regression requested above as the negative counterpart.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Replace existing WASM cursor-length panics with returned errors — Out of scope: the fallible cursor conversions followed by unwrap are present unchanged in the base revision. This PR adds operator mappings without changing cursor handling or introducing a new cursor precondition, so the adjacent error-handling cleanup is not carried into this review.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

Review fixes for typed array element indexes (PV14).

- Grouping v1 keeps `contains`/`containsAny` on `$id` grouped, so the
  element-clause check refuses it. Before, the primary key slots took
  only `Equal`/`In` and the clause was dropped: the query ran unfiltered
  and its proof verified.
- A composite sub-query refuses `containsAny`: its documents are read
  out of the merged proof without the repeats dropped. A count sub-query
  keys its element clauses as the count surface does.
- `answers_per_element` is the one rule for when a count, sum or average
  reads several elements; the dispatchers use it and the SDK verifiers
  can too. The refusal names both conditions (grouped by the array alone,
  no range clause).
- `skipIfAbsent` whose only skip property is the indexed array is
  refused with a reason naming the array, not as a keyword on required
  properties.
- The repeat drop borrows the stored bytes instead of copying them, and
  reads `ItemWithSumItem` documents as items.
- `typed_array_property` and `Index::typed_arrays` replace seven copies
  of the typed-array lookup. `validate_in_clause_shape` is renamed
  `validate_where_clause_shape`, the preflight it has become.
- Tests: `$id` element clauses refused, `containsAny` in a sub-query,
  count sub-query keying, the reworded refusals, and the estimated
  (`apply: false`) create, replace and delete of an array-indexed post.
- Stale "a typed array cannot be an index property" docs corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer force-pushed the claude/typed-array-element-indexes-sdk branch from 2050c6d to 7d19cae Compare October 11, 2026 16:25
Review fixes for typed array element indexes (PV14), round two.

- `Index::indexed_array` names the typed array an index keys by (stamped
  by the parser on every parse). The skip gate binds that member of the
  skip set by any element constraint, as it does without a declared
  `skipIfAbsent`, while the declared members keep the missing-value rule.
  Before, on an index such as `[hashtags, language]` with
  `skipIfAbsent: ["language"]`, a count or sum with `contains ""` was
  refused: keyed as `== ""`, the element read as a possibly missing value.
- `execute_document_count_and_sum_request` is crate-internal: it takes the
  average dispatcher's already keyed clauses and cannot tell a keyed
  `contains` from a raw equality on the array.
- The e2e storage helpers treat only a missing path or key as absent; any
  other read error fails the test.
- Tests: per-element sums and averages, proved and unproved, through an
  amount-only replace, an element change and a delete, with a total over
  several elements refused; the empty-string element beside a declared
  skip property; the key codec at protocol version 13 and latest (scalar
  keys frozen and alike, generation 0 refusing an array key, element keys
  only from 14); the parser's `indexed_array` stamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer force-pushed the claude/typed-array-element-indexes-sdk branch from 7d19cae to f133921 Compare October 11, 2026 17:09

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The exact-head diff addresses four prior findings, including the aggregate proof-substitution blocker, and preserves existing wire discriminants and dependency boundaries. Three non-blocking binding and validation issues remain: advertised raw-byte operands, ranked/having TypeScript pins, and historical element-operator acceptance in standalone WASM verifiers. Verification was static only; the supplied head-specific CI snapshot still has build/test-related checks queued or pending.

🟡 3 suggestion(s)

2 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Preserve binary operands in element-query bindings
packages/wasm-sdk/src/queries/document.rs:544-545

The new operator mappings do not make the README's advertised byte-valued identity operand usable. DocumentsFacade.query forwards the input unchanged, and deserialize_required_query uses from_object: a Uint8Array becomes platform Value::Bytes, but DocumentsQueryInput.where_clauses is Option<Vec<serde_json::Value>>. The platform-value deserializer invokes visit_bytes, which serde_json's value visitor does not implement, so the query fails before this operand conversion.

Replacing Uint8Array with a plain numeric array does not solve it: json_to_platform_value creates Array, while to_identifier_bytes and to_binary_bytes accept array members only as U8. A containsAny list of numeric byte arrays also hits the wire encoder's nested-list rejection. The FFI JSON converters share that numeric-array limitation; encoded identifier and binary strings remain usable.

The underlying marshalling limitation predates this PR, but the newly added 'base58 or bytes' example exposes it as a supported element-query API. Either preserve binary platform values through the WASM operand path and cover contains/containsAny, or narrow the new README example to base58 strings. A broader FFI/parser redesign is not required merely to correct the newly advertised contract.

source: muse-spark-1.3-contributor (phase1-reviewer: general); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)

🟡 Suggestion: Expose element pins in the ranked/having TypeScript contract
packages/wasm-sdk/src/queries/document_ranked.rs:105-107

The new ranked and having pre-checks correctly normalize a typed-array contains pin through Drive's shared helper, but DocumentsIndexPin still permits only equality and IN. Both DocumentsRankedQuery.where and DocumentsHavingQuery.where use this type, and the evo-sdk facade exposes those interfaces directly. TypeScript callers therefore cannot express the newly supported pinned-element query without a cast; substituting == is not equivalent because Drive rejects equality on a typed array.

Add contains/Contains to the single-value pin alternative and update the associated pin documentation. Keep containsAny excluded, since these surfaces deliberately permit only one pinned array element, and add a TypeScript API fixture for the newly supported spelling.

export type DocumentsIndexPin =
  | [string, '==' | '=' | 'contains' | 'Contains', unknown]
  | [string, 'in' | 'In', unknown[]];

source: gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The cross-cutting change is intricate and modifies cryptographic proof acceptance in array_element_clauses.rs and the count, sum, and average proof helpers, where incorrect multi-element verification could accept double-counted aggregate totals.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/queries/document.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document.rs:544-545: Preserve binary operands in element-query bindings
  The new operator mappings do not make the README's advertised byte-valued identity operand usable. DocumentsFacade.query forwards the input unchanged, and deserialize_required_query uses from_object: a Uint8Array becomes platform Value::Bytes, but DocumentsQueryInput.where_clauses is Option<Vec<serde_json::Value>>. The platform-value deserializer invokes visit_bytes, which serde_json's value visitor does not implement, so the query fails before this operand conversion.

  Replacing Uint8Array with a plain numeric array does not solve it: json_to_platform_value creates Array<I64>, while to_identifier_bytes and to_binary_bytes accept array members only as U8. A containsAny list of numeric byte arrays also hits the wire encoder's nested-list rejection. The FFI JSON converters share that numeric-array limitation; encoded identifier and binary strings remain usable.

  The underlying marshalling limitation predates this PR, but the newly added 'base58 or bytes' example exposes it as a supported element-query API. Either preserve binary platform values through the WASM operand path and cover contains/containsAny, or narrow the new README example to base58 strings. A broader FFI/parser redesign is not required merely to correct the newly advertised contract.

In `packages/wasm-sdk/src/queries/document_ranked.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document_ranked.rs:105-107: Expose element pins in the ranked/having TypeScript contract
  The new ranked and having pre-checks correctly normalize a typed-array contains pin through Drive's shared helper, but DocumentsIndexPin still permits only equality and IN. Both DocumentsRankedQuery.where and DocumentsHavingQuery.where use this type, and the evo-sdk facade exposes those interfaces directly. TypeScript callers therefore cannot express the newly supported pinned-element query without a cast; substituting == is not equivalent because Drive rejects equality on a typed array.

  Add contains/Contains to the single-value pin alternative and update the associated pin documentation. Keep containsAny excluded, since these surfaces deliberately permit only one pinned array element, and add a TypeScript API fixture for the newly supported spelling.

In `packages/wasm-drive-verify/src/document/verify_proof.rs`:
- [SUGGESTION] packages/wasm-drive-verify/src/document/verify_proof.rs:280-281: Gate standalone WASM element operators on PV14
  These new parser arms accept element operators for historical platform versions, but this wrapper builds InternalClauses directly instead of calling its versioned extract_from_clauses path. At PV13, validate_where_clause_shape checks only the number of IN clauses; the element-type validation runs only in the PV14 generation. A Contains clause supplied in equal_clauses on an indexed scalar consequently reaches to_path_query, whose as_key_operator maps it to equality. A genuine scalar-equality proof can therefore verify for an element query that the historical server rejects. The absence of historical typed-array state does not protect this manually constructed path.

  The same parser bypass exists in verify_proof_keep_serialized.rs and verify_start_at_document_in_proof.rs. Pass PlatformVersion into parsing and reject element operators before the PV14 grammar, or reconstruct the clauses through versioned extraction. Add historical rejection coverage and PV14 standalone-wrapper proof round trips without changing shipped Drive generations.

  This is a standalone client query-validation defect, not a consensus change or a bypass of Merkle authentication, so it is retained as a suggestion rather than the proposed blocker.

Comment on lines +280 to +281
"Contains" => WhereOperator::Contains,
"ContainsAny" => WhereOperator::ContainsAny,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Gate standalone WASM element operators on PV14

These new parser arms accept element operators for historical platform versions, but this wrapper builds InternalClauses directly instead of calling its versioned extract_from_clauses path. At PV13, validate_where_clause_shape checks only the number of IN clauses; the element-type validation runs only in the PV14 generation. A Contains clause supplied in equal_clauses on an indexed scalar consequently reaches to_path_query, whose as_key_operator maps it to equality. A genuine scalar-equality proof can therefore verify for an element query that the historical server rejects. The absence of historical typed-array state does not protect this manually constructed path.

The same parser bypass exists in verify_proof_keep_serialized.rs and verify_start_at_document_in_proof.rs. Pass PlatformVersion into parsing and reject element operators before the PV14 grammar, or reconstruct the clauses through versioned extraction. Add historical rejection coverage and PV14 standalone-wrapper proof round trips without changing shipped Drive generations.

This is a standalone client query-validation defect, not a consensus change or a bypass of Merkle authentication, so it is retained as a suggestion rather than the proposed blocker.

source: gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

QuantumExplorer and others added 4 commits October 12, 2026 07:35
…a window

An indexed typed array's `maxItems` is held to 16 (was 32), or 4 when the
index also has a `timeRange` or `integerRange` window (was 8), for the
first release: `SYSTEM_LIMITS_V4.max_indexed_array_items` and
`max_windowed_indexed_array_items`, edited in place since protocol
version 14 is unreleased. A later limits table can raise them without
stranding a contract; lowering them after release could.

The 256-entry budget per document is unchanged. With 16 elements and 10
indexes to a type, plain array indexes stay under it (160 entries); only
windows reach it, so its test now spends it on two windowed indexes of a
4-element array (96 entries each) and plain ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`getDocuments` v1 gains `CONTAINS = 13` and `CONTAINS_ANY = 14`; the
regenerated clients carry them. drive-abci decodes them into Drive's element
operators and routes `containsAny` grouped by the array as a per-element
count. dash-platform-queries encodes them (refused below protocol version
14) and keys element clauses before verifying a count, sum, average,
ranked or having proof, as the server does. wasm-sdk, the FFI search and
count parsers and wasm-drive-verify accept `contains` and `containsAny`,
and the evo-sdk README documents them.

Refs #3696

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes for the typed array element wire and SDKs (PV14).

- The count, sum and average proof verifiers keyed element clauses with
  several elements always allowed, trusting the server to have refused a
  total over several. A dishonest node could prove the per-element counts
  of such a total and the SDK would sum them, counting a post holding two
  of the elements twice. They now pass the server's own rule
  (`aggregate_answers_per_element`, over drive's `answers_per_element`),
  so they refuse what the server refuses.
- One helper, `documents::array_element_clauses::key_element_clauses`,
  keys element clauses for the five verifiers and the wasm-sdk ranked and
  having pre-checks instead of seven copies.
- Test names start with "should", and the abci wire tests import their
  types instead of spelling `crate::` paths inline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes for the typed array element wire and SDKs (PV14), round two.

- The encoder's protocol version 14 gate for `contains`/`containsAny`
  also reads composite sub-queries, which ride the same wire mapping; an
  element operator only in a sub-query was encoded for protocol version
  13 instead of refused with the version floor.
- The in-place edit of the protocol version 13 routing table states its
  safety argument where it is made, and a test pins it: at protocol
  version 13 a documents read and a grouped count with an element
  operator are both refused.
- Proofs from the v1 handler verified through the SDK's
  `FromProof<DocumentQuery>` entry points: documents under several
  elements (each post once), per-element counts and sums, a pinned
  element's average, ranking and having bound within one element, and a
  grouped proof offered for an ungrouped total, which the SDK refuses
  (with the earlier blanket allowance it verified a count of 5 for 4
  posts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer force-pushed the claude/typed-array-element-indexes-sdk branch from f133921 to 307c822 Compare October 12, 2026 04:38
Review fixes for typed array element indexes (PV14), round three.

- A document of a type with a `ttl` prepays its cleanup by the index
  entries its indexed arrays' `maxItems` allow, and a replace pays only
  for its body's growth (one long element can make way for many short
  ones of the same size). So an update may no longer raise an indexed
  array's `maxItems` on such a type (`DocumentTypeUpdateError`, 40212,
  in update validation v1, the protocol version 14 generation).
- The cost model sizes a string element at least one character when its
  type allows one: a `maxLength: 1` element with no positive `minLength`
  had a middle length of 0, so every sample element was empty and the
  quote priced one entry instead of `maxItems`.
- The nested insert and delete walkers move the path into the last value
  instead of cloning it for every value.
- Tests: elements across overlapping windows (every window and element
  entered, an overlapping update, a delete pruning per window while
  another post keeps a shared element); sustained writes draining
  expired windows of an indexed array with a property below it, and a
  replace and a delete over drained windows (without the drain budget's
  `maxItems` term it falls 19 to 46 seconds behind); the time-to-live
  bound rule; the short-string sizing (fails without the fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The aggregate-proof restriction, composite version gate, historical-safety documentation, and signed SDK round-trip coverage are present and address four prior findings. Three non-blocking binding/API issues remain. Validation was static only: the supplied exact-head CI snapshot reports successful NPM release validation and Kotlin build/tests, but contains no dedicated Rust-suite result.

🟡 3 suggestion(s)

2 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Expose element pins in the ranked/having TypeScript contract
packages/wasm-sdk/src/queries/document_ranked.rs:105-107

The parser and ranked/having prechecks now accept a typed-array prefix pinned with contains, but the exported DocumentsIndexPin union still permits only equality and IN. Both DocumentsRankedQuery.where and DocumentsHavingQuery.where use this union, and evo-sdk exposes those types directly. A valid pin such as where: [['hashtags', 'contains', 'dash']] therefore fails TypeScript checking unless the caller casts away the public contract. Equality is not a workaround because Drive rejects ordinary equality on a typed array. Add the accepted contains/Contains spellings to the scalar-pin alternative and document their element semantics. Keep containsAny excluded, matching the runtime's deliberate single-element restriction.

export type DocumentsIndexPin =
  | [string, '==' | '=' | 'contains' | 'Contains', unknown]
  | [string, 'in' | 'In', unknown[]];

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)

🟡 Suggestion: Preserve binary operands in element-query bindings
packages/wasm-sdk/src/queries/document.rs:544-545

The new operators still pass through JSON-only where-clause inputs. DocumentsQueryInput.where_clauses and the ranked/having equivalents store serde_json::Value, while deserialize_required_query uses from_object, which preserves a JavaScript Uint8Array as platform Value::Bytes. Deserializing that value into JSON invokes visit_bytes, which JSON's value visitor does not support, so the README's mentions contains identityBytes form fails before this parser runs. Nested Uint8Array candidates in containsAny fail the same way. Plain number arrays are not a substitute: this converter produces I64 elements, whereas the identifier/binary encoders accept arrays of U8. The FFI search/count JSON converters have the same numeric-array mismatch. Base58 identifiers and base64 binary text remain usable; the defect is the byte-input spelling. Preserve platform values through the WASM input/parser boundary and normalize JSON byte operands according to the declared element type where supported. Keep the outer containsAny candidate list distinct from each binary element, and add binding-level binary coverage.

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The intricate cross-layer change modifies proof verification in dash-platform-queries/src/documents/array_element_clauses.rs and the aggregate proof helpers, where element keying and multi-element eligibility determine whether potentially double-counted aggregate proofs are accepted.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/queries/document_ranked.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document_ranked.rs:105-107: Expose element pins in the ranked/having TypeScript contract
  The parser and ranked/having prechecks now accept a typed-array prefix pinned with `contains`, but the exported `DocumentsIndexPin` union still permits only equality and IN. Both `DocumentsRankedQuery.where` and `DocumentsHavingQuery.where` use this union, and evo-sdk exposes those types directly. A valid pin such as `where: [['hashtags', 'contains', 'dash']]` therefore fails TypeScript checking unless the caller casts away the public contract. Equality is not a workaround because Drive rejects ordinary equality on a typed array. Add the accepted `contains`/`Contains` spellings to the scalar-pin alternative and document their element semantics. Keep `containsAny` excluded, matching the runtime's deliberate single-element restriction.

In `packages/wasm-sdk/src/queries/document.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document.rs:544-545: Preserve binary operands in element-query bindings
  The new operators still pass through JSON-only where-clause inputs. `DocumentsQueryInput.where_clauses` and the ranked/having equivalents store `serde_json::Value`, while `deserialize_required_query` uses `from_object`, which preserves a JavaScript `Uint8Array` as platform `Value::Bytes`. Deserializing that value into JSON invokes `visit_bytes`, which JSON's value visitor does not support, so the README's `mentions contains identityBytes` form fails before this parser runs. Nested `Uint8Array` candidates in `containsAny` fail the same way. Plain number arrays are not a substitute: this converter produces I64 elements, whereas the identifier/binary encoders accept arrays of U8. The FFI search/count JSON converters have the same numeric-array mismatch. Base58 identifiers and base64 binary text remain usable; the defect is the byte-input spelling. Preserve platform values through the WASM input/parser boundary and normalize JSON byte operands according to the declared element type where supported. Keep the outer `containsAny` candidate list distinct from each binary element, and add binding-level binary coverage.

In `packages/wasm-drive-verify/src/document/verify_proof.rs`:
- [SUGGESTION] packages/wasm-drive-verify/src/document/verify_proof.rs:280-281: Gate standalone WASM element operators on PV14
  (existing thread: https://github.com/dashpay/platform/pull/5372#discussion_r4242600610)
  All three standalone document verifiers accept the new operators without checking the supplied platform version, then construct `InternalClauses` directly instead of using the versioned where-clause grouper. At PV13, `validate_where_clause_shape` checks only the number of IN clauses; it does not validate element operators. A `Contains` clause supplied in `equal_clauses` on an ordinary indexed string field therefore reaches the historical secondary-index lowering, where `to_path_query` maps it to equality. An ordinary equality proof can consequently verify under that inactive element request, although the regular PV13 query path rejects it. This is an activation/input-validation mismatch, not a cryptographic bypass. Apply the activation check before verification in these exports, or enforce it through shared Drive preflight/grouping that also covers directly constructed clauses. Add a regression offering a valid historical equality proof under a `Contains` request.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Return an input error for malformed standalone-verifier cursors — Duplicate of the pre-existing malformed-cursor observation. The unchanged cursor handling is outside this PR's scope.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

QuantumExplorer and others added 8 commits October 12, 2026 11:09
…s queries do

A filter validated an element clause by the scalar rules of the element
type, so a boolean array's containsAny was refused, and its containsAny
matched only a list, not the bytes a u8 element list may be given as.
Both validators now share one element-value rule, and containsAny
matches each element as an in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s out

The cleanup weight multiplied every level of an index naming an array
by maxItems, though a delete walks the levels above the array once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves serialize_value_for_key generation 1 between #5350 (unsigned
integers keyed in value order) and this branch (a typed array keyed by its
elements): one generation 1 resolves a typed array to its element type
first, so an unsigned element takes the value-order key, and every other
element keeps the element type's encoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e array from the stamp

- A ranked element key over the limit is reported as `<array>.items.maxLength`
  (or `.maxItems`), as the unranked check reports it, not under the array.
- The skip set, level stamps, entry cap, skip rules and the ttl update rule read
  `Index::indexed_array` (`indexed_array_fan_out`, `windows_per_document`)
  instead of re-deriving it; the ranking-above rule reads `ranked_at_levels`.
- Tests for the nested-array and contested refusals and for an unsigned element
  keyed in value order; stale comments corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QuantumExplorer and others added 8 commits October 12, 2026 13:34
… an empty byte list

- Update v1's unique-index sibling scan read an indexed array's level as one
  value and failed every replace of such a document; an element level carries
  no missing value and is passed by.
- An empty list counts as absent only for an indexed typed array, so a byte
  array sent as an empty list is carried, as the indexOnly probes and row
  commitments read it.
- The fanned-out update reads each standing tree it shares once.
- The walkers read a level's keys through one `level_keys` rule; estimated
  element keys reuse the scalar estimate and `distinct_estimated_keys`.
- Tests: a replace beside a unique index, an empty-list byte trigger, and the
  drain test checks the drained post's own windows; shared test helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oof repeats by id

- `key_array_element_clauses` takes `grouped_by_in` and applies the
  per-element rule to its own clauses; `answers_per_element` is private.
- `verify_proof_keep_serialized` drops a containsAny page's repeats by the
  proved document ids.
- The per-mode aggregate executors are crate-internal; the index pickers say
  their clauses must be keyed first.
- Grouping v0's element arms note why they are unreachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/rs-platform-version/src/version/mocks/v2_test.rs
#	packages/rs-platform-version/src/version/system_limits/mod.rs
#	packages/rs-platform-version/src/version/system_limits/v1.rs
#	packages/rs-platform-version/src/version/system_limits/v2.rs
#	packages/rs-platform-version/src/version/system_limits/v3.rs
#	packages/rs-platform-version/src/version/system_limits/v4.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent-indexes-sdk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…views

DocumentCount, DocumentSum and DocumentAverage add the verified entries up,
so a grouped containsAny proof would count a document once per element it
holds; they now refuse several elements even grouped, and the split views
keep one entry per element. One helper, aggregate_where_clauses, keys and
canonicalizes for the three verifiers on the type each resolved, and the
keying takes grouped_by_in (grouped_by_in_field) as Drive's now does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… table

Routing v2, which only protocol version 14 selects, routes a containsAny as
the In it keys like, on a copy of the clauses. Routing v0 is back to its
shipped text, so protocol version 13 answers a grouped containsAny as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tals and pages

- At protocol version 13 a grouped containsAny gets routing v0's refusal,
  with a limit and without.
- The single-total views refuse the grouped per-element proofs.
- The documents round trip checks Drive's verified page, which the SDK's
  map would hide a repeat in.
- The wire request reuses empty_v1_request, and the SDK round trips share
  one prove-and-verify helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer changed the title feat(sdk)!: query the elements of typed arrays over the wire (PV14) feat(sdk): query the elements of typed arrays over the wire (PV14) Oct 12, 2026
QuantumExplorer and others added 3 commits October 12, 2026 14:21
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/rs-drive/src/drive/document/insert/add_document_for_contract/mod.rs
#	packages/rs-platform-version/src/version/v14.rs
…ent-indexes-sdk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two lines of the note began with `1)`, which rustdoc reads as list items,
so clippy's doc_lazy_continuation failed the platform-version build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The complete diff preserves existing wire discriminants and historical routing, and the aggregate-proof restrictions and signed round-trip coverage address the earlier blocker. Three binding/API issues and one breaking-change metadata issue remain as non-blocking suggestions. This was static verification only: the supplied exact-head CI snapshot reports successful checks, but does not independently establish execution of the targeted Rust proof regressions.

🟡 4 suggestion(s)

2 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Preserve binary operands in element-query bindings
packages/wasm-sdk/src/queries/document.rs:544-545

The newly supported operators still receive operands through DocumentsQueryInput.where_clauses: Option<Vec<JsonValue>>. deserialize_required_query calls from_object, which preserves a JavaScript Uint8Array as a platform byte value and then deserializes it into the JSON-only input model. Platform-value's non-human-readable deserializer invokes visit_bytes, which serde_json::Value does not support, so the README's documented byte-form identifier operand fails before this parser or the wire encoder runs. Binary candidates nested inside containsAny fail the same way. Plain numeric byte arrays are not a workaround: this converter produces arrays of I64, while identifier and binary key conversion require U8 entries. Base58 identifier strings and base64 binary strings remain usable; the problem is the advertised binary representation, not the absence of any usable spelling. Preserve platform-value operands through input deserialization, or parse them directly from JavaScript, and add boundary coverage for both operators with binary elements. Any normalization of plain arrays must use the element schema rather than converting ordinary numeric lists indiscriminately.

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)

🟡 Suggestion: Expose element pins in the ranked/having TypeScript contract
packages/wasm-sdk/src/queries/document_ranked.rs:105-107

The ranked and having runtime pre-checks now key a valid typed-array contains prefix pin as equality, but their shared exported DocumentsIndexPin type still permits only equality and in. Both query interfaces use this union, and the evo-sdk facades accept those interfaces directly, so TypeScript callers cannot express the newly supported pin without a cast. Substituting == does not work because Drive rejects ordinary equality on a typed array. Add the accepted contains and Contains spellings to the single-element alternative and update the accompanying pin documentation. Keep containsAny excluded because these surfaces deliberately reject it.

export type DocumentsIndexPin =
  | [string, '==' | '=' | 'contains' | 'Contains', unknown]
  | [string, 'in' | 'In', unknown[]];

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The intricate cross-layer change directly alters cryptographic proof acceptance and reconstructed query semantics in packages/dash-platform-queries/src/documents/array_element_clauses.rs and count_proof_helpers.rs, including safeguards against accepting incorrect multi-element totals.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/queries/document.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document.rs:544-545: Preserve binary operands in element-query bindings
  The newly supported operators still receive operands through `DocumentsQueryInput.where_clauses: Option<Vec<JsonValue>>`. `deserialize_required_query` calls `from_object`, which preserves a JavaScript `Uint8Array` as a platform byte value and then deserializes it into the JSON-only input model. Platform-value's non-human-readable deserializer invokes `visit_bytes`, which `serde_json::Value` does not support, so the README's documented byte-form identifier operand fails before this parser or the wire encoder runs. Binary candidates nested inside `containsAny` fail the same way. Plain numeric byte arrays are not a workaround: this converter produces arrays of `I64`, while identifier and binary key conversion require `U8` entries. Base58 identifier strings and base64 binary strings remain usable; the problem is the advertised binary representation, not the absence of any usable spelling. Preserve platform-value operands through input deserialization, or parse them directly from JavaScript, and add boundary coverage for both operators with binary elements. Any normalization of plain arrays must use the element schema rather than converting ordinary numeric lists indiscriminately.

In `packages/wasm-sdk/src/queries/document_ranked.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/document_ranked.rs:105-107: Expose element pins in the ranked/having TypeScript contract
  The ranked and having runtime pre-checks now key a valid typed-array `contains` prefix pin as equality, but their shared exported `DocumentsIndexPin` type still permits only equality and `in`. Both query interfaces use this union, and the evo-sdk facades accept those interfaces directly, so TypeScript callers cannot express the newly supported pin without a cast. Substituting `==` does not work because Drive rejects ordinary equality on a typed array. Add the accepted `contains` and `Contains` spellings to the single-element alternative and update the accompanying pin documentation. Keep `containsAny` excluded because these surfaces deliberately reject it.

In `packages/dapi-grpc/protos/platform/v0/platform.proto`:
- [SUGGESTION] packages/dapi-grpc/protos/platform/v0/platform.proto:1286-1294: Mark the public Rust enum extension as source-breaking
  Appending these values preserves protobuf compatibility, but adds variants to the public generated Rust `WhereOperator` enum, which is not marked `#[non_exhaustive]`. Downstream exhaustive matches must therefore change, as the PR's Breaking Changes section already acknowledges. Repository guidance and the PR template require `!` for breaking changes, not only consensus changes, so the explanation that no consensus change occurred does not justify omitting it. Change the title to `feat(sdk)!: ...` and complete the corresponding checklist item. This metadata correction does not require another protocol generation or an independent Cargo version bump.

In `packages/wasm-drive-verify/src/document/verify_proof.rs`:
- [SUGGESTION] packages/wasm-drive-verify/src/document/verify_proof.rs:280-281: Gate standalone WASM element operators on PV14
  (existing thread: https://github.com/dashpay/platform/pull/5372#discussion_r4242600610)
  All three standalone document verifiers now parse these operators without checking the supplied platform version, and they construct `InternalClauses` directly instead of passing through versioned raw-clause grouping. The pre-PV14 rejection in `group_where_clauses` therefore never runs. Historical `validate_where_clause_shape` checks only the number of `in` clauses, and historical index selection uses the caller-supplied clause buckets without validating element/property compatibility. A `Contains` clause on an ordinary scalar indexed string in `equal_clauses` consequently constructs the same path query as `Equal`, because `WhereClause::to_path_query` lowers the operator unconditionally. Thus a genuine scalar-equality proof can be accepted under an element-query label at PV13; historical typed-array state is not required, and deeper type errors do not reliably close this path. Enforce the activation boundary consistently in `verify_proof`, `verify_proof_keep_serialized`, and `verify_start_at_document_in_proof`, or route their inputs through the appropriate shared versioned preflight. Add historical refusal coverage using a valid scalar proof. This is a query-API activation inconsistency, not a forged-proof or chain-consensus blocker.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Return errors instead of trapping on malformed standalone WASM cursors — Outside this PR's scope. The malformed-length start_at conversions in verify_proof.rs and verify_start_at_document_in_proof.rs are unchanged from the exact base, and the new operator mappings neither worsen nor rely on their panic behavior. The equivalent cursor-trap observations from the other lanes are consolidated here rather than retained as an exceptional follow-up.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

Comment on lines +1286 to +1294
CONTAINS = 13;
// An element of a typed array is one of the operands (v1 only;
// protocol version 14): an `IN` over the elements. A document
// holding several of them is under each, and a documents page
// returns it once, where it first appears; the limit counts every
// entry the walk passes. Takes an `ORDER BY` on the array (the
// order the elements are walked in) and no cursor. Counts, sums and
// averages read several elements only grouped by them.
CONTAINS_ANY = 14;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Mark the public Rust enum extension as source-breaking

Appending these values preserves protobuf compatibility, but adds variants to the public generated Rust WhereOperator enum, which is not marked #[non_exhaustive]. Downstream exhaustive matches must therefore change, as the PR's Breaking Changes section already acknowledges. Repository guidance and the PR template require ! for breaking changes, not only consensus changes, so the explanation that no consensus change occurred does not justify omitting it. Change the title to feat(sdk)!: ... and complete the corresponding checklist item. This metadata correction does not require another protocol generation or an independent Cargo version bump.

source: gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

QuantumExplorer and others added 5 commits October 12, 2026 18:06
…a change estimate

A change was estimated as an insert of the new version alone, so a replace
emptying or shrinking an indexed array, which removes an entry per dropped
element in every window, could cost more processing than its estimate: 24
one-day windows of 4 elements emptied cost 15,450,740 against 12,346,320.
For a type with an index naming a typed array, the estimate now also prices
removing the stored version's index entries at their worst case, as a
delete prices it. Other types estimate as before.

The estimate tests shrink to one element and to none, under 24 windows and
on a type with a ttl, and require processing to be covered too; the ranked
proof test checks the live root hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`LevelKeys::nested_keys` yields the elements of an array level or the
single value of any other, without a Vec for a single key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A subscription filter compared elements by value variant, so a u8 element
missed a u64 clause value and an identifier missed its bytes or base58 text,
though validation and indexed queries take them. Element clauses now match
by the element type's key, as indexed queries do; scalar clauses unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/rs-platform-version/src/version/v14.rs
…ent-indexes-sdk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py
Base automatically changed from claude/typed-array-element-indexes to v5.0-dev October 12, 2026 15:15
@github-actions github-actions Bot added this to the v5.0.0 milestone Oct 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 12, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/dash-platform-queries/src/documents/document_query.rs
@github-actions

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants