diff --git a/book/src/drive/document-ranked-trees.md b/book/src/drive/document-ranked-trees.md index 6197c622352..8a0343ceb40 100644 --- a/book/src/drive/document-ranked-trees.md +++ b/book/src/drive/document-ranked-trees.md @@ -91,7 +91,7 @@ One asymmetry is worth knowing when authoring: **the meta-schema demands the lit Two structural rules, both enforced at contract-parse time in rs-dpp: -- **No aggregating index on a compound ranked index's full prefix.** Ranked flags are allowed on compound indexes, with **per-prefix** semantics: a ranked `[identityId, class]` puts the indexed tree at each prefix value's terminal `class` property-name level — one ordered secondary per `identityId`, each ranking only that identity's `class` groups. There is deliberately no global cross-prefix ordering; the query surfaces require every leading property to be pinned by an equality `where` clause. The one shape that stays impossible — and is rejected per document type, where all indexes are visible (`validate_no_ranked_prefix_overlap`) — is a countable/summable index terminating at exactly the compound's leading prefix: its aggregating value trees would demand the `NonCounted` / `NotSummed` shell around the ranked terminal tree, and the storage layer structurally rejects any wrapper around an indexed tree, because the wrapper would neutralise the very aggregates the secondaries order by. (Drive's fail-closed guard behind the parse-time check is `INDEXED_INNER_UNWRAPPABLE`.) Only the exact `n-1` prefix conflicts: an aggregating index at a shorter prefix wraps a plain intermediate tree, and one extending past the ranked terminal lives inside its value trees — both supported. +- **No aggregating index on a compound ranked index's full prefix.** Ranked flags are allowed on compound indexes, with **per-prefix** semantics: a ranked `[identityId, class]` puts the indexed tree at each prefix value's terminal `class` property-name level — one ordered secondary per `identityId`, each ranking only that identity's `class` groups. There is deliberately no global cross-prefix ordering; the query surfaces require every leading property to be pinned by a `where` clause — equalities, plus at most one `IN` that fans out across prefix branches and merges deterministically. The one shape that stays impossible — and is rejected per document type, where all indexes are visible (`validate_no_ranked_prefix_overlap`) — is a countable/summable index terminating at exactly the compound's leading prefix: its aggregating value trees would demand the `NonCounted` / `NotSummed` shell around the ranked terminal tree, and the storage layer structurally rejects any wrapper around an indexed tree, because the wrapper would neutralise the very aggregates the secondaries order by. (Drive's fail-closed guard behind the parse-time check is `INDEXED_INNER_UNWRAPPABLE`.) Only the exact `n-1` prefix conflicts: an aggregating index at a shorter prefix wraps a plain intermediate tree, and one extending past the ranked terminal lives inside its value trees — both supported. - **Non-unique indexes only.** `ranked aggregates are not supported on unique indexes: each group of a unique index contains at most one document, so there is nothing meaningful to rank`. Contested indexes are covered transitively — a contested index is unique by construction, so it hits the same check rather than needing its own. ### Version Gate @@ -231,7 +231,7 @@ Every ranked read — and, on the prove path, every ranked proof — is issued a / // e.g. b"restaurantId" ``` -The children of that tree are the *groups*: one value tree per distinct value of the last index property, keyed by the raw index-key bytes of that value (for a `string` property, its UTF-8 bytes — e.g. `b"alpha"`). The secondary entries a top-k read returns are keyed by those same group keys. A compound index `[a, b]` inserts ` / ` between the doctype and the terminal `` level — the value segment comes from the request's equality `where` pin on `a`, encoded with the same `serialize_value_for_key` the write path used to key that prefix's value tree, so the walk lands on **that prefix's own** indexed tree and secondary. +The children of that tree are the *groups*: one value tree per distinct value of the last index property, keyed by the raw index-key bytes of that value (for a `string` property, its UTF-8 bytes — e.g. `b"alpha"`). The secondary entries a top-k read returns are keyed by those same group keys. A compound index `[a, b]` inserts ` / ` between the doctype and the terminal `` level — the value segment comes from the request's `where` pin on `a` — an equality pin, or one element of the single permitted `IN` (one branch per element) — encoded with the same `serialize_value_for_key` the write path used to key that prefix's value tree, so the walk lands on **that prefix's own** indexed tree and secondary. Prover and verifier build this path through the same function, `DriveDocumentRankedQuery::indexed_property_name_tree_path` (with the pinned prefix values encoded by the shared resolver, `resolve_ranked_query_for_mode`), which is why they agree on the root hash by construction. @@ -341,7 +341,7 @@ Note that the fixture puts each shape on its **own document type**. That's not a | Top / bottom K groups by sum of a property | `rankedSummable: true` on an index with `summable: ""` + `rangeSummable: true` | | Top / bottom K groups by average of a property | `rankedAverageable: true` on an index with `averageable: ""` + `rangeAverageable: true` (or the count+sum longhand) | | Two rankings on one index (e.g. by count *and* by average) | Both keywords. The tree is a PCPSIT carrying both axes in its TLV; you pay one secondary Merk per axis on every write. | -| A ranking filtered by another property (`top 5 restaurants in London`) | A **compound ranked index** with the filter property leading: `[city, restaurantId]` with the ranked flags. Each city gets its own secondary; the query pins the prefix with an equality `where` (`WHERE city == "London" GROUP BY restaurantId ORDER BY DESC LIMIT 5`). Only equality pins — a range or `IN` on the prefix is rejected, and there is no cross-prefix (global) ordering on a compound ranked index. | +| A ranking filtered by another property (`top 5 restaurants in London`) | A **compound ranked index** with the filter property leading: `[city, restaurantId]` with the ranked flags. Each city gets its own secondary; the query pins the prefix with an equality `where` (`WHERE city == "London" GROUP BY restaurantId ORDER BY DESC LIMIT 5`). Equality pins select one prefix; at most one pin may be an `IN` (2..=10 distinct elements; a never-written element — or one whose deeper pinned path was never written — contributes an empty branch), which walks one secondary per element and merges by `(aggregate, encoded prefix, group key)`, proved in one branched `PathQuery` envelope (shared ancestors proved once, per-element authenticated absence) — entries then carry `in_key`. A single-element `IN` normalizes to the equality pin; a `null` pin stays legal on its own but cannot combine with an `IN` (null addresses its prefix through an empty path segment the branched proof cannot express); a non-zero `OFFSET` is rejected together with `IN`; and branched proofs are generated from committed state only. A range operator on the prefix stays rejected, and there is still no global cross-prefix ordering beyond that merge. | | A ranking on a unique or contested index | Not available, and not meaningful: every group holds at most one document. | | Range aggregates without ranking (the 4.0 surface) | Just the `range*` flags. Ranking is strictly additive — adding it never changes what a range query returns. | | Nothing ranking-aware (default) | Don't set any `ranked*` flag. The terminal property-name tree keeps the type its range flags give it. | diff --git a/packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js b/packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js index e4b945e22d7..c20469451a8 100644 --- a/packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js +++ b/packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js @@ -26734,6 +26734,7 @@ $root.org = (function() { * @property {number|Long|null} [count] RankedEntry count * @property {number|Long|null} [sum] RankedEntry sum * @property {number|null} [avg] RankedEntry avg + * @property {Uint8Array|null} [inKey] RankedEntry inKey */ /** @@ -26783,6 +26784,14 @@ $root.org = (function() { */ RankedEntry.prototype.avg = 0; + /** + * RankedEntry inKey. + * @member {Uint8Array} inKey + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry + * @instance + */ + RankedEntry.prototype.inKey = $util.newBuffer([]); + // OneOf field names bound to virtual getters and setters var $oneOfFields; @@ -26829,6 +26838,8 @@ $root.org = (function() { writer.uint32(/* id 3, wireType 0 =*/24).sint64(message.sum); if (message.avg != null && Object.hasOwnProperty.call(message, "avg")) writer.uint32(/* id 4, wireType 1 =*/33).double(message.avg); + if (message.inKey != null && Object.hasOwnProperty.call(message, "inKey")) + writer.uint32(/* id 5, wireType 2 =*/42).bytes(message.inKey); return writer; }; @@ -26875,6 +26886,9 @@ $root.org = (function() { case 4: message.avg = reader.double(); break; + case 5: + message.inKey = reader.bytes(); + break; default: reader.skipType(tag & 7); break; @@ -26933,6 +26947,9 @@ $root.org = (function() { if (typeof message.avg !== "number") return "avg: number expected"; } + if (message.inKey != null && message.hasOwnProperty("inKey")) + if (!(message.inKey && typeof message.inKey.length === "number" || $util.isString(message.inKey))) + return "inKey: buffer expected"; return null; }; @@ -26973,6 +26990,11 @@ $root.org = (function() { message.sum = new $util.LongBits(object.sum.low >>> 0, object.sum.high >>> 0).toNumber(); if (object.avg != null) message.avg = Number(object.avg); + if (object.inKey != null) + if (typeof object.inKey === "string") + $util.base64.decode(object.inKey, message.inKey = $util.newBuffer($util.base64.length(object.inKey)), 0); + else if (object.inKey.length >= 0) + message.inKey = object.inKey; return message; }; @@ -26989,7 +27011,7 @@ $root.org = (function() { if (!options) options = {}; var object = {}; - if (options.defaults) + if (options.defaults) { if (options.bytes === String) object.key = ""; else { @@ -26997,6 +27019,14 @@ $root.org = (function() { if (options.bytes !== Array) object.key = $util.newBuffer(object.key); } + if (options.bytes === String) + object.inKey = ""; + else { + object.inKey = []; + if (options.bytes !== Array) + object.inKey = $util.newBuffer(object.inKey); + } + } if (message.key != null && message.hasOwnProperty("key")) object.key = options.bytes === String ? $util.base64.encode(message.key, 0, message.key.length) : options.bytes === Array ? Array.prototype.slice.call(message.key) : message.key; if (message.count != null && message.hasOwnProperty("count")) { @@ -27020,6 +27050,8 @@ $root.org = (function() { if (options.oneofs) object.value = "avg"; } + if (message.inKey != null && message.hasOwnProperty("inKey")) + object.inKey = options.bytes === String ? $util.base64.encode(message.inKey, 0, message.inKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.inKey) : message.inKey; return object; }; diff --git a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js index f9f35bde110..13eaebf2a58 100644 --- a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js +++ b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js @@ -26226,6 +26226,7 @@ $root.org = (function() { * @property {number|Long|null} [count] RankedEntry count * @property {number|Long|null} [sum] RankedEntry sum * @property {number|null} [avg] RankedEntry avg + * @property {Uint8Array|null} [inKey] RankedEntry inKey */ /** @@ -26275,6 +26276,14 @@ $root.org = (function() { */ RankedEntry.prototype.avg = 0; + /** + * RankedEntry inKey. + * @member {Uint8Array} inKey + * @memberof org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry + * @instance + */ + RankedEntry.prototype.inKey = $util.newBuffer([]); + // OneOf field names bound to virtual getters and setters var $oneOfFields; @@ -26321,6 +26330,8 @@ $root.org = (function() { writer.uint32(/* id 3, wireType 0 =*/24).sint64(message.sum); if (message.avg != null && Object.hasOwnProperty.call(message, "avg")) writer.uint32(/* id 4, wireType 1 =*/33).double(message.avg); + if (message.inKey != null && Object.hasOwnProperty.call(message, "inKey")) + writer.uint32(/* id 5, wireType 2 =*/42).bytes(message.inKey); return writer; }; @@ -26367,6 +26378,9 @@ $root.org = (function() { case 4: message.avg = reader.double(); break; + case 5: + message.inKey = reader.bytes(); + break; default: reader.skipType(tag & 7); break; @@ -26425,6 +26439,9 @@ $root.org = (function() { if (typeof message.avg !== "number") return "avg: number expected"; } + if (message.inKey != null && message.hasOwnProperty("inKey")) + if (!(message.inKey && typeof message.inKey.length === "number" || $util.isString(message.inKey))) + return "inKey: buffer expected"; return null; }; @@ -26465,6 +26482,11 @@ $root.org = (function() { message.sum = new $util.LongBits(object.sum.low >>> 0, object.sum.high >>> 0).toNumber(); if (object.avg != null) message.avg = Number(object.avg); + if (object.inKey != null) + if (typeof object.inKey === "string") + $util.base64.decode(object.inKey, message.inKey = $util.newBuffer($util.base64.length(object.inKey)), 0); + else if (object.inKey.length >= 0) + message.inKey = object.inKey; return message; }; @@ -26481,7 +26503,7 @@ $root.org = (function() { if (!options) options = {}; var object = {}; - if (options.defaults) + if (options.defaults) { if (options.bytes === String) object.key = ""; else { @@ -26489,6 +26511,14 @@ $root.org = (function() { if (options.bytes !== Array) object.key = $util.newBuffer(object.key); } + if (options.bytes === String) + object.inKey = ""; + else { + object.inKey = []; + if (options.bytes !== Array) + object.inKey = $util.newBuffer(object.inKey); + } + } if (message.key != null && message.hasOwnProperty("key")) object.key = options.bytes === String ? $util.base64.encode(message.key, 0, message.key.length) : options.bytes === Array ? Array.prototype.slice.call(message.key) : message.key; if (message.count != null && message.hasOwnProperty("count")) { @@ -26512,6 +26542,8 @@ $root.org = (function() { if (options.oneofs) object.value = "avg"; } + if (message.inKey != null && message.hasOwnProperty("inKey")) + object.inKey = options.bytes === String ? $util.base64.encode(message.inKey, 0, message.inKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.inKey) : message.inKey; return object; }; diff --git a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js index ad740f8e1c9..fb135455827 100644 --- a/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js +++ b/packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js @@ -30660,7 +30660,8 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank key: msg.getKey_asB64(), count: jspb.Message.getFieldWithDefault(msg, 2, "0"), sum: jspb.Message.getFieldWithDefault(msg, 3, "0"), - avg: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0) + avg: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), + inKey: msg.getInKey_asB64() }; if (includeInstance) { @@ -30713,6 +30714,10 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank var value = /** @type {number} */ (reader.readDouble()); msg.setAvg(value); break; + case 5: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setInKey(value); + break; default: reader.skipField(); break; @@ -30770,6 +30775,13 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank f ); } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 5)); + if (f != null) { + writer.writeBytes( + 5, + f + ); + } }; @@ -30923,6 +30935,66 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank }; +/** + * optional bytes in_key = 5; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** + * optional bytes in_key = 5; + * This is a type-conversion wrapper around `getInKey()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getInKey())); +}; + + +/** + * optional bytes in_key = 5; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getInKey()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getInKey())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.setInKey = function(value) { + return jspb.Message.setField(this, 5, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.clearInKey = function() { + return jspb.Message.setField(this, 5, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.hasInKey = function() { + return jspb.Message.getField(this, 5) != null; +}; + + /** * List of repeated fields within this message type. diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h index a713016035f..64fcd94d1db 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -2956,16 +2956,16 @@ typedef GPB_ENUM(GetDocumentsRequest_GetDocumentsRequestV1_Start_OneOfCase) { * - a is the In field AND b is the range field, in that order → existing compound distinct shape; entries carry both `in_key` (= a's value) and `key` (= b's value). * * `select=, group_by=[p], order_by=[]` (protocol v14+) — **ranked mode**: - * - exactly one `group_by` property, exactly one `order_by` clause naming the select's aggregate (`f` for `SUM(f)` / `AVG(f)`, the `$count` sentinel for `COUNT(*)`), a `limit` in `1 ..= 100`, an optional `offset`, and no `having` / `start_at`, on an index declaring the matching `rankedCountable` / `rankedSummable` / `rankedAverageable` axis → ranked executor, answered in `ResultData.ranked`. On a single-property ranked index no `where` is accepted; on a compound ranked index every leading index property must be pinned with an `EQUAL` where clause (one per property, `group_by` names the trailing property), selecting which prefix's ranking is read. + * - exactly one `group_by` property, exactly one `order_by` clause naming the select's aggregate (`f` for `SUM(f)` / `AVG(f)`, the `$count` sentinel for `COUNT(*)`), a `limit` in `1 ..= 100`, an optional `offset`, and no `having` / `start_at`, on an index declaring the matching `rankedCountable` / `rankedSummable` / `rankedAverageable` axis → ranked executor, answered in `ResultData.ranked`. On a single-property ranked index no `where` is accepted; on a compound ranked index every leading index property must be pinned (one clause per property, `group_by` names the trailing property) — `EQUAL` pins one prefix, and **at most one** clause may be `IN` (2..=10 distinct elements, `null` legal; a single-element `IN` normalizes to the equality pin; an element whose prefix was never written — at any depth of its pinned chain — contributes an empty branch, union semantics), fanning the walk out across one prefix branch per element and merging by `(aggregate, encoded prefix, group key)`; merged entries carry `in_key`. A non-zero `offset` is rejected together with `IN` (rank-skip is per-secondary; `OFFSET 0` is the offset-free request). * - `DESC` is the "top n" reading (walk the axis from the largest aggregate down), `ASC` the "bottom n" reading. Worked example: `SELECT AVG(grade) GROUP BY restaurantId ORDER BY grade DESC LIMIT 1 OFFSET 4` is the 5th-best restaurant. * * `select=, group_by=[p], having=[ ]` (protocol v14+) — **having-range mode**: - * - exactly one `group_by` property, exactly one `having` clause whose aggregate is the select's aggregate, an operator describing one contiguous range (`EQUAL`, `GREATER_THAN[_OR_EQUALS]`, `LESS_THAN[_OR_EQUALS]`, `BETWEEN*`; `NOT_EQUAL` / `IN` rejected), a `limit` in `1 ..= 100`, an optional `order_by` naming the same aggregate (walk direction; ascending by default), and no `offset` / `start_at` / `start_after`, on an index declaring the matching ranked axis → having-range executor, answered in `ResultData.ranked`. `where` follows the same rule as ranked mode: none on a single-property ranked index; exactly one `EQUAL` pin per leading index property on a compound ranked index. + * - exactly one `group_by` property, exactly one `having` clause whose aggregate is the select's aggregate, an operator describing one contiguous range (`EQUAL`, `GREATER_THAN[_OR_EQUALS]`, `LESS_THAN[_OR_EQUALS]`, `BETWEEN*`; `NOT_EQUAL` / `IN` rejected), a `limit` in `1 ..= 100`, an optional `order_by` naming the same aggregate (walk direction; ascending by default), and no `offset` / `start_at` / `start_after`, on an index declaring the matching ranked axis → having-range executor, answered in `ResultData.ranked`. `where` follows the same rule as ranked mode: none on a single-property ranked index; exactly one pin per leading index property on a compound ranked index, at most one of them an `IN` (2..=10 distinct elements) that fans the bound out across prefix branches and merges, entries carrying `in_key`. * - no offset or cursor pagination: a page cut at `limit` continues only by tightening the bound past the last *distinct* aggregate value seen; a cut inside a tie (several groups sharing the boundary aggregate) cannot be continued, so size `limit` above the widest expected tie. * * **Rejected shapes** (return `Unsupported`): - * - any non-empty `having` on protocol v13 and earlier; at v14+, any `having` shape outside having-range mode above (multiple clauses, an aggregate other than the select's, `NOT_EQUAL` / `IN`, a `where` shape other than the compound-index equality pins above, or a carried `offset` / cursor). - * - at v14+: a ranked-shaped request carrying a `where` shape other than the compound-index equality pins above (a non-`EQUAL` operator, a repeated or non-leading property, or a missing pin), a `start_at` / `start_after` cursor, more than one `order_by`, or an `order_by` naming anything but the selected aggregate. + * - any non-empty `having` on protocol v13 and earlier; at v14+, any `having` shape outside having-range mode above (multiple clauses, an aggregate other than the select's, `NOT_EQUAL` / `IN` as the having operator, a `where` shape other than the compound-index prefix pins above — equality pins plus at most one bounded `IN` — or a carried `offset` / cursor). + * - at v14+: a ranked-shaped request carrying a `where` shape other than the compound-index prefix pins above (an operator other than `EQUAL` or the one permitted `IN`, more than one `IN`, a `null` pin combined with an `IN`, a repeated or non-leading property, or a missing pin), a `start_at` / `start_after` cursor, more than one `order_by`, or an `order_by` naming anything but the selected aggregate. * - `select=DOCUMENTS` with non-empty `group_by`. * - `select=COUNT` with `group_by` on a field that is not constrained by an `In` or range where clause. * - `select=COUNT` with `group_by.len() > 2`. @@ -3732,6 +3732,7 @@ typedef GPB_ENUM(GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNu GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNumber_Count = 2, GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNumber_Sum = 3, GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNumber_Avg = 4, + GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNumber_InKey = 5, }; typedef GPB_ENUM(GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_Value_OneOfCase) { @@ -3816,6 +3817,19 @@ GPB_FINAL @interface GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry : G **/ @property(nonatomic, readwrite) double avg; +/** + * The prefix branch this entry came from, set **only** on an + * `IN`-pinned request (see the supported-shape table): the + * encoded index-key bytes of the `IN` property's pinned value — + * empty bytes for the `null` (absent-value) branch. Absent on + * single-prefix responses. The same group key can legally appear + * under two prefixes, so `(in_key, key)` is the entry's identity + * on a merged page, exactly as on `CountEntry`. + **/ +@property(nonatomic, readwrite, copy, null_resettable) NSData *inKey; +/** Test to see if @c inKey has been set. */ +@property(nonatomic, readwrite) BOOL hasInKey; + @end /** diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m index 15343d402ec..ece0c15cfa6 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m @@ -7166,10 +7166,12 @@ @implementation GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry @dynamic count; @dynamic sum; @dynamic avg; +@dynamic hasInKey, inKey; typedef struct GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry__storage_ { uint32_t _has_storage_[2]; NSData *key; + NSData *inKey; uint64_t count; int64_t sum; double avg; @@ -7217,6 +7219,15 @@ + (GPBDescriptor *)descriptor { .flags = GPBFieldOptional, .dataType = GPBDataTypeDouble, }, + { + .name = "inKey", + .dataTypeSpecific.clazz = Nil, + .number = GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry_FieldNumber_InKey, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry__storage_, inKey), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBytes, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GetDocumentsResponse_GetDocumentsResponseV1_RankedEntry class] diff --git a/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py index 8481dc746ea..d02d728b0cc 100644 --- a/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py +++ b/packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py @@ -23,7 +23,7 @@ syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0eplatform.proto\x12\x19org.dash.platform.dapi.v0\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x81\x01\n\x05Proof\x12\x15\n\rgrovedb_proof\x18\x01 \x01(\x0c\x12\x13\n\x0bquorum_hash\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\r\n\x05round\x18\x04 \x01(\r\x12\x15\n\rblock_id_hash\x18\x05 \x01(\x0c\x12\x13\n\x0bquorum_type\x18\x06 \x01(\r\"\x98\x01\n\x10ResponseMetadata\x12\x12\n\x06height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12 \n\x18\x63ore_chain_locked_height\x18\x02 \x01(\r\x12\r\n\x05\x65poch\x18\x03 \x01(\r\x12\x13\n\x07time_ms\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x18\n\x10protocol_version\x18\x05 \x01(\r\x12\x10\n\x08\x63hain_id\x18\x06 \x01(\t\"L\n\x1dStateTransitionBroadcastError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\";\n\x1f\x42roadcastStateTransitionRequest\x12\x18\n\x10state_transition\x18\x01 \x01(\x0c\"\"\n BroadcastStateTransitionResponse\"\xa4\x01\n\x12GetIdentityRequest\x12P\n\x02v0\x18\x01 \x01(\x0b\x32\x42.org.dash.platform.dapi.v0.GetIdentityRequest.GetIdentityRequestV0H\x00\x1a\x31\n\x14GetIdentityRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xc1\x01\n\x17GetIdentityNonceRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetIdentityNonceRequest.GetIdentityNonceRequestV0H\x00\x1a?\n\x19GetIdentityNonceRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf6\x01\n\x1fGetIdentityContractNonceRequest\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentityContractNonceRequest.GetIdentityContractNonceRequestV0H\x00\x1a\\\n!GetIdentityContractNonceRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ontract_id\x18\x02 \x01(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xc0\x01\n\x19GetIdentityBalanceRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetIdentityBalanceRequest.GetIdentityBalanceRequestV0H\x00\x1a\x38\n\x1bGetIdentityBalanceRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xec\x01\n$GetIdentityBalanceAndRevisionRequest\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionRequest.GetIdentityBalanceAndRevisionRequestV0H\x00\x1a\x43\n&GetIdentityBalanceAndRevisionRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9e\x02\n\x13GetIdentityResponse\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetIdentityResponse.GetIdentityResponseV0H\x00\x1a\xa7\x01\n\x15GetIdentityResponseV0\x12\x12\n\x08identity\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbc\x02\n\x18GetIdentityNonceResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetIdentityNonceResponse.GetIdentityNonceResponseV0H\x00\x1a\xb6\x01\n\x1aGetIdentityNonceResponseV0\x12\x1c\n\x0eidentity_nonce\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xe5\x02\n GetIdentityContractNonceResponse\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentityContractNonceResponse.GetIdentityContractNonceResponseV0H\x00\x1a\xc7\x01\n\"GetIdentityContractNonceResponseV0\x12%\n\x17identity_contract_nonce\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbd\x02\n\x1aGetIdentityBalanceResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetIdentityBalanceResponse.GetIdentityBalanceResponseV0H\x00\x1a\xb1\x01\n\x1cGetIdentityBalanceResponseV0\x12\x15\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb1\x04\n%GetIdentityBalanceAndRevisionResponse\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse.GetIdentityBalanceAndRevisionResponseV0H\x00\x1a\x84\x03\n\'GetIdentityBalanceAndRevisionResponseV0\x12\x9b\x01\n\x14\x62\x61lance_and_revision\x18\x01 \x01(\x0b\x32{.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse.GetIdentityBalanceAndRevisionResponseV0.BalanceAndRevisionH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a?\n\x12\x42\x61lanceAndRevision\x12\x13\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x14\n\x08revision\x18\x02 \x01(\x04\x42\x02\x30\x01\x42\x08\n\x06resultB\t\n\x07version\"\xd1\x01\n\x0eKeyRequestType\x12\x36\n\x08\x61ll_keys\x18\x01 \x01(\x0b\x32\".org.dash.platform.dapi.v0.AllKeysH\x00\x12@\n\rspecific_keys\x18\x02 \x01(\x0b\x32\'.org.dash.platform.dapi.v0.SpecificKeysH\x00\x12:\n\nsearch_key\x18\x03 \x01(\x0b\x32$.org.dash.platform.dapi.v0.SearchKeyH\x00\x42\t\n\x07request\"\t\n\x07\x41llKeys\"\x1f\n\x0cSpecificKeys\x12\x0f\n\x07key_ids\x18\x01 \x03(\r\"\xb6\x01\n\tSearchKey\x12I\n\x0bpurpose_map\x18\x01 \x03(\x0b\x32\x34.org.dash.platform.dapi.v0.SearchKey.PurposeMapEntry\x1a^\n\x0fPurposeMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.org.dash.platform.dapi.v0.SecurityLevelMap:\x02\x38\x01\"\xbf\x02\n\x10SecurityLevelMap\x12]\n\x12security_level_map\x18\x01 \x03(\x0b\x32\x41.org.dash.platform.dapi.v0.SecurityLevelMap.SecurityLevelMapEntry\x1aw\n\x15SecurityLevelMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12M\n\x05value\x18\x02 \x01(\x0e\x32>.org.dash.platform.dapi.v0.SecurityLevelMap.KeyKindRequestType:\x02\x38\x01\"S\n\x12KeyKindRequestType\x12\x1f\n\x1b\x43URRENT_KEY_OF_KIND_REQUEST\x10\x00\x12\x1c\n\x18\x41LL_KEYS_OF_KIND_REQUEST\x10\x01\"\xda\x02\n\x16GetIdentityKeysRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetIdentityKeysRequest.GetIdentityKeysRequestV0H\x00\x1a\xda\x01\n\x18GetIdentityKeysRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12?\n\x0crequest_type\x18\x02 \x01(\x0b\x32).org.dash.platform.dapi.v0.KeyRequestType\x12+\n\x05limit\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\x99\x03\n\x17GetIdentityKeysResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetIdentityKeysResponse.GetIdentityKeysResponseV0H\x00\x1a\x96\x02\n\x19GetIdentityKeysResponseV0\x12\x61\n\x04keys\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetIdentityKeysResponse.GetIdentityKeysResponseV0.KeysH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1a\n\x04Keys\x12\x12\n\nkeys_bytes\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xef\x02\n GetIdentitiesContractKeysRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentitiesContractKeysRequest.GetIdentitiesContractKeysRequestV0H\x00\x1a\xd1\x01\n\"GetIdentitiesContractKeysRequestV0\x12\x16\n\x0eidentities_ids\x18\x01 \x03(\x0c\x12\x13\n\x0b\x63ontract_id\x18\x02 \x01(\x0c\x12\x1f\n\x12\x64ocument_type_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x37\n\x08purposes\x18\x04 \x03(\x0e\x32%.org.dash.platform.dapi.v0.KeyPurpose\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x15\n\x13_document_type_nameB\t\n\x07version\"\xdf\x06\n!GetIdentitiesContractKeysResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0H\x00\x1a\xbe\x05\n#GetIdentitiesContractKeysResponseV0\x12\x8a\x01\n\x0fidentities_keys\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.IdentitiesKeysH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aY\n\x0bPurposeKeys\x12\x36\n\x07purpose\x18\x01 \x01(\x0e\x32%.org.dash.platform.dapi.v0.KeyPurpose\x12\x12\n\nkeys_bytes\x18\x02 \x03(\x0c\x1a\x9f\x01\n\x0cIdentityKeys\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12z\n\x04keys\x18\x02 \x03(\x0b\x32l.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.PurposeKeys\x1a\x90\x01\n\x0eIdentitiesKeys\x12~\n\x07\x65ntries\x18\x01 \x03(\x0b\x32m.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.IdentityKeysB\x08\n\x06resultB\t\n\x07version\"\xa4\x02\n*GetEvonodesProposedEpochBlocksByIdsRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByIdsRequest.GetEvonodesProposedEpochBlocksByIdsRequestV0H\x00\x1ah\n,GetEvonodesProposedEpochBlocksByIdsRequestV0\x12\x12\n\x05\x65poch\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x0b\n\x03ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\x08\n\x06_epochB\t\n\x07version\"\x92\x06\n&GetEvonodesProposedEpochBlocksResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0H\x00\x1a\xe2\x04\n(GetEvonodesProposedEpochBlocksResponseV0\x12\xb1\x01\n#evonodes_proposed_block_counts_info\x18\x01 \x01(\x0b\x32\x81\x01.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0.EvonodesProposedBlocksH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a?\n\x15\x45vonodeProposedBlocks\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x02 \x01(\x04\x42\x02\x30\x01\x1a\xc4\x01\n\x16\x45vonodesProposedBlocks\x12\xa9\x01\n\x1e\x65vonodes_proposed_block_counts\x18\x01 \x03(\x0b\x32\x80\x01.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0.EvonodeProposedBlocksB\x08\n\x06resultB\t\n\x07version\"\xf2\x02\n,GetEvonodesProposedEpochBlocksByRangeRequest\x12\x84\x01\n\x02v0\x18\x01 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByRangeRequest.GetEvonodesProposedEpochBlocksByRangeRequestV0H\x00\x1a\xaf\x01\n.GetEvonodesProposedEpochBlocksByRangeRequestV0\x12\x12\n\x05\x65poch\x18\x01 \x01(\rH\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x02 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x0bstart_after\x18\x03 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x04 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x07\n\x05startB\x08\n\x06_epochB\x08\n\x06_limitB\t\n\x07version\"\xcd\x01\n\x1cGetIdentitiesBalancesRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetIdentitiesBalancesRequest.GetIdentitiesBalancesRequestV0H\x00\x1a<\n\x1eGetIdentitiesBalancesRequestV0\x12\x0b\n\x03ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9f\x05\n\x1dGetIdentitiesBalancesResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0H\x00\x1a\x8a\x04\n\x1fGetIdentitiesBalancesResponseV0\x12\x8a\x01\n\x13identities_balances\x18\x01 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0.IdentitiesBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aL\n\x0fIdentityBalance\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x18\n\x07\x62\x61lance\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\x8f\x01\n\x12IdentitiesBalances\x12y\n\x07\x65ntries\x18\x01 \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0.IdentityBalanceB\x08\n\x06resultB\t\n\x07version\"\xb4\x01\n\x16GetDataContractRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetDataContractRequest.GetDataContractRequestV0H\x00\x1a\x35\n\x18GetDataContractRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xb3\x02\n\x17GetDataContractResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetDataContractResponse.GetDataContractResponseV0H\x00\x1a\xb0\x01\n\x19GetDataContractResponseV0\x12\x17\n\rdata_contract\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb9\x01\n\x17GetDataContractsRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetDataContractsRequest.GetDataContractsRequestV0H\x00\x1a\x37\n\x19GetDataContractsRequestV0\x12\x0b\n\x03ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xcf\x04\n\x18GetDataContractsResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetDataContractsResponse.GetDataContractsResponseV0H\x00\x1a[\n\x11\x44\x61taContractEntry\x12\x12\n\nidentifier\x18\x01 \x01(\x0c\x12\x32\n\rdata_contract\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.BytesValue\x1au\n\rDataContracts\x12\x64\n\x15\x64\x61ta_contract_entries\x18\x01 \x03(\x0b\x32\x45.org.dash.platform.dapi.v0.GetDataContractsResponse.DataContractEntry\x1a\xf5\x01\n\x1aGetDataContractsResponseV0\x12[\n\x0e\x64\x61ta_contracts\x18\x01 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDataContractsResponse.DataContractsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc5\x02\n\x1dGetDataContractHistoryRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetDataContractHistoryRequest.GetDataContractHistoryRequestV0H\x00\x1a\xb0\x01\n\x1fGetDataContractHistoryRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12+\n\x05limit\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0bstart_at_ms\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\xb2\x05\n\x1eGetDataContractHistoryResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0H\x00\x1a\x9a\x04\n GetDataContractHistoryResponseV0\x12\x8f\x01\n\x15\x64\x61ta_contract_history\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0.DataContractHistoryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a;\n\x18\x44\x61taContractHistoryEntry\x12\x10\n\x04\x64\x61te\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05value\x18\x02 \x01(\x0c\x1a\xaa\x01\n\x13\x44\x61taContractHistory\x12\x92\x01\n\x15\x64\x61ta_contract_entries\x18\x01 \x03(\x0b\x32s.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0.DataContractHistoryEntryB\x08\n\x06resultB\t\n\x07version\"\xf1\x15\n\x13GetDocumentsRequest\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV0H\x00\x12R\n\x02v1\x18\x02 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1H\x00\x1a\xfe\x02\n\x12\x44ocumentFieldValue\x12\x14\n\nbool_value\x18\x01 \x01(\x08H\x00\x12\x19\n\x0bint64_value\x18\x02 \x01(\x12\x42\x02\x30\x01H\x00\x12\x1a\n\x0cuint64_value\x18\x03 \x01(\x04\x42\x02\x30\x01H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04text\x18\x05 \x01(\tH\x00\x12\x15\n\x0b\x62ytes_value\x18\x06 \x01(\x0cH\x00\x12[\n\x04list\x18\x07 \x01(\x0b\x32K.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValue.ValueListH\x00\x12\x14\n\nnull_value\x18\x08 \x01(\x08H\x00\x1a^\n\tValueList\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValueB\t\n\x07variant\x1a\xbe\x01\n\x0bWhereClause\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12N\n\x08operator\x18\x02 \x01(\x0e\x32<.org.dash.platform.dapi.v0.GetDocumentsRequest.WhereOperator\x12P\n\x05value\x18\x03 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValue\x1a\xa4\x01\n\x0fHavingAggregate\x12Y\n\x08\x66unction\x18\x01 \x01(\x0e\x32G.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregate.Function\x12\r\n\x05\x66ield\x18\x02 \x01(\t\"\'\n\x08\x46unction\x12\t\n\x05\x43OUNT\x10\x00\x12\x07\n\x03SUM\x10\x01\x12\x07\n\x03\x41VG\x10\x02\x1a\xf9\x03\n\x0cHavingClause\x12Q\n\taggregate\x18\x01 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregate\x12V\n\x08operator\x18\x02 \x01(\x0e\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingClause.Operator\x12R\n\x05value\x18\x03 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValueH\x00\"\xe0\x01\n\x08Operator\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x1a\n\x16GREATER_THAN_OR_EQUALS\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x17\n\x13LESS_THAN_OR_EQUALS\x10\x05\x12\x0b\n\x07\x42\x45TWEEN\x10\x06\x12\x1a\n\x16\x42\x45TWEEN_EXCLUDE_BOUNDS\x10\x07\x12\x18\n\x14\x42\x45TWEEN_EXCLUDE_LEFT\x10\x08\x12\x19\n\x15\x42\x45TWEEN_EXCLUDE_RIGHT\x10\t\x12\x06\n\x02IN\x10\nB\x07\n\x05right\x1a\x90\x01\n\x0bOrderClause\x12\x0f\n\x05\x66ield\x18\x01 \x01(\tH\x00\x12S\n\taggregate\x18\x03 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregateH\x00\x12\x11\n\tascending\x18\x02 \x01(\x08\x42\x08\n\x06target\x1a\xbb\x01\n\x15GetDocumentsRequestV0\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x15\n\rdocument_type\x18\x02 \x01(\t\x12\r\n\x05where\x18\x03 \x01(\x0c\x12\x10\n\x08order_by\x18\x04 \x01(\x0c\x12\r\n\x05limit\x18\x05 \x01(\r\x12\x15\n\x0bstart_after\x18\x06 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x07 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x08 \x01(\x08\x42\x07\n\x05start\x1a\xf3\x05\n\x15GetDocumentsRequestV1\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x15\n\rdocument_type\x18\x02 \x01(\t\x12Q\n\rwhere_clauses\x18\x03 \x03(\x0b\x32:.org.dash.platform.dapi.v0.GetDocumentsRequest.WhereClause\x12L\n\x08order_by\x18\x04 \x03(\x0b\x32:.org.dash.platform.dapi.v0.GetDocumentsRequest.OrderClause\x12\x12\n\x05limit\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x0bstart_after\x18\x06 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x07 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x08 \x01(\x08\x12\\\n\x07selects\x18\t \x03(\x0b\x32K.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1.Select\x12\x10\n\x08group_by\x18\n \x03(\t\x12K\n\x06having\x18\x0b \x03(\x0b\x32;.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingClause\x12\x13\n\x06offset\x18\x0c \x01(\rH\x02\x88\x01\x01\x1a\xc9\x01\n\x06Select\x12\x66\n\x08\x66unction\x18\x01 \x01(\x0e\x32T.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1.Select.Function\x12\r\n\x05\x66ield\x18\x02 \x01(\t\"H\n\x08\x46unction\x12\r\n\tDOCUMENTS\x10\x00\x12\t\n\x05\x43OUNT\x10\x01\x12\x07\n\x03SUM\x10\x02\x12\x07\n\x03\x41VG\x10\x03\x12\x07\n\x03MIN\x10\x04\x12\x07\n\x03MAX\x10\x05\x42\x07\n\x05startB\x08\n\x06_limitB\t\n\x07_offset\"\xfa\x01\n\rWhereOperator\x12\t\n\x05\x45QUAL\x10\x00\x12\x10\n\x0cGREATER_THAN\x10\x01\x12\x1a\n\x16GREATER_THAN_OR_EQUALS\x10\x02\x12\r\n\tLESS_THAN\x10\x03\x12\x17\n\x13LESS_THAN_OR_EQUALS\x10\x04\x12\x0b\n\x07\x42\x45TWEEN\x10\x05\x12\x1a\n\x16\x42\x45TWEEN_EXCLUDE_BOUNDS\x10\x06\x12\x18\n\x14\x42\x45TWEEN_EXCLUDE_LEFT\x10\x07\x12\x19\n\x15\x42\x45TWEEN_EXCLUDE_RIGHT\x10\x08\x12\x06\n\x02IN\x10\t\x12\x0f\n\x0bSTARTS_WITH\x10\n\x12\x11\n\rIN_TIME_RANGE\x10\x0b\x42\t\n\x07version\"\xe7\x15\n\x14GetDocumentsResponse\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV0H\x00\x12T\n\x02v1\x18\x02 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1H\x00\x1a\x9b\x02\n\x16GetDocumentsResponseV0\x12\x65\n\tdocuments\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV0.DocumentsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1e\n\tDocuments\x12\x11\n\tdocuments\x18\x01 \x03(\x0c\x42\x08\n\x06result\x1a\xf9\x11\n\x16GetDocumentsResponseV1\x12\x61\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.ResultDataH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1e\n\tDocuments\x12\x11\n\tdocuments\x18\x01 \x03(\x0c\x1aL\n\nCountEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x03 \x01(\x04\x42\x02\x30\x01\x42\t\n\x07_in_key\x1ar\n\x0c\x43ountEntries\x12\x62\n\x07\x65ntries\x18\x01 \x03(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountEntry\x1a\xa0\x01\n\x0c\x43ountResults\x12\x1d\n\x0f\x61ggregate_count\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x66\n\x07\x65ntries\x18\x02 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountEntriesH\x00\x42\t\n\x07variant\x1aH\n\x08SumEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x0f\n\x03sum\x18\x03 \x01(\x12\x42\x02\x30\x01\x42\t\n\x07_in_key\x1an\n\nSumEntries\x12`\n\x07\x65ntries\x18\x01 \x03(\x0b\x32O.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumEntry\x1a\x9a\x01\n\nSumResults\x12\x1b\n\raggregate_sum\x18\x01 \x01(\x12\x42\x02\x30\x01H\x00\x12\x64\n\x07\x65ntries\x18\x02 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumEntriesH\x00\x42\t\n\x07variant\x1a_\n\x0c\x41verageEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x03 \x01(\x04\x42\x02\x30\x01\x12\x0f\n\x03sum\x18\x04 \x01(\x12\x42\x02\x30\x01\x42\t\n\x07_in_key\x1av\n\x0e\x41verageEntries\x12\x64\n\x07\x65ntries\x18\x01 \x03(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageEntry\x1a\x36\n\x10\x41verageAggregate\x12\x11\n\x05\x63ount\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x0f\n\x03sum\x18\x02 \x01(\x12\x42\x02\x30\x01\x1a\xfb\x01\n\x0e\x41verageResults\x12t\n\x11\x61ggregate_average\x18\x01 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageAggregateH\x00\x12h\n\x07\x65ntries\x18\x02 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageEntriesH\x00\x42\t\n\x07variant\x1aZ\n\x0bRankedEntry\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\x13\n\x05\x63ount\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x11\n\x03sum\x18\x03 \x01(\x12\x42\x02\x30\x01H\x00\x12\r\n\x03\x61vg\x18\x04 \x01(\x01H\x00\x42\x07\n\x05value\x1a\x9a\x01\n\rRankedEntries\x12\x63\n\x07\x65ntries\x18\x01 \x03(\x0b\x32R.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry\x12\x18\n\x07skipped\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x42\n\n\x08_skipped\x1a\x9b\x04\n\nResultData\x12\x65\n\tdocuments\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.DocumentsH\x00\x12\x65\n\x06\x63ounts\x18\x02 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountResultsH\x00\x12\x61\n\x04sums\x18\x03 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumResultsH\x00\x12i\n\x08\x61verages\x18\x04 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageResultsH\x00\x12\x66\n\x06ranked\x18\x05 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntriesH\x00\x42\t\n\x07variantB\x08\n\x06resultB\t\n\x07version\"\xf4\x02\n\x19GetDocumentHistoryRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentHistoryRequest.GetDocumentHistoryRequestV0H\x00\x1a\xeb\x01\n\x1bGetDocumentHistoryRequestV0\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x03 \x01(\x0c\x12+\n\x05limit\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0bstart_at_ms\x18\x06 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x07 \x01(\x08\x42\t\n\x07version\"\xf7\x04\n\x1aGetDocumentHistoryResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0H\x00\x1a\xeb\x03\n\x1cGetDocumentHistoryResponseV0\x12~\n\x10\x64ocument_history\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0.DocumentHistoryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x37\n\x14\x44ocumentHistoryEntry\x12\x10\n\x04\x64\x61te\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05value\x18\x02 \x01(\x0c\x1a\x95\x01\n\x0f\x44ocumentHistory\x12\x81\x01\n\x10\x64ocument_entries\x18\x01 \x03(\x0b\x32g.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0.DocumentHistoryEntryB\x08\n\x06resultB\t\n\x07version\"\xed\x01\n!GetIdentityByPublicKeyHashRequest\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashRequest.GetIdentityByPublicKeyHashRequestV0H\x00\x1aM\n#GetIdentityByPublicKeyHashRequestV0\x12\x17\n\x0fpublic_key_hash\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xda\x02\n\"GetIdentityByPublicKeyHashResponse\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashResponse.GetIdentityByPublicKeyHashResponseV0H\x00\x1a\xb6\x01\n$GetIdentityByPublicKeyHashResponseV0\x12\x12\n\x08identity\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbd\x02\n*GetIdentityByNonUniquePublicKeyHashRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashRequest.GetIdentityByNonUniquePublicKeyHashRequestV0H\x00\x1a\x80\x01\n,GetIdentityByNonUniquePublicKeyHashRequestV0\x12\x17\n\x0fpublic_key_hash\x18\x01 \x01(\x0c\x12\x18\n\x0bstart_after\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\x0e\n\x0c_start_afterB\t\n\x07version\"\xd6\x06\n+GetIdentityByNonUniquePublicKeyHashResponse\x12\x82\x01\n\x02v0\x18\x01 \x01(\x0b\x32t.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0H\x00\x1a\x96\x05\n-GetIdentityByNonUniquePublicKeyHashResponseV0\x12\x9a\x01\n\x08identity\x18\x01 \x01(\x0b\x32\x85\x01.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0.IdentityResponseH\x00\x12\x9d\x01\n\x05proof\x18\x02 \x01(\x0b\x32\x8b\x01.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0.IdentityProvedResponseH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x36\n\x10IdentityResponse\x12\x15\n\x08identity\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_identity\x1a\xa6\x01\n\x16IdentityProvedResponse\x12P\n&grovedb_identity_public_key_hash_proof\x18\x01 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12!\n\x14identity_proof_bytes\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\x17\n\x15_identity_proof_bytesB\x08\n\x06resultB\t\n\x07version\"\xfb\x01\n#WaitForStateTransitionResultRequest\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.WaitForStateTransitionResultRequestV0H\x00\x1aU\n%WaitForStateTransitionResultRequestV0\x12\x1d\n\x15state_transition_hash\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x99\x03\n$WaitForStateTransitionResultResponse\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.WaitForStateTransitionResultResponseV0H\x00\x1a\xef\x01\n&WaitForStateTransitionResultResponseV0\x12I\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x38.org.dash.platform.dapi.v0.StateTransitionBroadcastErrorH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc4\x01\n\x19GetConsensusParamsRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetConsensusParamsRequest.GetConsensusParamsRequestV0H\x00\x1a<\n\x1bGetConsensusParamsRequestV0\x12\x0e\n\x06height\x18\x01 \x01(\x05\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9c\x04\n\x1aGetConsensusParamsResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetConsensusParamsResponse.GetConsensusParamsResponseV0H\x00\x1aP\n\x14\x43onsensusParamsBlock\x12\x11\n\tmax_bytes\x18\x01 \x01(\t\x12\x0f\n\x07max_gas\x18\x02 \x01(\t\x12\x14\n\x0ctime_iota_ms\x18\x03 \x01(\t\x1a\x62\n\x17\x43onsensusParamsEvidence\x12\x1a\n\x12max_age_num_blocks\x18\x01 \x01(\t\x12\x18\n\x10max_age_duration\x18\x02 \x01(\t\x12\x11\n\tmax_bytes\x18\x03 \x01(\t\x1a\xda\x01\n\x1cGetConsensusParamsResponseV0\x12Y\n\x05\x62lock\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetConsensusParamsResponse.ConsensusParamsBlock\x12_\n\x08\x65vidence\x18\x02 \x01(\x0b\x32M.org.dash.platform.dapi.v0.GetConsensusParamsResponse.ConsensusParamsEvidenceB\t\n\x07version\"\xe4\x01\n%GetProtocolVersionUpgradeStateRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateRequest.GetProtocolVersionUpgradeStateRequestV0H\x00\x1a\x38\n\'GetProtocolVersionUpgradeStateRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xb5\x05\n&GetProtocolVersionUpgradeStateResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0H\x00\x1a\x85\x04\n(GetProtocolVersionUpgradeStateResponseV0\x12\x87\x01\n\x08versions\x18\x01 \x01(\x0b\x32s.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0.VersionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x96\x01\n\x08Versions\x12\x89\x01\n\x08versions\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0.VersionEntry\x1a:\n\x0cVersionEntry\x12\x16\n\x0eversion_number\x18\x01 \x01(\r\x12\x12\n\nvote_count\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xa3\x02\n*GetProtocolVersionUpgradeVoteStatusRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusRequest.GetProtocolVersionUpgradeVoteStatusRequestV0H\x00\x1ag\n,GetProtocolVersionUpgradeVoteStatusRequestV0\x12\x19\n\x11start_pro_tx_hash\x18\x01 \x01(\x0c\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xef\x05\n+GetProtocolVersionUpgradeVoteStatusResponse\x12\x82\x01\n\x02v0\x18\x01 \x01(\x0b\x32t.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0H\x00\x1a\xaf\x04\n-GetProtocolVersionUpgradeVoteStatusResponseV0\x12\x98\x01\n\x08versions\x18\x01 \x01(\x0b\x32\x83\x01.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0.VersionSignalsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xaf\x01\n\x0eVersionSignals\x12\x9c\x01\n\x0fversion_signals\x18\x01 \x03(\x0b\x32\x82\x01.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0.VersionSignal\x1a\x35\n\rVersionSignal\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x0f\n\x07version\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xf5\x01\n\x14GetEpochsInfoRequest\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetEpochsInfoRequest.GetEpochsInfoRequestV0H\x00\x1a|\n\x16GetEpochsInfoRequestV0\x12\x31\n\x0bstart_epoch\x18\x01 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\x11\n\tascending\x18\x03 \x01(\x08\x12\r\n\x05prove\x18\x04 \x01(\x08\x42\t\n\x07version\"\x99\x05\n\x15GetEpochsInfoResponse\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0H\x00\x1a\x9c\x04\n\x17GetEpochsInfoResponseV0\x12\x65\n\x06\x65pochs\x18\x01 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0.EpochInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1au\n\nEpochInfos\x12g\n\x0b\x65poch_infos\x18\x01 \x03(\x0b\x32R.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0.EpochInfo\x1a\xa6\x01\n\tEpochInfo\x12\x0e\n\x06number\x18\x01 \x01(\r\x12\x1e\n\x12\x66irst_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x1f\n\x17\x66irst_core_block_height\x18\x03 \x01(\r\x12\x16\n\nstart_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x0e\x66\x65\x65_multiplier\x18\x05 \x01(\x01\x12\x18\n\x10protocol_version\x18\x06 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xbf\x02\n\x1dGetFinalizedEpochInfosRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetFinalizedEpochInfosRequest.GetFinalizedEpochInfosRequestV0H\x00\x1a\xaa\x01\n\x1fGetFinalizedEpochInfosRequestV0\x12\x19\n\x11start_epoch_index\x18\x01 \x01(\r\x12\"\n\x1astart_epoch_index_included\x18\x02 \x01(\x08\x12\x17\n\x0f\x65nd_epoch_index\x18\x03 \x01(\r\x12 \n\x18\x65nd_epoch_index_included\x18\x04 \x01(\x08\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\xbd\t\n\x1eGetFinalizedEpochInfosResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0H\x00\x1a\xa5\x08\n GetFinalizedEpochInfosResponseV0\x12\x80\x01\n\x06\x65pochs\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.FinalizedEpochInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xa4\x01\n\x13\x46inalizedEpochInfos\x12\x8c\x01\n\x15\x66inalized_epoch_infos\x18\x01 \x03(\x0b\x32m.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.FinalizedEpochInfo\x1a\x9f\x04\n\x12\x46inalizedEpochInfo\x12\x0e\n\x06number\x18\x01 \x01(\r\x12\x1e\n\x12\x66irst_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x1f\n\x17\x66irst_core_block_height\x18\x03 \x01(\r\x12\x1c\n\x10\x66irst_block_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x0e\x66\x65\x65_multiplier\x18\x05 \x01(\x01\x12\x18\n\x10protocol_version\x18\x06 \x01(\r\x12!\n\x15total_blocks_in_epoch\x18\x07 \x01(\x04\x42\x02\x30\x01\x12*\n\"next_epoch_start_core_block_height\x18\x08 \x01(\r\x12!\n\x15total_processing_fees\x18\t \x01(\x04\x42\x02\x30\x01\x12*\n\x1etotal_distributed_storage_fees\x18\n \x01(\x04\x42\x02\x30\x01\x12&\n\x1atotal_created_storage_fees\x18\x0b \x01(\x04\x42\x02\x30\x01\x12\x1e\n\x12\x63ore_block_rewards\x18\x0c \x01(\x04\x42\x02\x30\x01\x12\x81\x01\n\x0f\x62lock_proposers\x18\r \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.BlockProposer\x1a\x39\n\rBlockProposer\x12\x13\n\x0bproposer_id\x18\x01 \x01(\x0c\x12\x13\n\x0b\x62lock_count\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xde\x04\n\x1cGetContestedResourcesRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetContestedResourcesRequest.GetContestedResourcesRequestV0H\x00\x1a\xcc\x03\n\x1eGetContestedResourcesRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x1a\n\x12start_index_values\x18\x04 \x03(\x0c\x12\x18\n\x10\x65nd_index_values\x18\x05 \x03(\x0c\x12\x89\x01\n\x13start_at_value_info\x18\x06 \x01(\x0b\x32g.org.dash.platform.dapi.v0.GetContestedResourcesRequest.GetContestedResourcesRequestV0.StartAtValueInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x07 \x01(\rH\x01\x88\x01\x01\x12\x17\n\x0forder_ascending\x18\x08 \x01(\x08\x12\r\n\x05prove\x18\t \x01(\x08\x1a\x45\n\x10StartAtValueInfo\x12\x13\n\x0bstart_value\x18\x01 \x01(\x0c\x12\x1c\n\x14start_value_included\x18\x02 \x01(\x08\x42\x16\n\x14_start_at_value_infoB\x08\n\x06_countB\t\n\x07version\"\x88\x04\n\x1dGetContestedResourcesResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetContestedResourcesResponse.GetContestedResourcesResponseV0H\x00\x1a\xf3\x02\n\x1fGetContestedResourcesResponseV0\x12\x95\x01\n\x19\x63ontested_resource_values\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetContestedResourcesResponse.GetContestedResourcesResponseV0.ContestedResourceValuesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a<\n\x17\x43ontestedResourceValues\x12!\n\x19\x63ontested_resource_values\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xd2\x05\n\x1cGetVotePollsByEndDateRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0H\x00\x1a\xc0\x04\n\x1eGetVotePollsByEndDateRequestV0\x12\x84\x01\n\x0fstart_time_info\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0.StartAtTimeInfoH\x00\x88\x01\x01\x12\x80\x01\n\rend_time_info\x18\x02 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0.EndAtTimeInfoH\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x13\n\x06offset\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x11\n\tascending\x18\x05 \x01(\x08\x12\r\n\x05prove\x18\x06 \x01(\x08\x1aI\n\x0fStartAtTimeInfo\x12\x19\n\rstart_time_ms\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x13start_time_included\x18\x02 \x01(\x08\x1a\x43\n\rEndAtTimeInfo\x12\x17\n\x0b\x65nd_time_ms\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x19\n\x11\x65nd_time_included\x18\x02 \x01(\x08\x42\x12\n\x10_start_time_infoB\x10\n\x0e_end_time_infoB\x08\n\x06_limitB\t\n\x07_offsetB\t\n\x07version\"\x83\x06\n\x1dGetVotePollsByEndDateResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0H\x00\x1a\xee\x04\n\x1fGetVotePollsByEndDateResponseV0\x12\x9c\x01\n\x18vote_polls_by_timestamps\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0.SerializedVotePollsByTimestampsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aV\n\x1eSerializedVotePollsByTimestamp\x12\x15\n\ttimestamp\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1d\n\x15serialized_vote_polls\x18\x02 \x03(\x0c\x1a\xd7\x01\n\x1fSerializedVotePollsByTimestamps\x12\x99\x01\n\x18vote_polls_by_timestamps\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0.SerializedVotePollsByTimestamp\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x42\x08\n\x06resultB\t\n\x07version\"\xff\x06\n$GetContestedResourceVoteStateRequest\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0H\x00\x1a\xd5\x05\n&GetContestedResourceVoteStateRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x14\n\x0cindex_values\x18\x04 \x03(\x0c\x12\x86\x01\n\x0bresult_type\x18\x05 \x01(\x0e\x32q.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0.ResultType\x12\x36\n.allow_include_locked_and_abstaining_vote_tally\x18\x06 \x01(\x08\x12\xa3\x01\n\x18start_at_identifier_info\x18\x07 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0.StartAtIdentifierInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x08 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\t \x01(\x08\x1aT\n\x15StartAtIdentifierInfo\x12\x18\n\x10start_identifier\x18\x01 \x01(\x0c\x12!\n\x19start_identifier_included\x18\x02 \x01(\x08\"I\n\nResultType\x12\r\n\tDOCUMENTS\x10\x00\x12\x0e\n\nVOTE_TALLY\x10\x01\x12\x1c\n\x18\x44OCUMENTS_AND_VOTE_TALLY\x10\x02\x42\x1b\n\x19_start_at_identifier_infoB\x08\n\x06_countB\t\n\x07version\"\x94\x0c\n%GetContestedResourceVoteStateResponse\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0H\x00\x1a\xe7\n\n\'GetContestedResourceVoteStateResponseV0\x12\xae\x01\n\x1d\x63ontested_resource_contenders\x18\x01 \x01(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.ContestedResourceContendersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xda\x03\n\x10\x46inishedVoteInfo\x12\xad\x01\n\x15\x66inished_vote_outcome\x18\x01 \x01(\x0e\x32\x8d\x01.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.FinishedVoteInfo.FinishedVoteOutcome\x12\x1f\n\x12won_by_identity_id\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12$\n\x18\x66inished_at_block_height\x18\x03 \x01(\x04\x42\x02\x30\x01\x12%\n\x1d\x66inished_at_core_block_height\x18\x04 \x01(\r\x12%\n\x19\x66inished_at_block_time_ms\x18\x05 \x01(\x04\x42\x02\x30\x01\x12\x19\n\x11\x66inished_at_epoch\x18\x06 \x01(\r\"O\n\x13\x46inishedVoteOutcome\x12\x14\n\x10TOWARDS_IDENTITY\x10\x00\x12\n\n\x06LOCKED\x10\x01\x12\x16\n\x12NO_PREVIOUS_WINNER\x10\x02\x42\x15\n\x13_won_by_identity_id\x1a\xc4\x03\n\x1b\x43ontestedResourceContenders\x12\x86\x01\n\ncontenders\x18\x01 \x03(\x0b\x32r.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.Contender\x12\x1f\n\x12\x61\x62stain_vote_tally\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x1c\n\x0flock_vote_tally\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x9a\x01\n\x12\x66inished_vote_info\x18\x04 \x01(\x0b\x32y.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.FinishedVoteInfoH\x02\x88\x01\x01\x42\x15\n\x13_abstain_vote_tallyB\x12\n\x10_lock_vote_tallyB\x15\n\x13_finished_vote_info\x1ak\n\tContender\x12\x12\n\nidentifier\x18\x01 \x01(\x0c\x12\x17\n\nvote_count\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08\x64ocument\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x42\r\n\x0b_vote_countB\x0b\n\t_documentB\x08\n\x06resultB\t\n\x07version\"\xd5\x05\n,GetContestedResourceVotersForIdentityRequest\x12\x84\x01\n\x02v0\x18\x01 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest.GetContestedResourceVotersForIdentityRequestV0H\x00\x1a\x92\x04\n.GetContestedResourceVotersForIdentityRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x14\n\x0cindex_values\x18\x04 \x03(\x0c\x12\x15\n\rcontestant_id\x18\x05 \x01(\x0c\x12\xb4\x01\n\x18start_at_identifier_info\x18\x06 \x01(\x0b\x32\x8c\x01.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest.GetContestedResourceVotersForIdentityRequestV0.StartAtIdentifierInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x07 \x01(\rH\x01\x88\x01\x01\x12\x17\n\x0forder_ascending\x18\x08 \x01(\x08\x12\r\n\x05prove\x18\t \x01(\x08\x1aT\n\x15StartAtIdentifierInfo\x12\x18\n\x10start_identifier\x18\x01 \x01(\x0c\x12!\n\x19start_identifier_included\x18\x02 \x01(\x08\x42\x1b\n\x19_start_at_identifier_infoB\x08\n\x06_countB\t\n\x07version\"\xf1\x04\n-GetContestedResourceVotersForIdentityResponse\x12\x86\x01\n\x02v0\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse.GetContestedResourceVotersForIdentityResponseV0H\x00\x1a\xab\x03\n/GetContestedResourceVotersForIdentityResponseV0\x12\xb6\x01\n\x19\x63ontested_resource_voters\x18\x01 \x01(\x0b\x32\x90\x01.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse.GetContestedResourceVotersForIdentityResponseV0.ContestedResourceVotersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x43\n\x17\x43ontestedResourceVoters\x12\x0e\n\x06voters\x18\x01 \x03(\x0c\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x42\x08\n\x06resultB\t\n\x07version\"\xad\x05\n(GetContestedResourceIdentityVotesRequest\x12|\n\x02v0\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest.GetContestedResourceIdentityVotesRequestV0H\x00\x1a\xf7\x03\n*GetContestedResourceIdentityVotesRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12+\n\x05limit\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0forder_ascending\x18\x04 \x01(\x08\x12\xae\x01\n\x1astart_at_vote_poll_id_info\x18\x05 \x01(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest.GetContestedResourceIdentityVotesRequestV0.StartAtVotePollIdInfoH\x00\x88\x01\x01\x12\r\n\x05prove\x18\x06 \x01(\x08\x1a\x61\n\x15StartAtVotePollIdInfo\x12 \n\x18start_at_poll_identifier\x18\x01 \x01(\x0c\x12&\n\x1estart_poll_identifier_included\x18\x02 \x01(\x08\x42\x1d\n\x1b_start_at_vote_poll_id_infoB\t\n\x07version\"\xc8\n\n)GetContestedResourceIdentityVotesResponse\x12~\n\x02v0\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0H\x00\x1a\x8f\t\n+GetContestedResourceIdentityVotesResponseV0\x12\xa1\x01\n\x05votes\x18\x01 \x01(\x0b\x32\x8f\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ContestedResourceIdentityVotesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xf7\x01\n\x1e\x43ontestedResourceIdentityVotes\x12\xba\x01\n!contested_resource_identity_votes\x18\x01 \x03(\x0b\x32\x8e\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ContestedResourceIdentityVote\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x1a\xad\x02\n\x12ResourceVoteChoice\x12\xad\x01\n\x10vote_choice_type\x18\x01 \x01(\x0e\x32\x92\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ResourceVoteChoice.VoteChoiceType\x12\x18\n\x0bidentity_id\x18\x02 \x01(\x0cH\x00\x88\x01\x01\"=\n\x0eVoteChoiceType\x12\x14\n\x10TOWARDS_IDENTITY\x10\x00\x12\x0b\n\x07\x41\x42STAIN\x10\x01\x12\x08\n\x04LOCK\x10\x02\x42\x0e\n\x0c_identity_id\x1a\x95\x02\n\x1d\x43ontestedResourceIdentityVote\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\'\n\x1fserialized_index_storage_values\x18\x03 \x03(\x0c\x12\x99\x01\n\x0bvote_choice\x18\x04 \x01(\x0b\x32\x83\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ResourceVoteChoiceB\x08\n\x06resultB\t\n\x07version\"\xf0\x01\n%GetPrefundedSpecializedBalanceRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceRequest.GetPrefundedSpecializedBalanceRequestV0H\x00\x1a\x44\n\'GetPrefundedSpecializedBalanceRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xed\x02\n&GetPrefundedSpecializedBalanceResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceResponse.GetPrefundedSpecializedBalanceResponseV0H\x00\x1a\xbd\x01\n(GetPrefundedSpecializedBalanceResponseV0\x12\x15\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xd0\x01\n GetTotalCreditsInPlatformRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformRequest.GetTotalCreditsInPlatformRequestV0H\x00\x1a\x33\n\"GetTotalCreditsInPlatformRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xd9\x02\n!GetTotalCreditsInPlatformResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformResponse.GetTotalCreditsInPlatformResponseV0H\x00\x1a\xb8\x01\n#GetTotalCreditsInPlatformResponseV0\x12\x15\n\x07\x63redits\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc4\x01\n\x16GetPathElementsRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetPathElementsRequest.GetPathElementsRequestV0H\x00\x1a\x45\n\x18GetPathElementsRequestV0\x12\x0c\n\x04path\x18\x01 \x03(\x0c\x12\x0c\n\x04keys\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xa3\x03\n\x17GetPathElementsResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetPathElementsResponse.GetPathElementsResponseV0H\x00\x1a\xa0\x02\n\x19GetPathElementsResponseV0\x12i\n\x08\x65lements\x18\x01 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetPathElementsResponse.GetPathElementsResponseV0.ElementsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1c\n\x08\x45lements\x12\x10\n\x08\x65lements\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\x81\x01\n\x10GetStatusRequest\x12L\n\x02v0\x18\x01 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetStatusRequest.GetStatusRequestV0H\x00\x1a\x14\n\x12GetStatusRequestV0B\t\n\x07version\"\xe4\x10\n\x11GetStatusResponse\x12N\n\x02v0\x18\x01 \x01(\x0b\x32@.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0H\x00\x1a\xf3\x0f\n\x13GetStatusResponseV0\x12Y\n\x07version\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version\x12S\n\x04node\x18\x02 \x01(\x0b\x32\x45.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Node\x12U\n\x05\x63hain\x18\x03 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Chain\x12Y\n\x07network\x18\x04 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Network\x12^\n\nstate_sync\x18\x05 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.StateSync\x12S\n\x04time\x18\x06 \x01(\x0b\x32\x45.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Time\x1a\x82\x05\n\x07Version\x12\x63\n\x08software\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Software\x12\x63\n\x08protocol\x18\x02 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol\x1a^\n\x08Software\x12\x0c\n\x04\x64\x61pi\x18\x01 \x01(\t\x12\x12\n\x05\x64rive\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntenderdash\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_driveB\r\n\x0b_tenderdash\x1a\xcc\x02\n\x08Protocol\x12p\n\ntenderdash\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol.Tenderdash\x12\x66\n\x05\x64rive\x18\x02 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol.Drive\x1a(\n\nTenderdash\x12\x0b\n\x03p2p\x18\x01 \x01(\r\x12\r\n\x05\x62lock\x18\x02 \x01(\r\x1a<\n\x05\x44rive\x12\x0e\n\x06latest\x18\x03 \x01(\r\x12\x0f\n\x07\x63urrent\x18\x04 \x01(\r\x12\x12\n\nnext_epoch\x18\x05 \x01(\r\x1a\x7f\n\x04Time\x12\x11\n\x05local\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x05\x62lock\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x12\x18\n\x07genesis\x18\x03 \x01(\x04\x42\x02\x30\x01H\x01\x88\x01\x01\x12\x12\n\x05\x65poch\x18\x04 \x01(\rH\x02\x88\x01\x01\x42\x08\n\x06_blockB\n\n\x08_genesisB\x08\n\x06_epoch\x1a<\n\x04Node\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x18\n\x0bpro_tx_hash\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\x0e\n\x0c_pro_tx_hash\x1a\xb3\x02\n\x05\x43hain\x12\x13\n\x0b\x63\x61tching_up\x18\x01 \x01(\x08\x12\x19\n\x11latest_block_hash\x18\x02 \x01(\x0c\x12\x17\n\x0flatest_app_hash\x18\x03 \x01(\x0c\x12\x1f\n\x13latest_block_height\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x13\x65\x61rliest_block_hash\x18\x05 \x01(\x0c\x12\x19\n\x11\x65\x61rliest_app_hash\x18\x06 \x01(\x0c\x12!\n\x15\x65\x61rliest_block_height\x18\x07 \x01(\x04\x42\x02\x30\x01\x12!\n\x15max_peer_block_height\x18\t \x01(\x04\x42\x02\x30\x01\x12%\n\x18\x63ore_chain_locked_height\x18\n \x01(\rH\x00\x88\x01\x01\x42\x1b\n\x19_core_chain_locked_height\x1a\x43\n\x07Network\x12\x10\n\x08\x63hain_id\x18\x01 \x01(\t\x12\x13\n\x0bpeers_count\x18\x02 \x01(\r\x12\x11\n\tlistening\x18\x03 \x01(\x08\x1a\x85\x02\n\tStateSync\x12\x1d\n\x11total_synced_time\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1a\n\x0eremaining_time\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x17\n\x0ftotal_snapshots\x18\x03 \x01(\r\x12\"\n\x16\x63hunk_process_avg_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x0fsnapshot_height\x18\x05 \x01(\x04\x42\x02\x30\x01\x12!\n\x15snapshot_chunks_count\x18\x06 \x01(\x04\x42\x02\x30\x01\x12\x1d\n\x11\x62\x61\x63kfilled_blocks\x18\x07 \x01(\x04\x42\x02\x30\x01\x12!\n\x15\x62\x61\x63kfill_blocks_total\x18\x08 \x01(\x04\x42\x02\x30\x01\x42\t\n\x07version\"\xb1\x01\n\x1cGetCurrentQuorumsInfoRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoRequest.GetCurrentQuorumsInfoRequestV0H\x00\x1a \n\x1eGetCurrentQuorumsInfoRequestV0B\t\n\x07version\"\xa1\x05\n\x1dGetCurrentQuorumsInfoResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.GetCurrentQuorumsInfoResponseV0H\x00\x1a\x46\n\x0bValidatorV0\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x0f\n\x07node_ip\x18\x02 \x01(\t\x12\x11\n\tis_banned\x18\x03 \x01(\x08\x1a\xaf\x01\n\x0eValidatorSetV0\x12\x13\n\x0bquorum_hash\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ore_height\x18\x02 \x01(\r\x12U\n\x07members\x18\x03 \x03(\x0b\x32\x44.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.ValidatorV0\x12\x1c\n\x14threshold_public_key\x18\x04 \x01(\x0c\x1a\x92\x02\n\x1fGetCurrentQuorumsInfoResponseV0\x12\x15\n\rquorum_hashes\x18\x01 \x03(\x0c\x12\x1b\n\x13\x63urrent_quorum_hash\x18\x02 \x01(\x0c\x12_\n\x0evalidator_sets\x18\x03 \x03(\x0b\x32G.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.ValidatorSetV0\x12\x1b\n\x13last_block_proposer\x18\x04 \x01(\x0c\x12=\n\x08metadata\x18\x05 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\t\n\x07version\"\xf4\x01\n\x1fGetIdentityTokenBalancesRequest\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentityTokenBalancesRequest.GetIdentityTokenBalancesRequestV0H\x00\x1aZ\n!GetIdentityTokenBalancesRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x11\n\ttoken_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xad\x05\n GetIdentityTokenBalancesResponse\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0H\x00\x1a\x8f\x04\n\"GetIdentityTokenBalancesResponseV0\x12\x86\x01\n\x0etoken_balances\x18\x01 \x01(\x0b\x32l.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0.TokenBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aG\n\x11TokenBalanceEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x07\x62\x61lance\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\x9a\x01\n\rTokenBalances\x12\x88\x01\n\x0etoken_balances\x18\x01 \x03(\x0b\x32p.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0.TokenBalanceEntryB\x08\n\x06resultB\t\n\x07version\"\xfc\x01\n!GetIdentitiesTokenBalancesRequest\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesRequest.GetIdentitiesTokenBalancesRequestV0H\x00\x1a\\\n#GetIdentitiesTokenBalancesRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x0cidentity_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xf2\x05\n\"GetIdentitiesTokenBalancesResponse\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0H\x00\x1a\xce\x04\n$GetIdentitiesTokenBalancesResponseV0\x12\x9b\x01\n\x17identity_token_balances\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0.IdentityTokenBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aR\n\x19IdentityTokenBalanceEntry\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x14\n\x07\x62\x61lance\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\xb7\x01\n\x15IdentityTokenBalances\x12\x9d\x01\n\x17identity_token_balances\x18\x01 \x03(\x0b\x32|.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0.IdentityTokenBalanceEntryB\x08\n\x06resultB\t\n\x07version\"\xe8\x01\n\x1cGetIdentityTokenInfosRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetIdentityTokenInfosRequest.GetIdentityTokenInfosRequestV0H\x00\x1aW\n\x1eGetIdentityTokenInfosRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x11\n\ttoken_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\x98\x06\n\x1dGetIdentityTokenInfosResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0H\x00\x1a\x83\x05\n\x1fGetIdentityTokenInfosResponseV0\x12z\n\x0btoken_infos\x18\x01 \x01(\x0b\x32\x63.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a(\n\x16TokenIdentityInfoEntry\x12\x0e\n\x06\x66rozen\x18\x01 \x01(\x08\x1a\xb0\x01\n\x0eTokenInfoEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x82\x01\n\x04info\x18\x02 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenIdentityInfoEntryH\x00\x88\x01\x01\x42\x07\n\x05_info\x1a\x8a\x01\n\nTokenInfos\x12|\n\x0btoken_infos\x18\x01 \x03(\x0b\x32g.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xf0\x01\n\x1eGetIdentitiesTokenInfosRequest\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosRequest.GetIdentitiesTokenInfosRequestV0H\x00\x1aY\n GetIdentitiesTokenInfosRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x0cidentity_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xca\x06\n\x1fGetIdentitiesTokenInfosResponse\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0H\x00\x1a\xaf\x05\n!GetIdentitiesTokenInfosResponseV0\x12\x8f\x01\n\x14identity_token_infos\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.IdentityTokenInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a(\n\x16TokenIdentityInfoEntry\x12\x0e\n\x06\x66rozen\x18\x01 \x01(\x08\x1a\xb7\x01\n\x0eTokenInfoEntry\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x86\x01\n\x04info\x18\x02 \x01(\x0b\x32s.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.TokenIdentityInfoEntryH\x00\x88\x01\x01\x42\x07\n\x05_info\x1a\x97\x01\n\x12IdentityTokenInfos\x12\x80\x01\n\x0btoken_infos\x18\x01 \x03(\x0b\x32k.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.TokenInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xbf\x01\n\x17GetTokenStatusesRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetTokenStatusesRequest.GetTokenStatusesRequestV0H\x00\x1a=\n\x19GetTokenStatusesRequestV0\x12\x11\n\ttoken_ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xe7\x04\n\x18GetTokenStatusesResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0H\x00\x1a\xe1\x03\n\x1aGetTokenStatusesResponseV0\x12v\n\x0etoken_statuses\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0.TokenStatusesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x44\n\x10TokenStatusEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x13\n\x06paused\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\t\n\x07_paused\x1a\x88\x01\n\rTokenStatuses\x12w\n\x0etoken_statuses\x18\x01 \x03(\x0b\x32_.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0.TokenStatusEntryB\x08\n\x06resultB\t\n\x07version\"\xef\x01\n#GetTokenDirectPurchasePricesRequest\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesRequest.GetTokenDirectPurchasePricesRequestV0H\x00\x1aI\n%GetTokenDirectPurchasePricesRequestV0\x12\x11\n\ttoken_ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x8b\t\n$GetTokenDirectPurchasePricesResponse\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0H\x00\x1a\xe1\x07\n&GetTokenDirectPurchasePricesResponseV0\x12\xa9\x01\n\x1ctoken_direct_purchase_prices\x18\x01 \x01(\x0b\x32\x80\x01.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.TokenDirectPurchasePricesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x33\n\x10PriceForQuantity\x12\x10\n\x08quantity\x18\x01 \x01(\x04\x12\r\n\x05price\x18\x02 \x01(\x04\x1a\xa7\x01\n\x0fPricingSchedule\x12\x93\x01\n\x12price_for_quantity\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.PriceForQuantity\x1a\xe4\x01\n\x1dTokenDirectPurchasePriceEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x15\n\x0b\x66ixed_price\x18\x02 \x01(\x04H\x00\x12\x90\x01\n\x0evariable_price\x18\x03 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.PricingScheduleH\x00\x42\x07\n\x05price\x1a\xc8\x01\n\x19TokenDirectPurchasePrices\x12\xaa\x01\n\x1btoken_direct_purchase_price\x18\x01 \x03(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.TokenDirectPurchasePriceEntryB\x08\n\x06resultB\t\n\x07version\"\xce\x01\n\x1bGetTokenContractInfoRequest\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetTokenContractInfoRequest.GetTokenContractInfoRequestV0H\x00\x1a@\n\x1dGetTokenContractInfoRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xfb\x03\n\x1cGetTokenContractInfoResponse\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetTokenContractInfoResponse.GetTokenContractInfoResponseV0H\x00\x1a\xe9\x02\n\x1eGetTokenContractInfoResponseV0\x12|\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32l.org.dash.platform.dapi.v0.GetTokenContractInfoResponse.GetTokenContractInfoResponseV0.TokenContractInfoDataH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aM\n\x15TokenContractInfoData\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17token_contract_position\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xef\x04\n)GetTokenPreProgrammedDistributionsRequest\x12~\n\x02v0\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest.GetTokenPreProgrammedDistributionsRequestV0H\x00\x1a\xb6\x03\n+GetTokenPreProgrammedDistributionsRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x98\x01\n\rstart_at_info\x18\x02 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest.GetTokenPreProgrammedDistributionsRequestV0.StartAtInfoH\x00\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x04 \x01(\x08\x1a\x9a\x01\n\x0bStartAtInfo\x12\x15\n\rstart_time_ms\x18\x01 \x01(\x04\x12\x1c\n\x0fstart_recipient\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12%\n\x18start_recipient_included\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x12\n\x10_start_recipientB\x1b\n\x19_start_recipient_includedB\x10\n\x0e_start_at_infoB\x08\n\x06_limitB\t\n\x07version\"\xec\x07\n*GetTokenPreProgrammedDistributionsResponse\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0H\x00\x1a\xaf\x06\n,GetTokenPreProgrammedDistributionsResponseV0\x12\xa5\x01\n\x13token_distributions\x18\x01 \x01(\x0b\x32\x85\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenDistributionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a>\n\x16TokenDistributionEntry\x12\x14\n\x0crecipient_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x1a\xd4\x01\n\x1bTokenTimedDistributionEntry\x12\x11\n\ttimestamp\x18\x01 \x01(\x04\x12\xa1\x01\n\rdistributions\x18\x02 \x03(\x0b\x32\x89\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenDistributionEntry\x1a\xc3\x01\n\x12TokenDistributions\x12\xac\x01\n\x13token_distributions\x18\x01 \x03(\x0b\x32\x8e\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenTimedDistributionEntryB\x08\n\x06resultB\t\n\x07version\"\x82\x04\n-GetTokenPerpetualDistributionLastClaimRequest\x12\x86\x01\n\x02v0\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest.GetTokenPerpetualDistributionLastClaimRequestV0H\x00\x1aI\n\x11\x43ontractTokenInfo\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17token_contract_position\x18\x02 \x01(\r\x1a\xf1\x01\n/GetTokenPerpetualDistributionLastClaimRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12v\n\rcontract_info\x18\x02 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest.ContractTokenInfoH\x00\x88\x01\x01\x12\x13\n\x0bidentity_id\x18\x04 \x01(\x0c\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x10\n\x0e_contract_infoB\t\n\x07version\"\x93\x05\n.GetTokenPerpetualDistributionLastClaimResponse\x12\x88\x01\n\x02v0\x18\x01 \x01(\x0b\x32z.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse.GetTokenPerpetualDistributionLastClaimResponseV0H\x00\x1a\xca\x03\n0GetTokenPerpetualDistributionLastClaimResponseV0\x12\x9f\x01\n\nlast_claim\x18\x01 \x01(\x0b\x32\x88\x01.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse.GetTokenPerpetualDistributionLastClaimResponseV0.LastClaimInfoH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1ax\n\rLastClaimInfo\x12\x1a\n\x0ctimestamp_ms\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x1a\n\x0c\x62lock_height\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x0f\n\x05\x65poch\x18\x03 \x01(\rH\x00\x12\x13\n\traw_bytes\x18\x04 \x01(\x0cH\x00\x42\t\n\x07paid_atB\x08\n\x06resultB\t\n\x07version\"\xca\x01\n\x1aGetTokenTotalSupplyRequest\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetTokenTotalSupplyRequest.GetTokenTotalSupplyRequestV0H\x00\x1a?\n\x1cGetTokenTotalSupplyRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xaf\x04\n\x1bGetTokenTotalSupplyResponse\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse.GetTokenTotalSupplyResponseV0H\x00\x1a\xa0\x03\n\x1dGetTokenTotalSupplyResponseV0\x12\x88\x01\n\x12token_total_supply\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse.GetTokenTotalSupplyResponseV0.TokenTotalSupplyEntryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1ax\n\x15TokenTotalSupplyEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x30\n(total_aggregated_amount_in_user_accounts\x18\x02 \x01(\x04\x12\x1b\n\x13total_system_amount\x18\x03 \x01(\x04\x42\x08\n\x06resultB\t\n\x07version\"\xd2\x01\n\x13GetGroupInfoRequest\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetGroupInfoRequest.GetGroupInfoRequestV0H\x00\x1a\\\n\x15GetGroupInfoRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xd4\x05\n\x14GetGroupInfoResponse\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0H\x00\x1a\xda\x04\n\x16GetGroupInfoResponseV0\x12\x66\n\ngroup_info\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupInfoH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x04 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x34\n\x10GroupMemberEntry\x12\x11\n\tmember_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\x98\x01\n\x0eGroupInfoEntry\x12h\n\x07members\x18\x01 \x03(\x0b\x32W.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupMemberEntry\x12\x1c\n\x14group_required_power\x18\x02 \x01(\r\x1a\x8a\x01\n\tGroupInfo\x12n\n\ngroup_info\x18\x01 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupInfoEntryH\x00\x88\x01\x01\x42\r\n\x0b_group_infoB\x08\n\x06resultB\t\n\x07version\"\xed\x03\n\x14GetGroupInfosRequest\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetGroupInfosRequest.GetGroupInfosRequestV0H\x00\x1au\n\x1cStartAtGroupContractPosition\x12%\n\x1dstart_group_contract_position\x18\x01 \x01(\r\x12.\n&start_group_contract_position_included\x18\x02 \x01(\x08\x1a\xfc\x01\n\x16GetGroupInfosRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12{\n start_at_group_contract_position\x18\x02 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetGroupInfosRequest.StartAtGroupContractPositionH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x04 \x01(\x08\x42#\n!_start_at_group_contract_positionB\x08\n\x06_countB\t\n\x07version\"\xff\x05\n\x15GetGroupInfosResponse\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0H\x00\x1a\x82\x05\n\x17GetGroupInfosResponseV0\x12j\n\x0bgroup_infos\x18\x01 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x04 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x34\n\x10GroupMemberEntry\x12\x11\n\tmember_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\xc3\x01\n\x16GroupPositionInfoEntry\x12\x1f\n\x17group_contract_position\x18\x01 \x01(\r\x12j\n\x07members\x18\x02 \x03(\x0b\x32Y.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupMemberEntry\x12\x1c\n\x14group_required_power\x18\x03 \x01(\r\x1a\x82\x01\n\nGroupInfos\x12t\n\x0bgroup_infos\x18\x01 \x03(\x0b\x32_.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupPositionInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xbe\x04\n\x16GetGroupActionsRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetGroupActionsRequest.GetGroupActionsRequestV0H\x00\x1aL\n\x0fStartAtActionId\x12\x17\n\x0fstart_action_id\x18\x01 \x01(\x0c\x12 \n\x18start_action_id_included\x18\x02 \x01(\x08\x1a\xc8\x02\n\x18GetGroupActionsRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12N\n\x06status\x18\x03 \x01(\x0e\x32>.org.dash.platform.dapi.v0.GetGroupActionsRequest.ActionStatus\x12\x62\n\x12start_at_action_id\x18\x04 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetGroupActionsRequest.StartAtActionIdH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x06 \x01(\x08\x42\x15\n\x13_start_at_action_idB\x08\n\x06_count\"&\n\x0c\x41\x63tionStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\x42\t\n\x07version\"\xd6\x1e\n\x17GetGroupActionsResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0H\x00\x1a\xd3\x1d\n\x19GetGroupActionsResponseV0\x12r\n\rgroup_actions\x18\x01 \x01(\x0b\x32Y.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a[\n\tMintEvent\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x04\x12\x14\n\x0crecipient_id\x18\x02 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a[\n\tBurnEvent\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x04\x12\x14\n\x0c\x62urn_from_id\x18\x02 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1aJ\n\x0b\x46reezeEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1aL\n\rUnfreezeEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\x66\n\x17\x44\x65stroyFrozenFundsEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\x64\n\x13SharedEncryptedNote\x12\x18\n\x10sender_key_index\x18\x01 \x01(\r\x12\x1b\n\x13recipient_key_index\x18\x02 \x01(\r\x12\x16\n\x0e\x65ncrypted_data\x18\x03 \x01(\x0c\x1a{\n\x15PersonalEncryptedNote\x12!\n\x19root_encryption_key_index\x18\x01 \x01(\r\x12\'\n\x1f\x64\x65rivation_encryption_key_index\x18\x02 \x01(\r\x12\x16\n\x0e\x65ncrypted_data\x18\x03 \x01(\x0c\x1a\xe9\x01\n\x14\x45mergencyActionEvent\x12\x81\x01\n\x0b\x61\x63tion_type\x18\x01 \x01(\x0e\x32l.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.EmergencyActionEvent.ActionType\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\"#\n\nActionType\x12\t\n\x05PAUSE\x10\x00\x12\n\n\x06RESUME\x10\x01\x42\x0e\n\x0c_public_note\x1a\x64\n\x16TokenConfigUpdateEvent\x12 \n\x18token_config_update_item\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\xe6\x03\n\x1eUpdateDirectPurchasePriceEvent\x12\x15\n\x0b\x66ixed_price\x18\x01 \x01(\x04H\x00\x12\x95\x01\n\x0evariable_price\x18\x02 \x01(\x0b\x32{.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEvent.PricingScheduleH\x00\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x01\x88\x01\x01\x1a\x33\n\x10PriceForQuantity\x12\x10\n\x08quantity\x18\x01 \x01(\x04\x12\r\n\x05price\x18\x02 \x01(\x04\x1a\xac\x01\n\x0fPricingSchedule\x12\x98\x01\n\x12price_for_quantity\x18\x01 \x03(\x0b\x32|.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEvent.PriceForQuantityB\x07\n\x05priceB\x0e\n\x0c_public_note\x1a\xfc\x02\n\x10GroupActionEvent\x12n\n\x0btoken_event\x18\x01 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.TokenEventH\x00\x12t\n\x0e\x64ocument_event\x18\x02 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DocumentEventH\x00\x12t\n\x0e\x63ontract_event\x18\x03 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.ContractEventH\x00\x42\x0c\n\nevent_type\x1a\x8b\x01\n\rDocumentEvent\x12r\n\x06\x63reate\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DocumentCreateEventH\x00\x42\x06\n\x04type\x1a/\n\x13\x44ocumentCreateEvent\x12\x18\n\x10\x63reated_document\x18\x01 \x01(\x0c\x1a/\n\x13\x43ontractUpdateEvent\x12\x18\n\x10updated_contract\x18\x01 \x01(\x0c\x1a\x8b\x01\n\rContractEvent\x12r\n\x06update\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.ContractUpdateEventH\x00\x42\x06\n\x04type\x1a\xd1\x07\n\nTokenEvent\x12\x66\n\x04mint\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.MintEventH\x00\x12\x66\n\x04\x62urn\x18\x02 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.BurnEventH\x00\x12j\n\x06\x66reeze\x18\x03 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.FreezeEventH\x00\x12n\n\x08unfreeze\x18\x04 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UnfreezeEventH\x00\x12\x84\x01\n\x14\x64\x65stroy_frozen_funds\x18\x05 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DestroyFrozenFundsEventH\x00\x12}\n\x10\x65mergency_action\x18\x06 \x01(\x0b\x32\x61.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.EmergencyActionEventH\x00\x12\x82\x01\n\x13token_config_update\x18\x07 \x01(\x0b\x32\x63.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.TokenConfigUpdateEventH\x00\x12\x83\x01\n\x0cupdate_price\x18\x08 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEventH\x00\x42\x06\n\x04type\x1a\x93\x01\n\x10GroupActionEntry\x12\x11\n\taction_id\x18\x01 \x01(\x0c\x12l\n\x05\x65vent\x18\x02 \x01(\x0b\x32].org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionEvent\x1a\x84\x01\n\x0cGroupActions\x12t\n\rgroup_actions\x18\x01 \x03(\x0b\x32].org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionEntryB\x08\n\x06resultB\t\n\x07version\"\x88\x03\n\x1cGetGroupActionSignersRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionSignersRequest.GetGroupActionSignersRequestV0H\x00\x1a\xce\x01\n\x1eGetGroupActionSignersRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12T\n\x06status\x18\x03 \x01(\x0e\x32\x44.org.dash.platform.dapi.v0.GetGroupActionSignersRequest.ActionStatus\x12\x11\n\taction_id\x18\x04 \x01(\x0c\x12\r\n\x05prove\x18\x05 \x01(\x08\"&\n\x0c\x41\x63tionStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\x42\t\n\x07version\"\x8b\x05\n\x1dGetGroupActionSignersResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0H\x00\x1a\xf6\x03\n\x1fGetGroupActionSignersResponseV0\x12\x8b\x01\n\x14group_action_signers\x18\x01 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0.GroupActionSignersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x35\n\x11GroupActionSigner\x12\x11\n\tsigner_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\x91\x01\n\x12GroupActionSigners\x12{\n\x07signers\x18\x01 \x03(\x0b\x32j.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0.GroupActionSignerB\x08\n\x06resultB\t\n\x07version\"\xb5\x01\n\x15GetAddressInfoRequest\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetAddressInfoRequest.GetAddressInfoRequestV0H\x00\x1a\x39\n\x17GetAddressInfoRequestV0\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x85\x01\n\x10\x41\x64\x64ressInfoEntry\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12J\n\x11\x62\x61lance_and_nonce\x18\x02 \x01(\x0b\x32*.org.dash.platform.dapi.v0.BalanceAndNonceH\x00\x88\x01\x01\x42\x14\n\x12_balance_and_nonce\"1\n\x0f\x42\x61lanceAndNonce\x12\x0f\n\x07\x62\x61lance\x18\x01 \x01(\x04\x12\r\n\x05nonce\x18\x02 \x01(\r\"_\n\x12\x41\x64\x64ressInfoEntries\x12I\n\x14\x61\x64\x64ress_info_entries\x18\x01 \x03(\x0b\x32+.org.dash.platform.dapi.v0.AddressInfoEntry\"m\n\x14\x41\x64\x64ressBalanceChange\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x19\n\x0bset_balance\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x1c\n\x0e\x61\x64\x64_to_balance\x18\x03 \x01(\x04\x42\x02\x30\x01H\x00\x42\x0b\n\toperation\"x\n\x1a\x42lockAddressBalanceChanges\x12\x18\n\x0c\x62lock_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12@\n\x07\x63hanges\x18\x02 \x03(\x0b\x32/.org.dash.platform.dapi.v0.AddressBalanceChange\"k\n\x1b\x41\x64\x64ressBalanceUpdateEntries\x12L\n\rblock_changes\x18\x01 \x03(\x0b\x32\x35.org.dash.platform.dapi.v0.BlockAddressBalanceChanges\"\xe1\x02\n\x16GetAddressInfoResponse\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetAddressInfoResponse.GetAddressInfoResponseV0H\x00\x1a\xe1\x01\n\x18GetAddressInfoResponseV0\x12I\n\x12\x61\x64\x64ress_info_entry\x18\x01 \x01(\x0b\x32+.org.dash.platform.dapi.v0.AddressInfoEntryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc3\x01\n\x18GetAddressesInfosRequest\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetAddressesInfosRequest.GetAddressesInfosRequestV0H\x00\x1a>\n\x1aGetAddressesInfosRequestV0\x12\x11\n\taddresses\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf1\x02\n\x19GetAddressesInfosResponse\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetAddressesInfosResponse.GetAddressesInfosResponseV0H\x00\x1a\xe8\x01\n\x1bGetAddressesInfosResponseV0\x12M\n\x14\x61\x64\x64ress_info_entries\x18\x01 \x01(\x0b\x32-.org.dash.platform.dapi.v0.AddressInfoEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb5\x01\n\x1dGetAddressesTrunkStateRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetAddressesTrunkStateRequest.GetAddressesTrunkStateRequestV0H\x00\x1a!\n\x1fGetAddressesTrunkStateRequestV0B\t\n\x07version\"\xaa\x02\n\x1eGetAddressesTrunkStateResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetAddressesTrunkStateResponse.GetAddressesTrunkStateResponseV0H\x00\x1a\x92\x01\n GetAddressesTrunkStateResponseV0\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\t\n\x07version\"\xf0\x01\n\x1eGetAddressesBranchStateRequest\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetAddressesBranchStateRequest.GetAddressesBranchStateRequestV0H\x00\x1aY\n GetAddressesBranchStateRequestV0\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\r\n\x05\x64\x65pth\x18\x02 \x01(\r\x12\x19\n\x11\x63heckpoint_height\x18\x03 \x01(\x04\x42\t\n\x07version\"\xd1\x01\n\x1fGetAddressesBranchStateResponse\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetAddressesBranchStateResponse.GetAddressesBranchStateResponseV0H\x00\x1a\x37\n!GetAddressesBranchStateResponseV0\x12\x12\n\nmerk_proof\x18\x02 \x01(\x0c\x42\t\n\x07version\"\x9e\x02\n%GetRecentAddressBalanceChangesRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesRequest.GetRecentAddressBalanceChangesRequestV0H\x00\x1ar\n\'GetRecentAddressBalanceChangesRequestV0\x12\x18\n\x0cstart_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x02 \x01(\x08\x12\x1e\n\x16start_height_exclusive\x18\x03 \x01(\x08\x42\t\n\x07version\"\xb8\x03\n&GetRecentAddressBalanceChangesResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesResponse.GetRecentAddressBalanceChangesResponseV0H\x00\x1a\x88\x02\n(GetRecentAddressBalanceChangesResponseV0\x12`\n\x1e\x61\x64\x64ress_balance_update_entries\x18\x01 \x01(\x0b\x32\x36.org.dash.platform.dapi.v0.AddressBalanceUpdateEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"G\n\x16\x42lockHeightCreditEntry\x12\x18\n\x0c\x62lock_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x13\n\x07\x63redits\x18\x02 \x01(\x04\x42\x02\x30\x01\"\xb0\x01\n\x1d\x43ompactedAddressBalanceChange\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x19\n\x0bset_credits\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12V\n\x19\x61\x64\x64_to_credits_operations\x18\x03 \x01(\x0b\x32\x31.org.dash.platform.dapi.v0.AddToCreditsOperationsH\x00\x42\x0b\n\toperation\"\\\n\x16\x41\x64\x64ToCreditsOperations\x12\x42\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x31.org.dash.platform.dapi.v0.BlockHeightCreditEntry\"\xae\x01\n#CompactedBlockAddressBalanceChanges\x12\x1e\n\x12start_block_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1c\n\x10\x65nd_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12I\n\x07\x63hanges\x18\x03 \x03(\x0b\x32\x38.org.dash.platform.dapi.v0.CompactedAddressBalanceChange\"\x87\x01\n$CompactedAddressBalanceUpdateEntries\x12_\n\x17\x63ompacted_block_changes\x18\x01 \x03(\x0b\x32>.org.dash.platform.dapi.v0.CompactedBlockAddressBalanceChanges\"\xa9\x02\n.GetRecentCompactedAddressBalanceChangesRequest\x12\x88\x01\n\x02v0\x18\x01 \x01(\x0b\x32z.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesRequest.GetRecentCompactedAddressBalanceChangesRequestV0H\x00\x1a\x61\n0GetRecentCompactedAddressBalanceChangesRequestV0\x12\x1e\n\x12start_block_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf0\x03\n/GetRecentCompactedAddressBalanceChangesResponse\x12\x8a\x01\n\x02v0\x18\x01 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesResponse.GetRecentCompactedAddressBalanceChangesResponseV0H\x00\x1a\xa4\x02\n1GetRecentCompactedAddressBalanceChangesResponseV0\x12s\n(compacted_address_balance_update_entries\x18\x01 \x01(\x0b\x32?.org.dash.platform.dapi.v0.CompactedAddressBalanceUpdateEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xf4\x01\n GetShieldedEncryptedNotesRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesRequest.GetShieldedEncryptedNotesRequestV0H\x00\x1aW\n\"GetShieldedEncryptedNotesRequestV0\x12\x13\n\x0bstart_index\x18\x01 \x01(\x04\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xbc\x05\n!GetShieldedEncryptedNotesResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0H\x00\x1a\x9b\x04\n#GetShieldedEncryptedNotesResponseV0\x12\x8a\x01\n\x0f\x65ncrypted_notes\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0.EncryptedNotesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aW\n\rEncryptedNote\x12\x11\n\tnullifier\x18\x01 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x02 \x01(\x0c\x12\x16\n\x0e\x65ncrypted_note\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x1a\x91\x01\n\x0e\x45ncryptedNotes\x12\x7f\n\x07\x65ntries\x18\x01 \x03(\x0b\x32n.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0.EncryptedNoteB\x08\n\x06resultB\t\n\x07version\"\xb4\x01\n\x19GetShieldedAnchorsRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetShieldedAnchorsRequest.GetShieldedAnchorsRequestV0H\x00\x1a,\n\x1bGetShieldedAnchorsRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xb1\x03\n\x1aGetShieldedAnchorsResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse.GetShieldedAnchorsResponseV0H\x00\x1a\xa5\x02\n\x1cGetShieldedAnchorsResponseV0\x12m\n\x07\x61nchors\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse.GetShieldedAnchorsResponseV0.AnchorsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1a\n\x07\x41nchors\x12\x0f\n\x07\x61nchors\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xd8\x01\n\"GetMostRecentShieldedAnchorRequest\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorRequest.GetMostRecentShieldedAnchorRequestV0H\x00\x1a\x35\n$GetMostRecentShieldedAnchorRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xdc\x02\n#GetMostRecentShieldedAnchorResponse\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorResponse.GetMostRecentShieldedAnchorResponseV0H\x00\x1a\xb5\x01\n%GetMostRecentShieldedAnchorResponseV0\x12\x10\n\x06\x61nchor\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbc\x01\n\x1bGetShieldedPoolStateRequest\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetShieldedPoolStateRequest.GetShieldedPoolStateRequestV0H\x00\x1a.\n\x1dGetShieldedPoolStateRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xcb\x02\n\x1cGetShieldedPoolStateResponse\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedPoolStateResponse.GetShieldedPoolStateResponseV0H\x00\x1a\xb9\x01\n\x1eGetShieldedPoolStateResponseV0\x12\x1b\n\rtotal_balance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc0\x01\n\x1cGetShieldedNotesCountRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedNotesCountRequest.GetShieldedNotesCountRequestV0H\x00\x1a/\n\x1eGetShieldedNotesCountRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xd3\x02\n\x1dGetShieldedNotesCountResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetShieldedNotesCountResponse.GetShieldedNotesCountResponseV0H\x00\x1a\xbe\x01\n\x1fGetShieldedNotesCountResponseV0\x12\x1f\n\x11total_notes_count\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xd4\x01\n\x1cGetShieldedNullifiersRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedNullifiersRequest.GetShieldedNullifiersRequestV0H\x00\x1a\x43\n\x1eGetShieldedNullifiersRequestV0\x12\x12\n\nnullifiers\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x86\x05\n\x1dGetShieldedNullifiersResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0H\x00\x1a\xf1\x03\n\x1fGetShieldedNullifiersResponseV0\x12\x88\x01\n\x12nullifier_statuses\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0.NullifierStatusesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x36\n\x0fNullifierStatus\x12\x11\n\tnullifier\x18\x01 \x01(\x0c\x12\x10\n\x08is_spent\x18\x02 \x01(\x08\x1a\x8e\x01\n\x11NullifierStatuses\x12y\n\x07\x65ntries\x18\x01 \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0.NullifierStatusB\x08\n\x06resultB\t\n\x07version*Z\n\nKeyPurpose\x12\x12\n\x0e\x41UTHENTICATION\x10\x00\x12\x0e\n\nENCRYPTION\x10\x01\x12\x0e\n\nDECRYPTION\x10\x02\x12\x0c\n\x08TRANSFER\x10\x03\x12\n\n\x06VOTING\x10\x05\x32\xce\x44\n\x08Platform\x12\x93\x01\n\x18\x62roadcastStateTransition\x12:.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest\x1a;.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse\x12l\n\x0bgetIdentity\x12-.org.dash.platform.dapi.v0.GetIdentityRequest\x1a..org.dash.platform.dapi.v0.GetIdentityResponse\x12x\n\x0fgetIdentityKeys\x12\x31.org.dash.platform.dapi.v0.GetIdentityKeysRequest\x1a\x32.org.dash.platform.dapi.v0.GetIdentityKeysResponse\x12\x96\x01\n\x19getIdentitiesContractKeys\x12;.org.dash.platform.dapi.v0.GetIdentitiesContractKeysRequest\x1a<.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse\x12{\n\x10getIdentityNonce\x12\x32.org.dash.platform.dapi.v0.GetIdentityNonceRequest\x1a\x33.org.dash.platform.dapi.v0.GetIdentityNonceResponse\x12\x93\x01\n\x18getIdentityContractNonce\x12:.org.dash.platform.dapi.v0.GetIdentityContractNonceRequest\x1a;.org.dash.platform.dapi.v0.GetIdentityContractNonceResponse\x12\x81\x01\n\x12getIdentityBalance\x12\x34.org.dash.platform.dapi.v0.GetIdentityBalanceRequest\x1a\x35.org.dash.platform.dapi.v0.GetIdentityBalanceResponse\x12\x8a\x01\n\x15getIdentitiesBalances\x12\x37.org.dash.platform.dapi.v0.GetIdentitiesBalancesRequest\x1a\x38.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse\x12\xa2\x01\n\x1dgetIdentityBalanceAndRevision\x12?.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionRequest\x1a@.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse\x12\xaf\x01\n#getEvonodesProposedEpochBlocksByIds\x12\x45.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByIdsRequest\x1a\x41.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse\x12\xb3\x01\n%getEvonodesProposedEpochBlocksByRange\x12G.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByRangeRequest\x1a\x41.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse\x12x\n\x0fgetDataContract\x12\x31.org.dash.platform.dapi.v0.GetDataContractRequest\x1a\x32.org.dash.platform.dapi.v0.GetDataContractResponse\x12\x8d\x01\n\x16getDataContractHistory\x12\x38.org.dash.platform.dapi.v0.GetDataContractHistoryRequest\x1a\x39.org.dash.platform.dapi.v0.GetDataContractHistoryResponse\x12{\n\x10getDataContracts\x12\x32.org.dash.platform.dapi.v0.GetDataContractsRequest\x1a\x33.org.dash.platform.dapi.v0.GetDataContractsResponse\x12\x81\x01\n\x12getDocumentHistory\x12\x34.org.dash.platform.dapi.v0.GetDocumentHistoryRequest\x1a\x35.org.dash.platform.dapi.v0.GetDocumentHistoryResponse\x12o\n\x0cgetDocuments\x12..org.dash.platform.dapi.v0.GetDocumentsRequest\x1a/.org.dash.platform.dapi.v0.GetDocumentsResponse\x12\x99\x01\n\x1agetIdentityByPublicKeyHash\x12<.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashRequest\x1a=.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashResponse\x12\xb4\x01\n#getIdentityByNonUniquePublicKeyHash\x12\x45.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashRequest\x1a\x46.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse\x12\x9f\x01\n\x1cwaitForStateTransitionResult\x12>.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest\x1a?.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse\x12\x81\x01\n\x12getConsensusParams\x12\x34.org.dash.platform.dapi.v0.GetConsensusParamsRequest\x1a\x35.org.dash.platform.dapi.v0.GetConsensusParamsResponse\x12\xa5\x01\n\x1egetProtocolVersionUpgradeState\x12@.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateRequest\x1a\x41.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse\x12\xb4\x01\n#getProtocolVersionUpgradeVoteStatus\x12\x45.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusRequest\x1a\x46.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse\x12r\n\rgetEpochsInfo\x12/.org.dash.platform.dapi.v0.GetEpochsInfoRequest\x1a\x30.org.dash.platform.dapi.v0.GetEpochsInfoResponse\x12\x8d\x01\n\x16getFinalizedEpochInfos\x12\x38.org.dash.platform.dapi.v0.GetFinalizedEpochInfosRequest\x1a\x39.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse\x12\x8a\x01\n\x15getContestedResources\x12\x37.org.dash.platform.dapi.v0.GetContestedResourcesRequest\x1a\x38.org.dash.platform.dapi.v0.GetContestedResourcesResponse\x12\xa2\x01\n\x1dgetContestedResourceVoteState\x12?.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest\x1a@.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse\x12\xba\x01\n%getContestedResourceVotersForIdentity\x12G.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest\x1aH.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse\x12\xae\x01\n!getContestedResourceIdentityVotes\x12\x43.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest\x1a\x44.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse\x12\x8a\x01\n\x15getVotePollsByEndDate\x12\x37.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest\x1a\x38.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse\x12\xa5\x01\n\x1egetPrefundedSpecializedBalance\x12@.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceRequest\x1a\x41.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceResponse\x12\x96\x01\n\x19getTotalCreditsInPlatform\x12;.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformRequest\x1a<.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformResponse\x12x\n\x0fgetPathElements\x12\x31.org.dash.platform.dapi.v0.GetPathElementsRequest\x1a\x32.org.dash.platform.dapi.v0.GetPathElementsResponse\x12\x66\n\tgetStatus\x12+.org.dash.platform.dapi.v0.GetStatusRequest\x1a,.org.dash.platform.dapi.v0.GetStatusResponse\x12\x8a\x01\n\x15getCurrentQuorumsInfo\x12\x37.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoRequest\x1a\x38.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse\x12\x93\x01\n\x18getIdentityTokenBalances\x12:.org.dash.platform.dapi.v0.GetIdentityTokenBalancesRequest\x1a;.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse\x12\x99\x01\n\x1agetIdentitiesTokenBalances\x12<.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesRequest\x1a=.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse\x12\x8a\x01\n\x15getIdentityTokenInfos\x12\x37.org.dash.platform.dapi.v0.GetIdentityTokenInfosRequest\x1a\x38.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse\x12\x90\x01\n\x17getIdentitiesTokenInfos\x12\x39.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosRequest\x1a:.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse\x12{\n\x10getTokenStatuses\x12\x32.org.dash.platform.dapi.v0.GetTokenStatusesRequest\x1a\x33.org.dash.platform.dapi.v0.GetTokenStatusesResponse\x12\x9f\x01\n\x1cgetTokenDirectPurchasePrices\x12>.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesRequest\x1a?.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse\x12\x87\x01\n\x14getTokenContractInfo\x12\x36.org.dash.platform.dapi.v0.GetTokenContractInfoRequest\x1a\x37.org.dash.platform.dapi.v0.GetTokenContractInfoResponse\x12\xb1\x01\n\"getTokenPreProgrammedDistributions\x12\x44.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest\x1a\x45.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse\x12\xbd\x01\n&getTokenPerpetualDistributionLastClaim\x12H.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest\x1aI.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse\x12\x84\x01\n\x13getTokenTotalSupply\x12\x35.org.dash.platform.dapi.v0.GetTokenTotalSupplyRequest\x1a\x36.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse\x12o\n\x0cgetGroupInfo\x12..org.dash.platform.dapi.v0.GetGroupInfoRequest\x1a/.org.dash.platform.dapi.v0.GetGroupInfoResponse\x12r\n\rgetGroupInfos\x12/.org.dash.platform.dapi.v0.GetGroupInfosRequest\x1a\x30.org.dash.platform.dapi.v0.GetGroupInfosResponse\x12x\n\x0fgetGroupActions\x12\x31.org.dash.platform.dapi.v0.GetGroupActionsRequest\x1a\x32.org.dash.platform.dapi.v0.GetGroupActionsResponse\x12\x8a\x01\n\x15getGroupActionSigners\x12\x37.org.dash.platform.dapi.v0.GetGroupActionSignersRequest\x1a\x38.org.dash.platform.dapi.v0.GetGroupActionSignersResponse\x12u\n\x0egetAddressInfo\x12\x30.org.dash.platform.dapi.v0.GetAddressInfoRequest\x1a\x31.org.dash.platform.dapi.v0.GetAddressInfoResponse\x12~\n\x11getAddressesInfos\x12\x33.org.dash.platform.dapi.v0.GetAddressesInfosRequest\x1a\x34.org.dash.platform.dapi.v0.GetAddressesInfosResponse\x12\x8d\x01\n\x16getAddressesTrunkState\x12\x38.org.dash.platform.dapi.v0.GetAddressesTrunkStateRequest\x1a\x39.org.dash.platform.dapi.v0.GetAddressesTrunkStateResponse\x12\x90\x01\n\x17getAddressesBranchState\x12\x39.org.dash.platform.dapi.v0.GetAddressesBranchStateRequest\x1a:.org.dash.platform.dapi.v0.GetAddressesBranchStateResponse\x12\xa5\x01\n\x1egetRecentAddressBalanceChanges\x12@.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesRequest\x1a\x41.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesResponse\x12\xc0\x01\n\'getRecentCompactedAddressBalanceChanges\x12I.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesRequest\x1aJ.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesResponse\x12\x96\x01\n\x19getShieldedEncryptedNotes\x12;.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesRequest\x1a<.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse\x12\x81\x01\n\x12getShieldedAnchors\x12\x34.org.dash.platform.dapi.v0.GetShieldedAnchorsRequest\x1a\x35.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse\x12\x9c\x01\n\x1bgetMostRecentShieldedAnchor\x12=.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorRequest\x1a>.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorResponse\x12\x87\x01\n\x14getShieldedPoolState\x12\x36.org.dash.platform.dapi.v0.GetShieldedPoolStateRequest\x1a\x37.org.dash.platform.dapi.v0.GetShieldedPoolStateResponse\x12\x8a\x01\n\x15getShieldedNotesCount\x12\x37.org.dash.platform.dapi.v0.GetShieldedNotesCountRequest\x1a\x38.org.dash.platform.dapi.v0.GetShieldedNotesCountResponse\x12\x8a\x01\n\x15getShieldedNullifiers\x12\x37.org.dash.platform.dapi.v0.GetShieldedNullifiersRequest\x1a\x38.org.dash.platform.dapi.v0.GetShieldedNullifiersResponseb\x06proto3' + serialized_pb=b'\n\x0eplatform.proto\x12\x19org.dash.platform.dapi.v0\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x81\x01\n\x05Proof\x12\x15\n\rgrovedb_proof\x18\x01 \x01(\x0c\x12\x13\n\x0bquorum_hash\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\r\n\x05round\x18\x04 \x01(\r\x12\x15\n\rblock_id_hash\x18\x05 \x01(\x0c\x12\x13\n\x0bquorum_type\x18\x06 \x01(\r\"\x98\x01\n\x10ResponseMetadata\x12\x12\n\x06height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12 \n\x18\x63ore_chain_locked_height\x18\x02 \x01(\r\x12\r\n\x05\x65poch\x18\x03 \x01(\r\x12\x13\n\x07time_ms\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x18\n\x10protocol_version\x18\x05 \x01(\r\x12\x10\n\x08\x63hain_id\x18\x06 \x01(\t\"L\n\x1dStateTransitionBroadcastError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\";\n\x1f\x42roadcastStateTransitionRequest\x12\x18\n\x10state_transition\x18\x01 \x01(\x0c\"\"\n BroadcastStateTransitionResponse\"\xa4\x01\n\x12GetIdentityRequest\x12P\n\x02v0\x18\x01 \x01(\x0b\x32\x42.org.dash.platform.dapi.v0.GetIdentityRequest.GetIdentityRequestV0H\x00\x1a\x31\n\x14GetIdentityRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xc1\x01\n\x17GetIdentityNonceRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetIdentityNonceRequest.GetIdentityNonceRequestV0H\x00\x1a?\n\x19GetIdentityNonceRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf6\x01\n\x1fGetIdentityContractNonceRequest\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentityContractNonceRequest.GetIdentityContractNonceRequestV0H\x00\x1a\\\n!GetIdentityContractNonceRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ontract_id\x18\x02 \x01(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xc0\x01\n\x19GetIdentityBalanceRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetIdentityBalanceRequest.GetIdentityBalanceRequestV0H\x00\x1a\x38\n\x1bGetIdentityBalanceRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xec\x01\n$GetIdentityBalanceAndRevisionRequest\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionRequest.GetIdentityBalanceAndRevisionRequestV0H\x00\x1a\x43\n&GetIdentityBalanceAndRevisionRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9e\x02\n\x13GetIdentityResponse\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetIdentityResponse.GetIdentityResponseV0H\x00\x1a\xa7\x01\n\x15GetIdentityResponseV0\x12\x12\n\x08identity\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbc\x02\n\x18GetIdentityNonceResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetIdentityNonceResponse.GetIdentityNonceResponseV0H\x00\x1a\xb6\x01\n\x1aGetIdentityNonceResponseV0\x12\x1c\n\x0eidentity_nonce\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xe5\x02\n GetIdentityContractNonceResponse\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentityContractNonceResponse.GetIdentityContractNonceResponseV0H\x00\x1a\xc7\x01\n\"GetIdentityContractNonceResponseV0\x12%\n\x17identity_contract_nonce\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbd\x02\n\x1aGetIdentityBalanceResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetIdentityBalanceResponse.GetIdentityBalanceResponseV0H\x00\x1a\xb1\x01\n\x1cGetIdentityBalanceResponseV0\x12\x15\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb1\x04\n%GetIdentityBalanceAndRevisionResponse\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse.GetIdentityBalanceAndRevisionResponseV0H\x00\x1a\x84\x03\n\'GetIdentityBalanceAndRevisionResponseV0\x12\x9b\x01\n\x14\x62\x61lance_and_revision\x18\x01 \x01(\x0b\x32{.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse.GetIdentityBalanceAndRevisionResponseV0.BalanceAndRevisionH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a?\n\x12\x42\x61lanceAndRevision\x12\x13\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x14\n\x08revision\x18\x02 \x01(\x04\x42\x02\x30\x01\x42\x08\n\x06resultB\t\n\x07version\"\xd1\x01\n\x0eKeyRequestType\x12\x36\n\x08\x61ll_keys\x18\x01 \x01(\x0b\x32\".org.dash.platform.dapi.v0.AllKeysH\x00\x12@\n\rspecific_keys\x18\x02 \x01(\x0b\x32\'.org.dash.platform.dapi.v0.SpecificKeysH\x00\x12:\n\nsearch_key\x18\x03 \x01(\x0b\x32$.org.dash.platform.dapi.v0.SearchKeyH\x00\x42\t\n\x07request\"\t\n\x07\x41llKeys\"\x1f\n\x0cSpecificKeys\x12\x0f\n\x07key_ids\x18\x01 \x03(\r\"\xb6\x01\n\tSearchKey\x12I\n\x0bpurpose_map\x18\x01 \x03(\x0b\x32\x34.org.dash.platform.dapi.v0.SearchKey.PurposeMapEntry\x1a^\n\x0fPurposeMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.org.dash.platform.dapi.v0.SecurityLevelMap:\x02\x38\x01\"\xbf\x02\n\x10SecurityLevelMap\x12]\n\x12security_level_map\x18\x01 \x03(\x0b\x32\x41.org.dash.platform.dapi.v0.SecurityLevelMap.SecurityLevelMapEntry\x1aw\n\x15SecurityLevelMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12M\n\x05value\x18\x02 \x01(\x0e\x32>.org.dash.platform.dapi.v0.SecurityLevelMap.KeyKindRequestType:\x02\x38\x01\"S\n\x12KeyKindRequestType\x12\x1f\n\x1b\x43URRENT_KEY_OF_KIND_REQUEST\x10\x00\x12\x1c\n\x18\x41LL_KEYS_OF_KIND_REQUEST\x10\x01\"\xda\x02\n\x16GetIdentityKeysRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetIdentityKeysRequest.GetIdentityKeysRequestV0H\x00\x1a\xda\x01\n\x18GetIdentityKeysRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12?\n\x0crequest_type\x18\x02 \x01(\x0b\x32).org.dash.platform.dapi.v0.KeyRequestType\x12+\n\x05limit\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\x99\x03\n\x17GetIdentityKeysResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetIdentityKeysResponse.GetIdentityKeysResponseV0H\x00\x1a\x96\x02\n\x19GetIdentityKeysResponseV0\x12\x61\n\x04keys\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetIdentityKeysResponse.GetIdentityKeysResponseV0.KeysH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1a\n\x04Keys\x12\x12\n\nkeys_bytes\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xef\x02\n GetIdentitiesContractKeysRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentitiesContractKeysRequest.GetIdentitiesContractKeysRequestV0H\x00\x1a\xd1\x01\n\"GetIdentitiesContractKeysRequestV0\x12\x16\n\x0eidentities_ids\x18\x01 \x03(\x0c\x12\x13\n\x0b\x63ontract_id\x18\x02 \x01(\x0c\x12\x1f\n\x12\x64ocument_type_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x37\n\x08purposes\x18\x04 \x03(\x0e\x32%.org.dash.platform.dapi.v0.KeyPurpose\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x15\n\x13_document_type_nameB\t\n\x07version\"\xdf\x06\n!GetIdentitiesContractKeysResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0H\x00\x1a\xbe\x05\n#GetIdentitiesContractKeysResponseV0\x12\x8a\x01\n\x0fidentities_keys\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.IdentitiesKeysH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aY\n\x0bPurposeKeys\x12\x36\n\x07purpose\x18\x01 \x01(\x0e\x32%.org.dash.platform.dapi.v0.KeyPurpose\x12\x12\n\nkeys_bytes\x18\x02 \x03(\x0c\x1a\x9f\x01\n\x0cIdentityKeys\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12z\n\x04keys\x18\x02 \x03(\x0b\x32l.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.PurposeKeys\x1a\x90\x01\n\x0eIdentitiesKeys\x12~\n\x07\x65ntries\x18\x01 \x03(\x0b\x32m.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse.GetIdentitiesContractKeysResponseV0.IdentityKeysB\x08\n\x06resultB\t\n\x07version\"\xa4\x02\n*GetEvonodesProposedEpochBlocksByIdsRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByIdsRequest.GetEvonodesProposedEpochBlocksByIdsRequestV0H\x00\x1ah\n,GetEvonodesProposedEpochBlocksByIdsRequestV0\x12\x12\n\x05\x65poch\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x0b\n\x03ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\x08\n\x06_epochB\t\n\x07version\"\x92\x06\n&GetEvonodesProposedEpochBlocksResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0H\x00\x1a\xe2\x04\n(GetEvonodesProposedEpochBlocksResponseV0\x12\xb1\x01\n#evonodes_proposed_block_counts_info\x18\x01 \x01(\x0b\x32\x81\x01.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0.EvonodesProposedBlocksH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a?\n\x15\x45vonodeProposedBlocks\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x02 \x01(\x04\x42\x02\x30\x01\x1a\xc4\x01\n\x16\x45vonodesProposedBlocks\x12\xa9\x01\n\x1e\x65vonodes_proposed_block_counts\x18\x01 \x03(\x0b\x32\x80\x01.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse.GetEvonodesProposedEpochBlocksResponseV0.EvonodeProposedBlocksB\x08\n\x06resultB\t\n\x07version\"\xf2\x02\n,GetEvonodesProposedEpochBlocksByRangeRequest\x12\x84\x01\n\x02v0\x18\x01 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByRangeRequest.GetEvonodesProposedEpochBlocksByRangeRequestV0H\x00\x1a\xaf\x01\n.GetEvonodesProposedEpochBlocksByRangeRequestV0\x12\x12\n\x05\x65poch\x18\x01 \x01(\rH\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x02 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x0bstart_after\x18\x03 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x04 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x07\n\x05startB\x08\n\x06_epochB\x08\n\x06_limitB\t\n\x07version\"\xcd\x01\n\x1cGetIdentitiesBalancesRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetIdentitiesBalancesRequest.GetIdentitiesBalancesRequestV0H\x00\x1a<\n\x1eGetIdentitiesBalancesRequestV0\x12\x0b\n\x03ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9f\x05\n\x1dGetIdentitiesBalancesResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0H\x00\x1a\x8a\x04\n\x1fGetIdentitiesBalancesResponseV0\x12\x8a\x01\n\x13identities_balances\x18\x01 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0.IdentitiesBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aL\n\x0fIdentityBalance\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x18\n\x07\x62\x61lance\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\x8f\x01\n\x12IdentitiesBalances\x12y\n\x07\x65ntries\x18\x01 \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse.GetIdentitiesBalancesResponseV0.IdentityBalanceB\x08\n\x06resultB\t\n\x07version\"\xb4\x01\n\x16GetDataContractRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetDataContractRequest.GetDataContractRequestV0H\x00\x1a\x35\n\x18GetDataContractRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xb3\x02\n\x17GetDataContractResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetDataContractResponse.GetDataContractResponseV0H\x00\x1a\xb0\x01\n\x19GetDataContractResponseV0\x12\x17\n\rdata_contract\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb9\x01\n\x17GetDataContractsRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetDataContractsRequest.GetDataContractsRequestV0H\x00\x1a\x37\n\x19GetDataContractsRequestV0\x12\x0b\n\x03ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xcf\x04\n\x18GetDataContractsResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetDataContractsResponse.GetDataContractsResponseV0H\x00\x1a[\n\x11\x44\x61taContractEntry\x12\x12\n\nidentifier\x18\x01 \x01(\x0c\x12\x32\n\rdata_contract\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.BytesValue\x1au\n\rDataContracts\x12\x64\n\x15\x64\x61ta_contract_entries\x18\x01 \x03(\x0b\x32\x45.org.dash.platform.dapi.v0.GetDataContractsResponse.DataContractEntry\x1a\xf5\x01\n\x1aGetDataContractsResponseV0\x12[\n\x0e\x64\x61ta_contracts\x18\x01 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDataContractsResponse.DataContractsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc5\x02\n\x1dGetDataContractHistoryRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetDataContractHistoryRequest.GetDataContractHistoryRequestV0H\x00\x1a\xb0\x01\n\x1fGetDataContractHistoryRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12+\n\x05limit\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0bstart_at_ms\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\xb2\x05\n\x1eGetDataContractHistoryResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0H\x00\x1a\x9a\x04\n GetDataContractHistoryResponseV0\x12\x8f\x01\n\x15\x64\x61ta_contract_history\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0.DataContractHistoryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a;\n\x18\x44\x61taContractHistoryEntry\x12\x10\n\x04\x64\x61te\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05value\x18\x02 \x01(\x0c\x1a\xaa\x01\n\x13\x44\x61taContractHistory\x12\x92\x01\n\x15\x64\x61ta_contract_entries\x18\x01 \x03(\x0b\x32s.org.dash.platform.dapi.v0.GetDataContractHistoryResponse.GetDataContractHistoryResponseV0.DataContractHistoryEntryB\x08\n\x06resultB\t\n\x07version\"\xf1\x15\n\x13GetDocumentsRequest\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV0H\x00\x12R\n\x02v1\x18\x02 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1H\x00\x1a\xfe\x02\n\x12\x44ocumentFieldValue\x12\x14\n\nbool_value\x18\x01 \x01(\x08H\x00\x12\x19\n\x0bint64_value\x18\x02 \x01(\x12\x42\x02\x30\x01H\x00\x12\x1a\n\x0cuint64_value\x18\x03 \x01(\x04\x42\x02\x30\x01H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04text\x18\x05 \x01(\tH\x00\x12\x15\n\x0b\x62ytes_value\x18\x06 \x01(\x0cH\x00\x12[\n\x04list\x18\x07 \x01(\x0b\x32K.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValue.ValueListH\x00\x12\x14\n\nnull_value\x18\x08 \x01(\x08H\x00\x1a^\n\tValueList\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValueB\t\n\x07variant\x1a\xbe\x01\n\x0bWhereClause\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12N\n\x08operator\x18\x02 \x01(\x0e\x32<.org.dash.platform.dapi.v0.GetDocumentsRequest.WhereOperator\x12P\n\x05value\x18\x03 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValue\x1a\xa4\x01\n\x0fHavingAggregate\x12Y\n\x08\x66unction\x18\x01 \x01(\x0e\x32G.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregate.Function\x12\r\n\x05\x66ield\x18\x02 \x01(\t\"\'\n\x08\x46unction\x12\t\n\x05\x43OUNT\x10\x00\x12\x07\n\x03SUM\x10\x01\x12\x07\n\x03\x41VG\x10\x02\x1a\xf9\x03\n\x0cHavingClause\x12Q\n\taggregate\x18\x01 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregate\x12V\n\x08operator\x18\x02 \x01(\x0e\x32\x44.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingClause.Operator\x12R\n\x05value\x18\x03 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetDocumentsRequest.DocumentFieldValueH\x00\"\xe0\x01\n\x08Operator\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x1a\n\x16GREATER_THAN_OR_EQUALS\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x17\n\x13LESS_THAN_OR_EQUALS\x10\x05\x12\x0b\n\x07\x42\x45TWEEN\x10\x06\x12\x1a\n\x16\x42\x45TWEEN_EXCLUDE_BOUNDS\x10\x07\x12\x18\n\x14\x42\x45TWEEN_EXCLUDE_LEFT\x10\x08\x12\x19\n\x15\x42\x45TWEEN_EXCLUDE_RIGHT\x10\t\x12\x06\n\x02IN\x10\nB\x07\n\x05right\x1a\x90\x01\n\x0bOrderClause\x12\x0f\n\x05\x66ield\x18\x01 \x01(\tH\x00\x12S\n\taggregate\x18\x03 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingAggregateH\x00\x12\x11\n\tascending\x18\x02 \x01(\x08\x42\x08\n\x06target\x1a\xbb\x01\n\x15GetDocumentsRequestV0\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x15\n\rdocument_type\x18\x02 \x01(\t\x12\r\n\x05where\x18\x03 \x01(\x0c\x12\x10\n\x08order_by\x18\x04 \x01(\x0c\x12\r\n\x05limit\x18\x05 \x01(\r\x12\x15\n\x0bstart_after\x18\x06 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x07 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x08 \x01(\x08\x42\x07\n\x05start\x1a\xf3\x05\n\x15GetDocumentsRequestV1\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x15\n\rdocument_type\x18\x02 \x01(\t\x12Q\n\rwhere_clauses\x18\x03 \x03(\x0b\x32:.org.dash.platform.dapi.v0.GetDocumentsRequest.WhereClause\x12L\n\x08order_by\x18\x04 \x03(\x0b\x32:.org.dash.platform.dapi.v0.GetDocumentsRequest.OrderClause\x12\x12\n\x05limit\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x0bstart_after\x18\x06 \x01(\x0cH\x00\x12\x12\n\x08start_at\x18\x07 \x01(\x0cH\x00\x12\r\n\x05prove\x18\x08 \x01(\x08\x12\\\n\x07selects\x18\t \x03(\x0b\x32K.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1.Select\x12\x10\n\x08group_by\x18\n \x03(\t\x12K\n\x06having\x18\x0b \x03(\x0b\x32;.org.dash.platform.dapi.v0.GetDocumentsRequest.HavingClause\x12\x13\n\x06offset\x18\x0c \x01(\rH\x02\x88\x01\x01\x1a\xc9\x01\n\x06Select\x12\x66\n\x08\x66unction\x18\x01 \x01(\x0e\x32T.org.dash.platform.dapi.v0.GetDocumentsRequest.GetDocumentsRequestV1.Select.Function\x12\r\n\x05\x66ield\x18\x02 \x01(\t\"H\n\x08\x46unction\x12\r\n\tDOCUMENTS\x10\x00\x12\t\n\x05\x43OUNT\x10\x01\x12\x07\n\x03SUM\x10\x02\x12\x07\n\x03\x41VG\x10\x03\x12\x07\n\x03MIN\x10\x04\x12\x07\n\x03MAX\x10\x05\x42\x07\n\x05startB\x08\n\x06_limitB\t\n\x07_offset\"\xfa\x01\n\rWhereOperator\x12\t\n\x05\x45QUAL\x10\x00\x12\x10\n\x0cGREATER_THAN\x10\x01\x12\x1a\n\x16GREATER_THAN_OR_EQUALS\x10\x02\x12\r\n\tLESS_THAN\x10\x03\x12\x17\n\x13LESS_THAN_OR_EQUALS\x10\x04\x12\x0b\n\x07\x42\x45TWEEN\x10\x05\x12\x1a\n\x16\x42\x45TWEEN_EXCLUDE_BOUNDS\x10\x06\x12\x18\n\x14\x42\x45TWEEN_EXCLUDE_LEFT\x10\x07\x12\x19\n\x15\x42\x45TWEEN_EXCLUDE_RIGHT\x10\x08\x12\x06\n\x02IN\x10\t\x12\x0f\n\x0bSTARTS_WITH\x10\n\x12\x11\n\rIN_TIME_RANGE\x10\x0b\x42\t\n\x07version\"\x87\x16\n\x14GetDocumentsResponse\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV0H\x00\x12T\n\x02v1\x18\x02 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1H\x00\x1a\x9b\x02\n\x16GetDocumentsResponseV0\x12\x65\n\tdocuments\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV0.DocumentsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1e\n\tDocuments\x12\x11\n\tdocuments\x18\x01 \x03(\x0c\x42\x08\n\x06result\x1a\x99\x12\n\x16GetDocumentsResponseV1\x12\x61\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.ResultDataH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1e\n\tDocuments\x12\x11\n\tdocuments\x18\x01 \x03(\x0c\x1aL\n\nCountEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x03 \x01(\x04\x42\x02\x30\x01\x42\t\n\x07_in_key\x1ar\n\x0c\x43ountEntries\x12\x62\n\x07\x65ntries\x18\x01 \x03(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountEntry\x1a\xa0\x01\n\x0c\x43ountResults\x12\x1d\n\x0f\x61ggregate_count\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x66\n\x07\x65ntries\x18\x02 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountEntriesH\x00\x42\t\n\x07variant\x1aH\n\x08SumEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x0f\n\x03sum\x18\x03 \x01(\x12\x42\x02\x30\x01\x42\t\n\x07_in_key\x1an\n\nSumEntries\x12`\n\x07\x65ntries\x18\x01 \x03(\x0b\x32O.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumEntry\x1a\x9a\x01\n\nSumResults\x12\x1b\n\raggregate_sum\x18\x01 \x01(\x12\x42\x02\x30\x01H\x00\x12\x64\n\x07\x65ntries\x18\x02 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumEntriesH\x00\x42\t\n\x07variant\x1a_\n\x0c\x41verageEntry\x12\x13\n\x06in_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x11\n\x05\x63ount\x18\x03 \x01(\x04\x42\x02\x30\x01\x12\x0f\n\x03sum\x18\x04 \x01(\x12\x42\x02\x30\x01\x42\t\n\x07_in_key\x1av\n\x0e\x41verageEntries\x12\x64\n\x07\x65ntries\x18\x01 \x03(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageEntry\x1a\x36\n\x10\x41verageAggregate\x12\x11\n\x05\x63ount\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x0f\n\x03sum\x18\x02 \x01(\x12\x42\x02\x30\x01\x1a\xfb\x01\n\x0e\x41verageResults\x12t\n\x11\x61ggregate_average\x18\x01 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageAggregateH\x00\x12h\n\x07\x65ntries\x18\x02 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageEntriesH\x00\x42\t\n\x07variant\x1az\n\x0bRankedEntry\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\x13\n\x05\x63ount\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x11\n\x03sum\x18\x03 \x01(\x12\x42\x02\x30\x01H\x00\x12\r\n\x03\x61vg\x18\x04 \x01(\x01H\x00\x12\x13\n\x06in_key\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x42\x07\n\x05valueB\t\n\x07_in_key\x1a\x9a\x01\n\rRankedEntries\x12\x63\n\x07\x65ntries\x18\x01 \x03(\x0b\x32R.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry\x12\x18\n\x07skipped\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x42\n\n\x08_skipped\x1a\x9b\x04\n\nResultData\x12\x65\n\tdocuments\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.DocumentsH\x00\x12\x65\n\x06\x63ounts\x18\x02 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.CountResultsH\x00\x12\x61\n\x04sums\x18\x03 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.SumResultsH\x00\x12i\n\x08\x61verages\x18\x04 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.AverageResultsH\x00\x12\x66\n\x06ranked\x18\x05 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntriesH\x00\x42\t\n\x07variantB\x08\n\x06resultB\t\n\x07version\"\xf4\x02\n\x19GetDocumentHistoryRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetDocumentHistoryRequest.GetDocumentHistoryRequestV0H\x00\x1a\xeb\x01\n\x1bGetDocumentHistoryRequestV0\x12\x18\n\x10\x64\x61ta_contract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x03 \x01(\x0c\x12+\n\x05limit\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0bstart_at_ms\x18\x06 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x07 \x01(\x08\x42\t\n\x07version\"\xf7\x04\n\x1aGetDocumentHistoryResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0H\x00\x1a\xeb\x03\n\x1cGetDocumentHistoryResponseV0\x12~\n\x10\x64ocument_history\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0.DocumentHistoryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x37\n\x14\x44ocumentHistoryEntry\x12\x10\n\x04\x64\x61te\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05value\x18\x02 \x01(\x0c\x1a\x95\x01\n\x0f\x44ocumentHistory\x12\x81\x01\n\x10\x64ocument_entries\x18\x01 \x03(\x0b\x32g.org.dash.platform.dapi.v0.GetDocumentHistoryResponse.GetDocumentHistoryResponseV0.DocumentHistoryEntryB\x08\n\x06resultB\t\n\x07version\"\xed\x01\n!GetIdentityByPublicKeyHashRequest\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashRequest.GetIdentityByPublicKeyHashRequestV0H\x00\x1aM\n#GetIdentityByPublicKeyHashRequestV0\x12\x17\n\x0fpublic_key_hash\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xda\x02\n\"GetIdentityByPublicKeyHashResponse\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashResponse.GetIdentityByPublicKeyHashResponseV0H\x00\x1a\xb6\x01\n$GetIdentityByPublicKeyHashResponseV0\x12\x12\n\x08identity\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbd\x02\n*GetIdentityByNonUniquePublicKeyHashRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashRequest.GetIdentityByNonUniquePublicKeyHashRequestV0H\x00\x1a\x80\x01\n,GetIdentityByNonUniquePublicKeyHashRequestV0\x12\x17\n\x0fpublic_key_hash\x18\x01 \x01(\x0c\x12\x18\n\x0bstart_after\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\x0e\n\x0c_start_afterB\t\n\x07version\"\xd6\x06\n+GetIdentityByNonUniquePublicKeyHashResponse\x12\x82\x01\n\x02v0\x18\x01 \x01(\x0b\x32t.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0H\x00\x1a\x96\x05\n-GetIdentityByNonUniquePublicKeyHashResponseV0\x12\x9a\x01\n\x08identity\x18\x01 \x01(\x0b\x32\x85\x01.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0.IdentityResponseH\x00\x12\x9d\x01\n\x05proof\x18\x02 \x01(\x0b\x32\x8b\x01.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse.GetIdentityByNonUniquePublicKeyHashResponseV0.IdentityProvedResponseH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x36\n\x10IdentityResponse\x12\x15\n\x08identity\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_identity\x1a\xa6\x01\n\x16IdentityProvedResponse\x12P\n&grovedb_identity_public_key_hash_proof\x18\x01 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12!\n\x14identity_proof_bytes\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\x17\n\x15_identity_proof_bytesB\x08\n\x06resultB\t\n\x07version\"\xfb\x01\n#WaitForStateTransitionResultRequest\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest.WaitForStateTransitionResultRequestV0H\x00\x1aU\n%WaitForStateTransitionResultRequestV0\x12\x1d\n\x15state_transition_hash\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x99\x03\n$WaitForStateTransitionResultResponse\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse.WaitForStateTransitionResultResponseV0H\x00\x1a\xef\x01\n&WaitForStateTransitionResultResponseV0\x12I\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x38.org.dash.platform.dapi.v0.StateTransitionBroadcastErrorH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc4\x01\n\x19GetConsensusParamsRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetConsensusParamsRequest.GetConsensusParamsRequestV0H\x00\x1a<\n\x1bGetConsensusParamsRequestV0\x12\x0e\n\x06height\x18\x01 \x01(\x05\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x9c\x04\n\x1aGetConsensusParamsResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetConsensusParamsResponse.GetConsensusParamsResponseV0H\x00\x1aP\n\x14\x43onsensusParamsBlock\x12\x11\n\tmax_bytes\x18\x01 \x01(\t\x12\x0f\n\x07max_gas\x18\x02 \x01(\t\x12\x14\n\x0ctime_iota_ms\x18\x03 \x01(\t\x1a\x62\n\x17\x43onsensusParamsEvidence\x12\x1a\n\x12max_age_num_blocks\x18\x01 \x01(\t\x12\x18\n\x10max_age_duration\x18\x02 \x01(\t\x12\x11\n\tmax_bytes\x18\x03 \x01(\t\x1a\xda\x01\n\x1cGetConsensusParamsResponseV0\x12Y\n\x05\x62lock\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetConsensusParamsResponse.ConsensusParamsBlock\x12_\n\x08\x65vidence\x18\x02 \x01(\x0b\x32M.org.dash.platform.dapi.v0.GetConsensusParamsResponse.ConsensusParamsEvidenceB\t\n\x07version\"\xe4\x01\n%GetProtocolVersionUpgradeStateRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateRequest.GetProtocolVersionUpgradeStateRequestV0H\x00\x1a\x38\n\'GetProtocolVersionUpgradeStateRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xb5\x05\n&GetProtocolVersionUpgradeStateResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0H\x00\x1a\x85\x04\n(GetProtocolVersionUpgradeStateResponseV0\x12\x87\x01\n\x08versions\x18\x01 \x01(\x0b\x32s.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0.VersionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x96\x01\n\x08Versions\x12\x89\x01\n\x08versions\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse.GetProtocolVersionUpgradeStateResponseV0.VersionEntry\x1a:\n\x0cVersionEntry\x12\x16\n\x0eversion_number\x18\x01 \x01(\r\x12\x12\n\nvote_count\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xa3\x02\n*GetProtocolVersionUpgradeVoteStatusRequest\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusRequest.GetProtocolVersionUpgradeVoteStatusRequestV0H\x00\x1ag\n,GetProtocolVersionUpgradeVoteStatusRequestV0\x12\x19\n\x11start_pro_tx_hash\x18\x01 \x01(\x0c\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xef\x05\n+GetProtocolVersionUpgradeVoteStatusResponse\x12\x82\x01\n\x02v0\x18\x01 \x01(\x0b\x32t.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0H\x00\x1a\xaf\x04\n-GetProtocolVersionUpgradeVoteStatusResponseV0\x12\x98\x01\n\x08versions\x18\x01 \x01(\x0b\x32\x83\x01.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0.VersionSignalsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xaf\x01\n\x0eVersionSignals\x12\x9c\x01\n\x0fversion_signals\x18\x01 \x03(\x0b\x32\x82\x01.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse.GetProtocolVersionUpgradeVoteStatusResponseV0.VersionSignal\x1a\x35\n\rVersionSignal\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x0f\n\x07version\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xf5\x01\n\x14GetEpochsInfoRequest\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetEpochsInfoRequest.GetEpochsInfoRequestV0H\x00\x1a|\n\x16GetEpochsInfoRequestV0\x12\x31\n\x0bstart_epoch\x18\x01 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\x11\n\tascending\x18\x03 \x01(\x08\x12\r\n\x05prove\x18\x04 \x01(\x08\x42\t\n\x07version\"\x99\x05\n\x15GetEpochsInfoResponse\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0H\x00\x1a\x9c\x04\n\x17GetEpochsInfoResponseV0\x12\x65\n\x06\x65pochs\x18\x01 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0.EpochInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1au\n\nEpochInfos\x12g\n\x0b\x65poch_infos\x18\x01 \x03(\x0b\x32R.org.dash.platform.dapi.v0.GetEpochsInfoResponse.GetEpochsInfoResponseV0.EpochInfo\x1a\xa6\x01\n\tEpochInfo\x12\x0e\n\x06number\x18\x01 \x01(\r\x12\x1e\n\x12\x66irst_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x1f\n\x17\x66irst_core_block_height\x18\x03 \x01(\r\x12\x16\n\nstart_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x0e\x66\x65\x65_multiplier\x18\x05 \x01(\x01\x12\x18\n\x10protocol_version\x18\x06 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xbf\x02\n\x1dGetFinalizedEpochInfosRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetFinalizedEpochInfosRequest.GetFinalizedEpochInfosRequestV0H\x00\x1a\xaa\x01\n\x1fGetFinalizedEpochInfosRequestV0\x12\x19\n\x11start_epoch_index\x18\x01 \x01(\r\x12\"\n\x1astart_epoch_index_included\x18\x02 \x01(\x08\x12\x17\n\x0f\x65nd_epoch_index\x18\x03 \x01(\r\x12 \n\x18\x65nd_epoch_index_included\x18\x04 \x01(\x08\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\t\n\x07version\"\xbd\t\n\x1eGetFinalizedEpochInfosResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0H\x00\x1a\xa5\x08\n GetFinalizedEpochInfosResponseV0\x12\x80\x01\n\x06\x65pochs\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.FinalizedEpochInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xa4\x01\n\x13\x46inalizedEpochInfos\x12\x8c\x01\n\x15\x66inalized_epoch_infos\x18\x01 \x03(\x0b\x32m.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.FinalizedEpochInfo\x1a\x9f\x04\n\x12\x46inalizedEpochInfo\x12\x0e\n\x06number\x18\x01 \x01(\r\x12\x1e\n\x12\x66irst_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x1f\n\x17\x66irst_core_block_height\x18\x03 \x01(\r\x12\x1c\n\x10\x66irst_block_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x0e\x66\x65\x65_multiplier\x18\x05 \x01(\x01\x12\x18\n\x10protocol_version\x18\x06 \x01(\r\x12!\n\x15total_blocks_in_epoch\x18\x07 \x01(\x04\x42\x02\x30\x01\x12*\n\"next_epoch_start_core_block_height\x18\x08 \x01(\r\x12!\n\x15total_processing_fees\x18\t \x01(\x04\x42\x02\x30\x01\x12*\n\x1etotal_distributed_storage_fees\x18\n \x01(\x04\x42\x02\x30\x01\x12&\n\x1atotal_created_storage_fees\x18\x0b \x01(\x04\x42\x02\x30\x01\x12\x1e\n\x12\x63ore_block_rewards\x18\x0c \x01(\x04\x42\x02\x30\x01\x12\x81\x01\n\x0f\x62lock_proposers\x18\r \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse.GetFinalizedEpochInfosResponseV0.BlockProposer\x1a\x39\n\rBlockProposer\x12\x13\n\x0bproposer_id\x18\x01 \x01(\x0c\x12\x13\n\x0b\x62lock_count\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xde\x04\n\x1cGetContestedResourcesRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetContestedResourcesRequest.GetContestedResourcesRequestV0H\x00\x1a\xcc\x03\n\x1eGetContestedResourcesRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x1a\n\x12start_index_values\x18\x04 \x03(\x0c\x12\x18\n\x10\x65nd_index_values\x18\x05 \x03(\x0c\x12\x89\x01\n\x13start_at_value_info\x18\x06 \x01(\x0b\x32g.org.dash.platform.dapi.v0.GetContestedResourcesRequest.GetContestedResourcesRequestV0.StartAtValueInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x07 \x01(\rH\x01\x88\x01\x01\x12\x17\n\x0forder_ascending\x18\x08 \x01(\x08\x12\r\n\x05prove\x18\t \x01(\x08\x1a\x45\n\x10StartAtValueInfo\x12\x13\n\x0bstart_value\x18\x01 \x01(\x0c\x12\x1c\n\x14start_value_included\x18\x02 \x01(\x08\x42\x16\n\x14_start_at_value_infoB\x08\n\x06_countB\t\n\x07version\"\x88\x04\n\x1dGetContestedResourcesResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetContestedResourcesResponse.GetContestedResourcesResponseV0H\x00\x1a\xf3\x02\n\x1fGetContestedResourcesResponseV0\x12\x95\x01\n\x19\x63ontested_resource_values\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetContestedResourcesResponse.GetContestedResourcesResponseV0.ContestedResourceValuesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a<\n\x17\x43ontestedResourceValues\x12!\n\x19\x63ontested_resource_values\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xd2\x05\n\x1cGetVotePollsByEndDateRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0H\x00\x1a\xc0\x04\n\x1eGetVotePollsByEndDateRequestV0\x12\x84\x01\n\x0fstart_time_info\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0.StartAtTimeInfoH\x00\x88\x01\x01\x12\x80\x01\n\rend_time_info\x18\x02 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest.GetVotePollsByEndDateRequestV0.EndAtTimeInfoH\x01\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x13\n\x06offset\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x11\n\tascending\x18\x05 \x01(\x08\x12\r\n\x05prove\x18\x06 \x01(\x08\x1aI\n\x0fStartAtTimeInfo\x12\x19\n\rstart_time_ms\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x13start_time_included\x18\x02 \x01(\x08\x1a\x43\n\rEndAtTimeInfo\x12\x17\n\x0b\x65nd_time_ms\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x19\n\x11\x65nd_time_included\x18\x02 \x01(\x08\x42\x12\n\x10_start_time_infoB\x10\n\x0e_end_time_infoB\x08\n\x06_limitB\t\n\x07_offsetB\t\n\x07version\"\x83\x06\n\x1dGetVotePollsByEndDateResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0H\x00\x1a\xee\x04\n\x1fGetVotePollsByEndDateResponseV0\x12\x9c\x01\n\x18vote_polls_by_timestamps\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0.SerializedVotePollsByTimestampsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aV\n\x1eSerializedVotePollsByTimestamp\x12\x15\n\ttimestamp\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1d\n\x15serialized_vote_polls\x18\x02 \x03(\x0c\x1a\xd7\x01\n\x1fSerializedVotePollsByTimestamps\x12\x99\x01\n\x18vote_polls_by_timestamps\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse.GetVotePollsByEndDateResponseV0.SerializedVotePollsByTimestamp\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x42\x08\n\x06resultB\t\n\x07version\"\xff\x06\n$GetContestedResourceVoteStateRequest\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0H\x00\x1a\xd5\x05\n&GetContestedResourceVoteStateRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x14\n\x0cindex_values\x18\x04 \x03(\x0c\x12\x86\x01\n\x0bresult_type\x18\x05 \x01(\x0e\x32q.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0.ResultType\x12\x36\n.allow_include_locked_and_abstaining_vote_tally\x18\x06 \x01(\x08\x12\xa3\x01\n\x18start_at_identifier_info\x18\x07 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest.GetContestedResourceVoteStateRequestV0.StartAtIdentifierInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x08 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\t \x01(\x08\x1aT\n\x15StartAtIdentifierInfo\x12\x18\n\x10start_identifier\x18\x01 \x01(\x0c\x12!\n\x19start_identifier_included\x18\x02 \x01(\x08\"I\n\nResultType\x12\r\n\tDOCUMENTS\x10\x00\x12\x0e\n\nVOTE_TALLY\x10\x01\x12\x1c\n\x18\x44OCUMENTS_AND_VOTE_TALLY\x10\x02\x42\x1b\n\x19_start_at_identifier_infoB\x08\n\x06_countB\t\n\x07version\"\x94\x0c\n%GetContestedResourceVoteStateResponse\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0H\x00\x1a\xe7\n\n\'GetContestedResourceVoteStateResponseV0\x12\xae\x01\n\x1d\x63ontested_resource_contenders\x18\x01 \x01(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.ContestedResourceContendersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xda\x03\n\x10\x46inishedVoteInfo\x12\xad\x01\n\x15\x66inished_vote_outcome\x18\x01 \x01(\x0e\x32\x8d\x01.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.FinishedVoteInfo.FinishedVoteOutcome\x12\x1f\n\x12won_by_identity_id\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12$\n\x18\x66inished_at_block_height\x18\x03 \x01(\x04\x42\x02\x30\x01\x12%\n\x1d\x66inished_at_core_block_height\x18\x04 \x01(\r\x12%\n\x19\x66inished_at_block_time_ms\x18\x05 \x01(\x04\x42\x02\x30\x01\x12\x19\n\x11\x66inished_at_epoch\x18\x06 \x01(\r\"O\n\x13\x46inishedVoteOutcome\x12\x14\n\x10TOWARDS_IDENTITY\x10\x00\x12\n\n\x06LOCKED\x10\x01\x12\x16\n\x12NO_PREVIOUS_WINNER\x10\x02\x42\x15\n\x13_won_by_identity_id\x1a\xc4\x03\n\x1b\x43ontestedResourceContenders\x12\x86\x01\n\ncontenders\x18\x01 \x03(\x0b\x32r.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.Contender\x12\x1f\n\x12\x61\x62stain_vote_tally\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x1c\n\x0flock_vote_tally\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x9a\x01\n\x12\x66inished_vote_info\x18\x04 \x01(\x0b\x32y.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse.GetContestedResourceVoteStateResponseV0.FinishedVoteInfoH\x02\x88\x01\x01\x42\x15\n\x13_abstain_vote_tallyB\x12\n\x10_lock_vote_tallyB\x15\n\x13_finished_vote_info\x1ak\n\tContender\x12\x12\n\nidentifier\x18\x01 \x01(\x0c\x12\x17\n\nvote_count\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x15\n\x08\x64ocument\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x42\r\n\x0b_vote_countB\x0b\n\t_documentB\x08\n\x06resultB\t\n\x07version\"\xd5\x05\n,GetContestedResourceVotersForIdentityRequest\x12\x84\x01\n\x02v0\x18\x01 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest.GetContestedResourceVotersForIdentityRequestV0H\x00\x1a\x92\x04\n.GetContestedResourceVotersForIdentityRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\x12\n\nindex_name\x18\x03 \x01(\t\x12\x14\n\x0cindex_values\x18\x04 \x03(\x0c\x12\x15\n\rcontestant_id\x18\x05 \x01(\x0c\x12\xb4\x01\n\x18start_at_identifier_info\x18\x06 \x01(\x0b\x32\x8c\x01.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest.GetContestedResourceVotersForIdentityRequestV0.StartAtIdentifierInfoH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x07 \x01(\rH\x01\x88\x01\x01\x12\x17\n\x0forder_ascending\x18\x08 \x01(\x08\x12\r\n\x05prove\x18\t \x01(\x08\x1aT\n\x15StartAtIdentifierInfo\x12\x18\n\x10start_identifier\x18\x01 \x01(\x0c\x12!\n\x19start_identifier_included\x18\x02 \x01(\x08\x42\x1b\n\x19_start_at_identifier_infoB\x08\n\x06_countB\t\n\x07version\"\xf1\x04\n-GetContestedResourceVotersForIdentityResponse\x12\x86\x01\n\x02v0\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse.GetContestedResourceVotersForIdentityResponseV0H\x00\x1a\xab\x03\n/GetContestedResourceVotersForIdentityResponseV0\x12\xb6\x01\n\x19\x63ontested_resource_voters\x18\x01 \x01(\x0b\x32\x90\x01.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse.GetContestedResourceVotersForIdentityResponseV0.ContestedResourceVotersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x43\n\x17\x43ontestedResourceVoters\x12\x0e\n\x06voters\x18\x01 \x03(\x0c\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x42\x08\n\x06resultB\t\n\x07version\"\xad\x05\n(GetContestedResourceIdentityVotesRequest\x12|\n\x02v0\x18\x01 \x01(\x0b\x32n.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest.GetContestedResourceIdentityVotesRequestV0H\x00\x1a\xf7\x03\n*GetContestedResourceIdentityVotesRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12+\n\x05limit\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12,\n\x06offset\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x17\n\x0forder_ascending\x18\x04 \x01(\x08\x12\xae\x01\n\x1astart_at_vote_poll_id_info\x18\x05 \x01(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest.GetContestedResourceIdentityVotesRequestV0.StartAtVotePollIdInfoH\x00\x88\x01\x01\x12\r\n\x05prove\x18\x06 \x01(\x08\x1a\x61\n\x15StartAtVotePollIdInfo\x12 \n\x18start_at_poll_identifier\x18\x01 \x01(\x0c\x12&\n\x1estart_poll_identifier_included\x18\x02 \x01(\x08\x42\x1d\n\x1b_start_at_vote_poll_id_infoB\t\n\x07version\"\xc8\n\n)GetContestedResourceIdentityVotesResponse\x12~\n\x02v0\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0H\x00\x1a\x8f\t\n+GetContestedResourceIdentityVotesResponseV0\x12\xa1\x01\n\x05votes\x18\x01 \x01(\x0b\x32\x8f\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ContestedResourceIdentityVotesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\xf7\x01\n\x1e\x43ontestedResourceIdentityVotes\x12\xba\x01\n!contested_resource_identity_votes\x18\x01 \x03(\x0b\x32\x8e\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ContestedResourceIdentityVote\x12\x18\n\x10\x66inished_results\x18\x02 \x01(\x08\x1a\xad\x02\n\x12ResourceVoteChoice\x12\xad\x01\n\x10vote_choice_type\x18\x01 \x01(\x0e\x32\x92\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ResourceVoteChoice.VoteChoiceType\x12\x18\n\x0bidentity_id\x18\x02 \x01(\x0cH\x00\x88\x01\x01\"=\n\x0eVoteChoiceType\x12\x14\n\x10TOWARDS_IDENTITY\x10\x00\x12\x0b\n\x07\x41\x42STAIN\x10\x01\x12\x08\n\x04LOCK\x10\x02\x42\x0e\n\x0c_identity_id\x1a\x95\x02\n\x1d\x43ontestedResourceIdentityVote\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1a\n\x12\x64ocument_type_name\x18\x02 \x01(\t\x12\'\n\x1fserialized_index_storage_values\x18\x03 \x03(\x0c\x12\x99\x01\n\x0bvote_choice\x18\x04 \x01(\x0b\x32\x83\x01.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse.GetContestedResourceIdentityVotesResponseV0.ResourceVoteChoiceB\x08\n\x06resultB\t\n\x07version\"\xf0\x01\n%GetPrefundedSpecializedBalanceRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceRequest.GetPrefundedSpecializedBalanceRequestV0H\x00\x1a\x44\n\'GetPrefundedSpecializedBalanceRequestV0\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xed\x02\n&GetPrefundedSpecializedBalanceResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceResponse.GetPrefundedSpecializedBalanceResponseV0H\x00\x1a\xbd\x01\n(GetPrefundedSpecializedBalanceResponseV0\x12\x15\n\x07\x62\x61lance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xd0\x01\n GetTotalCreditsInPlatformRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformRequest.GetTotalCreditsInPlatformRequestV0H\x00\x1a\x33\n\"GetTotalCreditsInPlatformRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xd9\x02\n!GetTotalCreditsInPlatformResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformResponse.GetTotalCreditsInPlatformResponseV0H\x00\x1a\xb8\x01\n#GetTotalCreditsInPlatformResponseV0\x12\x15\n\x07\x63redits\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc4\x01\n\x16GetPathElementsRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetPathElementsRequest.GetPathElementsRequestV0H\x00\x1a\x45\n\x18GetPathElementsRequestV0\x12\x0c\n\x04path\x18\x01 \x03(\x0c\x12\x0c\n\x04keys\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xa3\x03\n\x17GetPathElementsResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetPathElementsResponse.GetPathElementsResponseV0H\x00\x1a\xa0\x02\n\x19GetPathElementsResponseV0\x12i\n\x08\x65lements\x18\x01 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetPathElementsResponse.GetPathElementsResponseV0.ElementsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1c\n\x08\x45lements\x12\x10\n\x08\x65lements\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\x81\x01\n\x10GetStatusRequest\x12L\n\x02v0\x18\x01 \x01(\x0b\x32>.org.dash.platform.dapi.v0.GetStatusRequest.GetStatusRequestV0H\x00\x1a\x14\n\x12GetStatusRequestV0B\t\n\x07version\"\xe4\x10\n\x11GetStatusResponse\x12N\n\x02v0\x18\x01 \x01(\x0b\x32@.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0H\x00\x1a\xf3\x0f\n\x13GetStatusResponseV0\x12Y\n\x07version\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version\x12S\n\x04node\x18\x02 \x01(\x0b\x32\x45.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Node\x12U\n\x05\x63hain\x18\x03 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Chain\x12Y\n\x07network\x18\x04 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Network\x12^\n\nstate_sync\x18\x05 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.StateSync\x12S\n\x04time\x18\x06 \x01(\x0b\x32\x45.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Time\x1a\x82\x05\n\x07Version\x12\x63\n\x08software\x18\x01 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Software\x12\x63\n\x08protocol\x18\x02 \x01(\x0b\x32Q.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol\x1a^\n\x08Software\x12\x0c\n\x04\x64\x61pi\x18\x01 \x01(\t\x12\x12\n\x05\x64rive\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntenderdash\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_driveB\r\n\x0b_tenderdash\x1a\xcc\x02\n\x08Protocol\x12p\n\ntenderdash\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol.Tenderdash\x12\x66\n\x05\x64rive\x18\x02 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetStatusResponse.GetStatusResponseV0.Version.Protocol.Drive\x1a(\n\nTenderdash\x12\x0b\n\x03p2p\x18\x01 \x01(\r\x12\r\n\x05\x62lock\x18\x02 \x01(\r\x1a<\n\x05\x44rive\x12\x0e\n\x06latest\x18\x03 \x01(\r\x12\x0f\n\x07\x63urrent\x18\x04 \x01(\r\x12\x12\n\nnext_epoch\x18\x05 \x01(\r\x1a\x7f\n\x04Time\x12\x11\n\x05local\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x16\n\x05\x62lock\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x88\x01\x01\x12\x18\n\x07genesis\x18\x03 \x01(\x04\x42\x02\x30\x01H\x01\x88\x01\x01\x12\x12\n\x05\x65poch\x18\x04 \x01(\rH\x02\x88\x01\x01\x42\x08\n\x06_blockB\n\n\x08_genesisB\x08\n\x06_epoch\x1a<\n\x04Node\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x18\n\x0bpro_tx_hash\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\x0e\n\x0c_pro_tx_hash\x1a\xb3\x02\n\x05\x43hain\x12\x13\n\x0b\x63\x61tching_up\x18\x01 \x01(\x08\x12\x19\n\x11latest_block_hash\x18\x02 \x01(\x0c\x12\x17\n\x0flatest_app_hash\x18\x03 \x01(\x0c\x12\x1f\n\x13latest_block_height\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x13\x65\x61rliest_block_hash\x18\x05 \x01(\x0c\x12\x19\n\x11\x65\x61rliest_app_hash\x18\x06 \x01(\x0c\x12!\n\x15\x65\x61rliest_block_height\x18\x07 \x01(\x04\x42\x02\x30\x01\x12!\n\x15max_peer_block_height\x18\t \x01(\x04\x42\x02\x30\x01\x12%\n\x18\x63ore_chain_locked_height\x18\n \x01(\rH\x00\x88\x01\x01\x42\x1b\n\x19_core_chain_locked_height\x1a\x43\n\x07Network\x12\x10\n\x08\x63hain_id\x18\x01 \x01(\t\x12\x13\n\x0bpeers_count\x18\x02 \x01(\r\x12\x11\n\tlistening\x18\x03 \x01(\x08\x1a\x85\x02\n\tStateSync\x12\x1d\n\x11total_synced_time\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1a\n\x0eremaining_time\x18\x02 \x01(\x04\x42\x02\x30\x01\x12\x17\n\x0ftotal_snapshots\x18\x03 \x01(\r\x12\"\n\x16\x63hunk_process_avg_time\x18\x04 \x01(\x04\x42\x02\x30\x01\x12\x1b\n\x0fsnapshot_height\x18\x05 \x01(\x04\x42\x02\x30\x01\x12!\n\x15snapshot_chunks_count\x18\x06 \x01(\x04\x42\x02\x30\x01\x12\x1d\n\x11\x62\x61\x63kfilled_blocks\x18\x07 \x01(\x04\x42\x02\x30\x01\x12!\n\x15\x62\x61\x63kfill_blocks_total\x18\x08 \x01(\x04\x42\x02\x30\x01\x42\t\n\x07version\"\xb1\x01\n\x1cGetCurrentQuorumsInfoRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoRequest.GetCurrentQuorumsInfoRequestV0H\x00\x1a \n\x1eGetCurrentQuorumsInfoRequestV0B\t\n\x07version\"\xa1\x05\n\x1dGetCurrentQuorumsInfoResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.GetCurrentQuorumsInfoResponseV0H\x00\x1a\x46\n\x0bValidatorV0\x12\x13\n\x0bpro_tx_hash\x18\x01 \x01(\x0c\x12\x0f\n\x07node_ip\x18\x02 \x01(\t\x12\x11\n\tis_banned\x18\x03 \x01(\x08\x1a\xaf\x01\n\x0eValidatorSetV0\x12\x13\n\x0bquorum_hash\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63ore_height\x18\x02 \x01(\r\x12U\n\x07members\x18\x03 \x03(\x0b\x32\x44.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.ValidatorV0\x12\x1c\n\x14threshold_public_key\x18\x04 \x01(\x0c\x1a\x92\x02\n\x1fGetCurrentQuorumsInfoResponseV0\x12\x15\n\rquorum_hashes\x18\x01 \x03(\x0c\x12\x1b\n\x13\x63urrent_quorum_hash\x18\x02 \x01(\x0c\x12_\n\x0evalidator_sets\x18\x03 \x03(\x0b\x32G.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse.ValidatorSetV0\x12\x1b\n\x13last_block_proposer\x18\x04 \x01(\x0c\x12=\n\x08metadata\x18\x05 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\t\n\x07version\"\xf4\x01\n\x1fGetIdentityTokenBalancesRequest\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentityTokenBalancesRequest.GetIdentityTokenBalancesRequestV0H\x00\x1aZ\n!GetIdentityTokenBalancesRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x11\n\ttoken_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xad\x05\n GetIdentityTokenBalancesResponse\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0H\x00\x1a\x8f\x04\n\"GetIdentityTokenBalancesResponseV0\x12\x86\x01\n\x0etoken_balances\x18\x01 \x01(\x0b\x32l.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0.TokenBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aG\n\x11TokenBalanceEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x07\x62\x61lance\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\x9a\x01\n\rTokenBalances\x12\x88\x01\n\x0etoken_balances\x18\x01 \x03(\x0b\x32p.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse.GetIdentityTokenBalancesResponseV0.TokenBalanceEntryB\x08\n\x06resultB\t\n\x07version\"\xfc\x01\n!GetIdentitiesTokenBalancesRequest\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesRequest.GetIdentitiesTokenBalancesRequestV0H\x00\x1a\\\n#GetIdentitiesTokenBalancesRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x0cidentity_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xf2\x05\n\"GetIdentitiesTokenBalancesResponse\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0H\x00\x1a\xce\x04\n$GetIdentitiesTokenBalancesResponseV0\x12\x9b\x01\n\x17identity_token_balances\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0.IdentityTokenBalancesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aR\n\x19IdentityTokenBalanceEntry\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x14\n\x07\x62\x61lance\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_balance\x1a\xb7\x01\n\x15IdentityTokenBalances\x12\x9d\x01\n\x17identity_token_balances\x18\x01 \x03(\x0b\x32|.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse.GetIdentitiesTokenBalancesResponseV0.IdentityTokenBalanceEntryB\x08\n\x06resultB\t\n\x07version\"\xe8\x01\n\x1cGetIdentityTokenInfosRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetIdentityTokenInfosRequest.GetIdentityTokenInfosRequestV0H\x00\x1aW\n\x1eGetIdentityTokenInfosRequestV0\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x11\n\ttoken_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\x98\x06\n\x1dGetIdentityTokenInfosResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0H\x00\x1a\x83\x05\n\x1fGetIdentityTokenInfosResponseV0\x12z\n\x0btoken_infos\x18\x01 \x01(\x0b\x32\x63.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a(\n\x16TokenIdentityInfoEntry\x12\x0e\n\x06\x66rozen\x18\x01 \x01(\x08\x1a\xb0\x01\n\x0eTokenInfoEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x82\x01\n\x04info\x18\x02 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenIdentityInfoEntryH\x00\x88\x01\x01\x42\x07\n\x05_info\x1a\x8a\x01\n\nTokenInfos\x12|\n\x0btoken_infos\x18\x01 \x03(\x0b\x32g.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse.GetIdentityTokenInfosResponseV0.TokenInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xf0\x01\n\x1eGetIdentitiesTokenInfosRequest\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosRequest.GetIdentitiesTokenInfosRequestV0H\x00\x1aY\n GetIdentitiesTokenInfosRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x14\n\x0cidentity_ids\x18\x02 \x03(\x0c\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xca\x06\n\x1fGetIdentitiesTokenInfosResponse\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0H\x00\x1a\xaf\x05\n!GetIdentitiesTokenInfosResponseV0\x12\x8f\x01\n\x14identity_token_infos\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.IdentityTokenInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a(\n\x16TokenIdentityInfoEntry\x12\x0e\n\x06\x66rozen\x18\x01 \x01(\x08\x1a\xb7\x01\n\x0eTokenInfoEntry\x12\x13\n\x0bidentity_id\x18\x01 \x01(\x0c\x12\x86\x01\n\x04info\x18\x02 \x01(\x0b\x32s.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.TokenIdentityInfoEntryH\x00\x88\x01\x01\x42\x07\n\x05_info\x1a\x97\x01\n\x12IdentityTokenInfos\x12\x80\x01\n\x0btoken_infos\x18\x01 \x03(\x0b\x32k.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse.GetIdentitiesTokenInfosResponseV0.TokenInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xbf\x01\n\x17GetTokenStatusesRequest\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetTokenStatusesRequest.GetTokenStatusesRequestV0H\x00\x1a=\n\x19GetTokenStatusesRequestV0\x12\x11\n\ttoken_ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xe7\x04\n\x18GetTokenStatusesResponse\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0H\x00\x1a\xe1\x03\n\x1aGetTokenStatusesResponseV0\x12v\n\x0etoken_statuses\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0.TokenStatusesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x44\n\x10TokenStatusEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x13\n\x06paused\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\t\n\x07_paused\x1a\x88\x01\n\rTokenStatuses\x12w\n\x0etoken_statuses\x18\x01 \x03(\x0b\x32_.org.dash.platform.dapi.v0.GetTokenStatusesResponse.GetTokenStatusesResponseV0.TokenStatusEntryB\x08\n\x06resultB\t\n\x07version\"\xef\x01\n#GetTokenDirectPurchasePricesRequest\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesRequest.GetTokenDirectPurchasePricesRequestV0H\x00\x1aI\n%GetTokenDirectPurchasePricesRequestV0\x12\x11\n\ttoken_ids\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x8b\t\n$GetTokenDirectPurchasePricesResponse\x12t\n\x02v0\x18\x01 \x01(\x0b\x32\x66.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0H\x00\x1a\xe1\x07\n&GetTokenDirectPurchasePricesResponseV0\x12\xa9\x01\n\x1ctoken_direct_purchase_prices\x18\x01 \x01(\x0b\x32\x80\x01.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.TokenDirectPurchasePricesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x33\n\x10PriceForQuantity\x12\x10\n\x08quantity\x18\x01 \x01(\x04\x12\r\n\x05price\x18\x02 \x01(\x04\x1a\xa7\x01\n\x0fPricingSchedule\x12\x93\x01\n\x12price_for_quantity\x18\x01 \x03(\x0b\x32w.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.PriceForQuantity\x1a\xe4\x01\n\x1dTokenDirectPurchasePriceEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x15\n\x0b\x66ixed_price\x18\x02 \x01(\x04H\x00\x12\x90\x01\n\x0evariable_price\x18\x03 \x01(\x0b\x32v.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.PricingScheduleH\x00\x42\x07\n\x05price\x1a\xc8\x01\n\x19TokenDirectPurchasePrices\x12\xaa\x01\n\x1btoken_direct_purchase_price\x18\x01 \x03(\x0b\x32\x84\x01.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse.GetTokenDirectPurchasePricesResponseV0.TokenDirectPurchasePriceEntryB\x08\n\x06resultB\t\n\x07version\"\xce\x01\n\x1bGetTokenContractInfoRequest\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetTokenContractInfoRequest.GetTokenContractInfoRequestV0H\x00\x1a@\n\x1dGetTokenContractInfoRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xfb\x03\n\x1cGetTokenContractInfoResponse\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetTokenContractInfoResponse.GetTokenContractInfoResponseV0H\x00\x1a\xe9\x02\n\x1eGetTokenContractInfoResponseV0\x12|\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32l.org.dash.platform.dapi.v0.GetTokenContractInfoResponse.GetTokenContractInfoResponseV0.TokenContractInfoDataH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aM\n\x15TokenContractInfoData\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17token_contract_position\x18\x02 \x01(\rB\x08\n\x06resultB\t\n\x07version\"\xef\x04\n)GetTokenPreProgrammedDistributionsRequest\x12~\n\x02v0\x18\x01 \x01(\x0b\x32p.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest.GetTokenPreProgrammedDistributionsRequestV0H\x00\x1a\xb6\x03\n+GetTokenPreProgrammedDistributionsRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x98\x01\n\rstart_at_info\x18\x02 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest.GetTokenPreProgrammedDistributionsRequestV0.StartAtInfoH\x00\x88\x01\x01\x12\x12\n\x05limit\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x04 \x01(\x08\x1a\x9a\x01\n\x0bStartAtInfo\x12\x15\n\rstart_time_ms\x18\x01 \x01(\x04\x12\x1c\n\x0fstart_recipient\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12%\n\x18start_recipient_included\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x12\n\x10_start_recipientB\x1b\n\x19_start_recipient_includedB\x10\n\x0e_start_at_infoB\x08\n\x06_limitB\t\n\x07version\"\xec\x07\n*GetTokenPreProgrammedDistributionsResponse\x12\x80\x01\n\x02v0\x18\x01 \x01(\x0b\x32r.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0H\x00\x1a\xaf\x06\n,GetTokenPreProgrammedDistributionsResponseV0\x12\xa5\x01\n\x13token_distributions\x18\x01 \x01(\x0b\x32\x85\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenDistributionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a>\n\x16TokenDistributionEntry\x12\x14\n\x0crecipient_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x1a\xd4\x01\n\x1bTokenTimedDistributionEntry\x12\x11\n\ttimestamp\x18\x01 \x01(\x04\x12\xa1\x01\n\rdistributions\x18\x02 \x03(\x0b\x32\x89\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenDistributionEntry\x1a\xc3\x01\n\x12TokenDistributions\x12\xac\x01\n\x13token_distributions\x18\x01 \x03(\x0b\x32\x8e\x01.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse.GetTokenPreProgrammedDistributionsResponseV0.TokenTimedDistributionEntryB\x08\n\x06resultB\t\n\x07version\"\x82\x04\n-GetTokenPerpetualDistributionLastClaimRequest\x12\x86\x01\n\x02v0\x18\x01 \x01(\x0b\x32x.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest.GetTokenPerpetualDistributionLastClaimRequestV0H\x00\x1aI\n\x11\x43ontractTokenInfo\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17token_contract_position\x18\x02 \x01(\r\x1a\xf1\x01\n/GetTokenPerpetualDistributionLastClaimRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12v\n\rcontract_info\x18\x02 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest.ContractTokenInfoH\x00\x88\x01\x01\x12\x13\n\x0bidentity_id\x18\x04 \x01(\x0c\x12\r\n\x05prove\x18\x05 \x01(\x08\x42\x10\n\x0e_contract_infoB\t\n\x07version\"\x93\x05\n.GetTokenPerpetualDistributionLastClaimResponse\x12\x88\x01\n\x02v0\x18\x01 \x01(\x0b\x32z.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse.GetTokenPerpetualDistributionLastClaimResponseV0H\x00\x1a\xca\x03\n0GetTokenPerpetualDistributionLastClaimResponseV0\x12\x9f\x01\n\nlast_claim\x18\x01 \x01(\x0b\x32\x88\x01.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse.GetTokenPerpetualDistributionLastClaimResponseV0.LastClaimInfoH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1ax\n\rLastClaimInfo\x12\x1a\n\x0ctimestamp_ms\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x1a\n\x0c\x62lock_height\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x0f\n\x05\x65poch\x18\x03 \x01(\rH\x00\x12\x13\n\traw_bytes\x18\x04 \x01(\x0cH\x00\x42\t\n\x07paid_atB\x08\n\x06resultB\t\n\x07version\"\xca\x01\n\x1aGetTokenTotalSupplyRequest\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetTokenTotalSupplyRequest.GetTokenTotalSupplyRequestV0H\x00\x1a?\n\x1cGetTokenTotalSupplyRequestV0\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xaf\x04\n\x1bGetTokenTotalSupplyResponse\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse.GetTokenTotalSupplyResponseV0H\x00\x1a\xa0\x03\n\x1dGetTokenTotalSupplyResponseV0\x12\x88\x01\n\x12token_total_supply\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse.GetTokenTotalSupplyResponseV0.TokenTotalSupplyEntryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1ax\n\x15TokenTotalSupplyEntry\x12\x10\n\x08token_id\x18\x01 \x01(\x0c\x12\x30\n(total_aggregated_amount_in_user_accounts\x18\x02 \x01(\x04\x12\x1b\n\x13total_system_amount\x18\x03 \x01(\x04\x42\x08\n\x06resultB\t\n\x07version\"\xd2\x01\n\x13GetGroupInfoRequest\x12R\n\x02v0\x18\x01 \x01(\x0b\x32\x44.org.dash.platform.dapi.v0.GetGroupInfoRequest.GetGroupInfoRequestV0H\x00\x1a\\\n\x15GetGroupInfoRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xd4\x05\n\x14GetGroupInfoResponse\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0H\x00\x1a\xda\x04\n\x16GetGroupInfoResponseV0\x12\x66\n\ngroup_info\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupInfoH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x04 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x34\n\x10GroupMemberEntry\x12\x11\n\tmember_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\x98\x01\n\x0eGroupInfoEntry\x12h\n\x07members\x18\x01 \x03(\x0b\x32W.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupMemberEntry\x12\x1c\n\x14group_required_power\x18\x02 \x01(\r\x1a\x8a\x01\n\tGroupInfo\x12n\n\ngroup_info\x18\x01 \x01(\x0b\x32U.org.dash.platform.dapi.v0.GetGroupInfoResponse.GetGroupInfoResponseV0.GroupInfoEntryH\x00\x88\x01\x01\x42\r\n\x0b_group_infoB\x08\n\x06resultB\t\n\x07version\"\xed\x03\n\x14GetGroupInfosRequest\x12T\n\x02v0\x18\x01 \x01(\x0b\x32\x46.org.dash.platform.dapi.v0.GetGroupInfosRequest.GetGroupInfosRequestV0H\x00\x1au\n\x1cStartAtGroupContractPosition\x12%\n\x1dstart_group_contract_position\x18\x01 \x01(\r\x12.\n&start_group_contract_position_included\x18\x02 \x01(\x08\x1a\xfc\x01\n\x16GetGroupInfosRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12{\n start_at_group_contract_position\x18\x02 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetGroupInfosRequest.StartAtGroupContractPositionH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x04 \x01(\x08\x42#\n!_start_at_group_contract_positionB\x08\n\x06_countB\t\n\x07version\"\xff\x05\n\x15GetGroupInfosResponse\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0H\x00\x1a\x82\x05\n\x17GetGroupInfosResponseV0\x12j\n\x0bgroup_infos\x18\x01 \x01(\x0b\x32S.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupInfosH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x04 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x34\n\x10GroupMemberEntry\x12\x11\n\tmember_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\xc3\x01\n\x16GroupPositionInfoEntry\x12\x1f\n\x17group_contract_position\x18\x01 \x01(\r\x12j\n\x07members\x18\x02 \x03(\x0b\x32Y.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupMemberEntry\x12\x1c\n\x14group_required_power\x18\x03 \x01(\r\x1a\x82\x01\n\nGroupInfos\x12t\n\x0bgroup_infos\x18\x01 \x03(\x0b\x32_.org.dash.platform.dapi.v0.GetGroupInfosResponse.GetGroupInfosResponseV0.GroupPositionInfoEntryB\x08\n\x06resultB\t\n\x07version\"\xbe\x04\n\x16GetGroupActionsRequest\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetGroupActionsRequest.GetGroupActionsRequestV0H\x00\x1aL\n\x0fStartAtActionId\x12\x17\n\x0fstart_action_id\x18\x01 \x01(\x0c\x12 \n\x18start_action_id_included\x18\x02 \x01(\x08\x1a\xc8\x02\n\x18GetGroupActionsRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12N\n\x06status\x18\x03 \x01(\x0e\x32>.org.dash.platform.dapi.v0.GetGroupActionsRequest.ActionStatus\x12\x62\n\x12start_at_action_id\x18\x04 \x01(\x0b\x32\x41.org.dash.platform.dapi.v0.GetGroupActionsRequest.StartAtActionIdH\x00\x88\x01\x01\x12\x12\n\x05\x63ount\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\r\n\x05prove\x18\x06 \x01(\x08\x42\x15\n\x13_start_at_action_idB\x08\n\x06_count\"&\n\x0c\x41\x63tionStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\x42\t\n\x07version\"\xd6\x1e\n\x17GetGroupActionsResponse\x12Z\n\x02v0\x18\x01 \x01(\x0b\x32L.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0H\x00\x1a\xd3\x1d\n\x19GetGroupActionsResponseV0\x12r\n\rgroup_actions\x18\x01 \x01(\x0b\x32Y.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a[\n\tMintEvent\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x04\x12\x14\n\x0crecipient_id\x18\x02 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a[\n\tBurnEvent\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x04\x12\x14\n\x0c\x62urn_from_id\x18\x02 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1aJ\n\x0b\x46reezeEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1aL\n\rUnfreezeEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\x66\n\x17\x44\x65stroyFrozenFundsEvent\x12\x11\n\tfrozen_id\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\x64\n\x13SharedEncryptedNote\x12\x18\n\x10sender_key_index\x18\x01 \x01(\r\x12\x1b\n\x13recipient_key_index\x18\x02 \x01(\r\x12\x16\n\x0e\x65ncrypted_data\x18\x03 \x01(\x0c\x1a{\n\x15PersonalEncryptedNote\x12!\n\x19root_encryption_key_index\x18\x01 \x01(\r\x12\'\n\x1f\x64\x65rivation_encryption_key_index\x18\x02 \x01(\r\x12\x16\n\x0e\x65ncrypted_data\x18\x03 \x01(\x0c\x1a\xe9\x01\n\x14\x45mergencyActionEvent\x12\x81\x01\n\x0b\x61\x63tion_type\x18\x01 \x01(\x0e\x32l.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.EmergencyActionEvent.ActionType\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\"#\n\nActionType\x12\t\n\x05PAUSE\x10\x00\x12\n\n\x06RESUME\x10\x01\x42\x0e\n\x0c_public_note\x1a\x64\n\x16TokenConfigUpdateEvent\x12 \n\x18token_config_update_item\x18\x01 \x01(\x0c\x12\x18\n\x0bpublic_note\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_public_note\x1a\xe6\x03\n\x1eUpdateDirectPurchasePriceEvent\x12\x15\n\x0b\x66ixed_price\x18\x01 \x01(\x04H\x00\x12\x95\x01\n\x0evariable_price\x18\x02 \x01(\x0b\x32{.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEvent.PricingScheduleH\x00\x12\x18\n\x0bpublic_note\x18\x03 \x01(\tH\x01\x88\x01\x01\x1a\x33\n\x10PriceForQuantity\x12\x10\n\x08quantity\x18\x01 \x01(\x04\x12\r\n\x05price\x18\x02 \x01(\x04\x1a\xac\x01\n\x0fPricingSchedule\x12\x98\x01\n\x12price_for_quantity\x18\x01 \x03(\x0b\x32|.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEvent.PriceForQuantityB\x07\n\x05priceB\x0e\n\x0c_public_note\x1a\xfc\x02\n\x10GroupActionEvent\x12n\n\x0btoken_event\x18\x01 \x01(\x0b\x32W.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.TokenEventH\x00\x12t\n\x0e\x64ocument_event\x18\x02 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DocumentEventH\x00\x12t\n\x0e\x63ontract_event\x18\x03 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.ContractEventH\x00\x42\x0c\n\nevent_type\x1a\x8b\x01\n\rDocumentEvent\x12r\n\x06\x63reate\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DocumentCreateEventH\x00\x42\x06\n\x04type\x1a/\n\x13\x44ocumentCreateEvent\x12\x18\n\x10\x63reated_document\x18\x01 \x01(\x0c\x1a/\n\x13\x43ontractUpdateEvent\x12\x18\n\x10updated_contract\x18\x01 \x01(\x0c\x1a\x8b\x01\n\rContractEvent\x12r\n\x06update\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.ContractUpdateEventH\x00\x42\x06\n\x04type\x1a\xd1\x07\n\nTokenEvent\x12\x66\n\x04mint\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.MintEventH\x00\x12\x66\n\x04\x62urn\x18\x02 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.BurnEventH\x00\x12j\n\x06\x66reeze\x18\x03 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.FreezeEventH\x00\x12n\n\x08unfreeze\x18\x04 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UnfreezeEventH\x00\x12\x84\x01\n\x14\x64\x65stroy_frozen_funds\x18\x05 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.DestroyFrozenFundsEventH\x00\x12}\n\x10\x65mergency_action\x18\x06 \x01(\x0b\x32\x61.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.EmergencyActionEventH\x00\x12\x82\x01\n\x13token_config_update\x18\x07 \x01(\x0b\x32\x63.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.TokenConfigUpdateEventH\x00\x12\x83\x01\n\x0cupdate_price\x18\x08 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.UpdateDirectPurchasePriceEventH\x00\x42\x06\n\x04type\x1a\x93\x01\n\x10GroupActionEntry\x12\x11\n\taction_id\x18\x01 \x01(\x0c\x12l\n\x05\x65vent\x18\x02 \x01(\x0b\x32].org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionEvent\x1a\x84\x01\n\x0cGroupActions\x12t\n\rgroup_actions\x18\x01 \x03(\x0b\x32].org.dash.platform.dapi.v0.GetGroupActionsResponse.GetGroupActionsResponseV0.GroupActionEntryB\x08\n\x06resultB\t\n\x07version\"\x88\x03\n\x1cGetGroupActionSignersRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetGroupActionSignersRequest.GetGroupActionSignersRequestV0H\x00\x1a\xce\x01\n\x1eGetGroupActionSignersRequestV0\x12\x13\n\x0b\x63ontract_id\x18\x01 \x01(\x0c\x12\x1f\n\x17group_contract_position\x18\x02 \x01(\r\x12T\n\x06status\x18\x03 \x01(\x0e\x32\x44.org.dash.platform.dapi.v0.GetGroupActionSignersRequest.ActionStatus\x12\x11\n\taction_id\x18\x04 \x01(\x0c\x12\r\n\x05prove\x18\x05 \x01(\x08\"&\n\x0c\x41\x63tionStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\x42\t\n\x07version\"\x8b\x05\n\x1dGetGroupActionSignersResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0H\x00\x1a\xf6\x03\n\x1fGetGroupActionSignersResponseV0\x12\x8b\x01\n\x14group_action_signers\x18\x01 \x01(\x0b\x32k.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0.GroupActionSignersH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x35\n\x11GroupActionSigner\x12\x11\n\tsigner_id\x18\x01 \x01(\x0c\x12\r\n\x05power\x18\x02 \x01(\r\x1a\x91\x01\n\x12GroupActionSigners\x12{\n\x07signers\x18\x01 \x03(\x0b\x32j.org.dash.platform.dapi.v0.GetGroupActionSignersResponse.GetGroupActionSignersResponseV0.GroupActionSignerB\x08\n\x06resultB\t\n\x07version\"\xb5\x01\n\x15GetAddressInfoRequest\x12V\n\x02v0\x18\x01 \x01(\x0b\x32H.org.dash.platform.dapi.v0.GetAddressInfoRequest.GetAddressInfoRequestV0H\x00\x1a\x39\n\x17GetAddressInfoRequestV0\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x85\x01\n\x10\x41\x64\x64ressInfoEntry\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12J\n\x11\x62\x61lance_and_nonce\x18\x02 \x01(\x0b\x32*.org.dash.platform.dapi.v0.BalanceAndNonceH\x00\x88\x01\x01\x42\x14\n\x12_balance_and_nonce\"1\n\x0f\x42\x61lanceAndNonce\x12\x0f\n\x07\x62\x61lance\x18\x01 \x01(\x04\x12\r\n\x05nonce\x18\x02 \x01(\r\"_\n\x12\x41\x64\x64ressInfoEntries\x12I\n\x14\x61\x64\x64ress_info_entries\x18\x01 \x03(\x0b\x32+.org.dash.platform.dapi.v0.AddressInfoEntry\"m\n\x14\x41\x64\x64ressBalanceChange\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x19\n\x0bset_balance\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12\x1c\n\x0e\x61\x64\x64_to_balance\x18\x03 \x01(\x04\x42\x02\x30\x01H\x00\x42\x0b\n\toperation\"x\n\x1a\x42lockAddressBalanceChanges\x12\x18\n\x0c\x62lock_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12@\n\x07\x63hanges\x18\x02 \x03(\x0b\x32/.org.dash.platform.dapi.v0.AddressBalanceChange\"k\n\x1b\x41\x64\x64ressBalanceUpdateEntries\x12L\n\rblock_changes\x18\x01 \x03(\x0b\x32\x35.org.dash.platform.dapi.v0.BlockAddressBalanceChanges\"\xe1\x02\n\x16GetAddressInfoResponse\x12X\n\x02v0\x18\x01 \x01(\x0b\x32J.org.dash.platform.dapi.v0.GetAddressInfoResponse.GetAddressInfoResponseV0H\x00\x1a\xe1\x01\n\x18GetAddressInfoResponseV0\x12I\n\x12\x61\x64\x64ress_info_entry\x18\x01 \x01(\x0b\x32+.org.dash.platform.dapi.v0.AddressInfoEntryH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc3\x01\n\x18GetAddressesInfosRequest\x12\\\n\x02v0\x18\x01 \x01(\x0b\x32N.org.dash.platform.dapi.v0.GetAddressesInfosRequest.GetAddressesInfosRequestV0H\x00\x1a>\n\x1aGetAddressesInfosRequestV0\x12\x11\n\taddresses\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf1\x02\n\x19GetAddressesInfosResponse\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetAddressesInfosResponse.GetAddressesInfosResponseV0H\x00\x1a\xe8\x01\n\x1bGetAddressesInfosResponseV0\x12M\n\x14\x61\x64\x64ress_info_entries\x18\x01 \x01(\x0b\x32-.org.dash.platform.dapi.v0.AddressInfoEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xb5\x01\n\x1dGetAddressesTrunkStateRequest\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetAddressesTrunkStateRequest.GetAddressesTrunkStateRequestV0H\x00\x1a!\n\x1fGetAddressesTrunkStateRequestV0B\t\n\x07version\"\xaa\x02\n\x1eGetAddressesTrunkStateResponse\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetAddressesTrunkStateResponse.GetAddressesTrunkStateResponseV0H\x00\x1a\x92\x01\n GetAddressesTrunkStateResponseV0\x12/\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.Proof\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\t\n\x07version\"\xf0\x01\n\x1eGetAddressesBranchStateRequest\x12h\n\x02v0\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetAddressesBranchStateRequest.GetAddressesBranchStateRequestV0H\x00\x1aY\n GetAddressesBranchStateRequestV0\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\r\n\x05\x64\x65pth\x18\x02 \x01(\r\x12\x19\n\x11\x63heckpoint_height\x18\x03 \x01(\x04\x42\t\n\x07version\"\xd1\x01\n\x1fGetAddressesBranchStateResponse\x12j\n\x02v0\x18\x01 \x01(\x0b\x32\\.org.dash.platform.dapi.v0.GetAddressesBranchStateResponse.GetAddressesBranchStateResponseV0H\x00\x1a\x37\n!GetAddressesBranchStateResponseV0\x12\x12\n\nmerk_proof\x18\x02 \x01(\x0c\x42\t\n\x07version\"\x9e\x02\n%GetRecentAddressBalanceChangesRequest\x12v\n\x02v0\x18\x01 \x01(\x0b\x32h.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesRequest.GetRecentAddressBalanceChangesRequestV0H\x00\x1ar\n\'GetRecentAddressBalanceChangesRequestV0\x12\x18\n\x0cstart_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x02 \x01(\x08\x12\x1e\n\x16start_height_exclusive\x18\x03 \x01(\x08\x42\t\n\x07version\"\xb8\x03\n&GetRecentAddressBalanceChangesResponse\x12x\n\x02v0\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesResponse.GetRecentAddressBalanceChangesResponseV0H\x00\x1a\x88\x02\n(GetRecentAddressBalanceChangesResponseV0\x12`\n\x1e\x61\x64\x64ress_balance_update_entries\x18\x01 \x01(\x0b\x32\x36.org.dash.platform.dapi.v0.AddressBalanceUpdateEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"G\n\x16\x42lockHeightCreditEntry\x12\x18\n\x0c\x62lock_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x13\n\x07\x63redits\x18\x02 \x01(\x04\x42\x02\x30\x01\"\xb0\x01\n\x1d\x43ompactedAddressBalanceChange\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x19\n\x0bset_credits\x18\x02 \x01(\x04\x42\x02\x30\x01H\x00\x12V\n\x19\x61\x64\x64_to_credits_operations\x18\x03 \x01(\x0b\x32\x31.org.dash.platform.dapi.v0.AddToCreditsOperationsH\x00\x42\x0b\n\toperation\"\\\n\x16\x41\x64\x64ToCreditsOperations\x12\x42\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x31.org.dash.platform.dapi.v0.BlockHeightCreditEntry\"\xae\x01\n#CompactedBlockAddressBalanceChanges\x12\x1e\n\x12start_block_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x1c\n\x10\x65nd_block_height\x18\x02 \x01(\x04\x42\x02\x30\x01\x12I\n\x07\x63hanges\x18\x03 \x03(\x0b\x32\x38.org.dash.platform.dapi.v0.CompactedAddressBalanceChange\"\x87\x01\n$CompactedAddressBalanceUpdateEntries\x12_\n\x17\x63ompacted_block_changes\x18\x01 \x03(\x0b\x32>.org.dash.platform.dapi.v0.CompactedBlockAddressBalanceChanges\"\xa9\x02\n.GetRecentCompactedAddressBalanceChangesRequest\x12\x88\x01\n\x02v0\x18\x01 \x01(\x0b\x32z.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesRequest.GetRecentCompactedAddressBalanceChangesRequestV0H\x00\x1a\x61\n0GetRecentCompactedAddressBalanceChangesRequestV0\x12\x1e\n\x12start_block_height\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\xf0\x03\n/GetRecentCompactedAddressBalanceChangesResponse\x12\x8a\x01\n\x02v0\x18\x01 \x01(\x0b\x32|.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesResponse.GetRecentCompactedAddressBalanceChangesResponseV0H\x00\x1a\xa4\x02\n1GetRecentCompactedAddressBalanceChangesResponseV0\x12s\n(compacted_address_balance_update_entries\x18\x01 \x01(\x0b\x32?.org.dash.platform.dapi.v0.CompactedAddressBalanceUpdateEntriesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xf4\x01\n GetShieldedEncryptedNotesRequest\x12l\n\x02v0\x18\x01 \x01(\x0b\x32^.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesRequest.GetShieldedEncryptedNotesRequestV0H\x00\x1aW\n\"GetShieldedEncryptedNotesRequestV0\x12\x13\n\x0bstart_index\x18\x01 \x01(\x04\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\r\n\x05prove\x18\x03 \x01(\x08\x42\t\n\x07version\"\xbc\x05\n!GetShieldedEncryptedNotesResponse\x12n\n\x02v0\x18\x01 \x01(\x0b\x32`.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0H\x00\x1a\x9b\x04\n#GetShieldedEncryptedNotesResponseV0\x12\x8a\x01\n\x0f\x65ncrypted_notes\x18\x01 \x01(\x0b\x32o.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0.EncryptedNotesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1aW\n\rEncryptedNote\x12\x11\n\tnullifier\x18\x01 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x02 \x01(\x0c\x12\x16\n\x0e\x65ncrypted_note\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x1a\x91\x01\n\x0e\x45ncryptedNotes\x12\x7f\n\x07\x65ntries\x18\x01 \x03(\x0b\x32n.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse.GetShieldedEncryptedNotesResponseV0.EncryptedNoteB\x08\n\x06resultB\t\n\x07version\"\xb4\x01\n\x19GetShieldedAnchorsRequest\x12^\n\x02v0\x18\x01 \x01(\x0b\x32P.org.dash.platform.dapi.v0.GetShieldedAnchorsRequest.GetShieldedAnchorsRequestV0H\x00\x1a,\n\x1bGetShieldedAnchorsRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xb1\x03\n\x1aGetShieldedAnchorsResponse\x12`\n\x02v0\x18\x01 \x01(\x0b\x32R.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse.GetShieldedAnchorsResponseV0H\x00\x1a\xa5\x02\n\x1cGetShieldedAnchorsResponseV0\x12m\n\x07\x61nchors\x18\x01 \x01(\x0b\x32Z.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse.GetShieldedAnchorsResponseV0.AnchorsH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x1a\n\x07\x41nchors\x12\x0f\n\x07\x61nchors\x18\x01 \x03(\x0c\x42\x08\n\x06resultB\t\n\x07version\"\xd8\x01\n\"GetMostRecentShieldedAnchorRequest\x12p\n\x02v0\x18\x01 \x01(\x0b\x32\x62.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorRequest.GetMostRecentShieldedAnchorRequestV0H\x00\x1a\x35\n$GetMostRecentShieldedAnchorRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xdc\x02\n#GetMostRecentShieldedAnchorResponse\x12r\n\x02v0\x18\x01 \x01(\x0b\x32\x64.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorResponse.GetMostRecentShieldedAnchorResponseV0H\x00\x1a\xb5\x01\n%GetMostRecentShieldedAnchorResponseV0\x12\x10\n\x06\x61nchor\x18\x01 \x01(\x0cH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xbc\x01\n\x1bGetShieldedPoolStateRequest\x12\x62\n\x02v0\x18\x01 \x01(\x0b\x32T.org.dash.platform.dapi.v0.GetShieldedPoolStateRequest.GetShieldedPoolStateRequestV0H\x00\x1a.\n\x1dGetShieldedPoolStateRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xcb\x02\n\x1cGetShieldedPoolStateResponse\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedPoolStateResponse.GetShieldedPoolStateResponseV0H\x00\x1a\xb9\x01\n\x1eGetShieldedPoolStateResponseV0\x12\x1b\n\rtotal_balance\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xc0\x01\n\x1cGetShieldedNotesCountRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedNotesCountRequest.GetShieldedNotesCountRequestV0H\x00\x1a/\n\x1eGetShieldedNotesCountRequestV0\x12\r\n\x05prove\x18\x01 \x01(\x08\x42\t\n\x07version\"\xd3\x02\n\x1dGetShieldedNotesCountResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetShieldedNotesCountResponse.GetShieldedNotesCountResponseV0H\x00\x1a\xbe\x01\n\x1fGetShieldedNotesCountResponseV0\x12\x1f\n\x11total_notes_count\x18\x01 \x01(\x04\x42\x02\x30\x01H\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadataB\x08\n\x06resultB\t\n\x07version\"\xd4\x01\n\x1cGetShieldedNullifiersRequest\x12\x64\n\x02v0\x18\x01 \x01(\x0b\x32V.org.dash.platform.dapi.v0.GetShieldedNullifiersRequest.GetShieldedNullifiersRequestV0H\x00\x1a\x43\n\x1eGetShieldedNullifiersRequestV0\x12\x12\n\nnullifiers\x18\x01 \x03(\x0c\x12\r\n\x05prove\x18\x02 \x01(\x08\x42\t\n\x07version\"\x86\x05\n\x1dGetShieldedNullifiersResponse\x12\x66\n\x02v0\x18\x01 \x01(\x0b\x32X.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0H\x00\x1a\xf1\x03\n\x1fGetShieldedNullifiersResponseV0\x12\x88\x01\n\x12nullifier_statuses\x18\x01 \x01(\x0b\x32j.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0.NullifierStatusesH\x00\x12\x31\n\x05proof\x18\x02 \x01(\x0b\x32 .org.dash.platform.dapi.v0.ProofH\x00\x12=\n\x08metadata\x18\x03 \x01(\x0b\x32+.org.dash.platform.dapi.v0.ResponseMetadata\x1a\x36\n\x0fNullifierStatus\x12\x11\n\tnullifier\x18\x01 \x01(\x0c\x12\x10\n\x08is_spent\x18\x02 \x01(\x08\x1a\x8e\x01\n\x11NullifierStatuses\x12y\n\x07\x65ntries\x18\x01 \x03(\x0b\x32h.org.dash.platform.dapi.v0.GetShieldedNullifiersResponse.GetShieldedNullifiersResponseV0.NullifierStatusB\x08\n\x06resultB\t\n\x07version*Z\n\nKeyPurpose\x12\x12\n\x0e\x41UTHENTICATION\x10\x00\x12\x0e\n\nENCRYPTION\x10\x01\x12\x0e\n\nDECRYPTION\x10\x02\x12\x0c\n\x08TRANSFER\x10\x03\x12\n\n\x06VOTING\x10\x05\x32\xce\x44\n\x08Platform\x12\x93\x01\n\x18\x62roadcastStateTransition\x12:.org.dash.platform.dapi.v0.BroadcastStateTransitionRequest\x1a;.org.dash.platform.dapi.v0.BroadcastStateTransitionResponse\x12l\n\x0bgetIdentity\x12-.org.dash.platform.dapi.v0.GetIdentityRequest\x1a..org.dash.platform.dapi.v0.GetIdentityResponse\x12x\n\x0fgetIdentityKeys\x12\x31.org.dash.platform.dapi.v0.GetIdentityKeysRequest\x1a\x32.org.dash.platform.dapi.v0.GetIdentityKeysResponse\x12\x96\x01\n\x19getIdentitiesContractKeys\x12;.org.dash.platform.dapi.v0.GetIdentitiesContractKeysRequest\x1a<.org.dash.platform.dapi.v0.GetIdentitiesContractKeysResponse\x12{\n\x10getIdentityNonce\x12\x32.org.dash.platform.dapi.v0.GetIdentityNonceRequest\x1a\x33.org.dash.platform.dapi.v0.GetIdentityNonceResponse\x12\x93\x01\n\x18getIdentityContractNonce\x12:.org.dash.platform.dapi.v0.GetIdentityContractNonceRequest\x1a;.org.dash.platform.dapi.v0.GetIdentityContractNonceResponse\x12\x81\x01\n\x12getIdentityBalance\x12\x34.org.dash.platform.dapi.v0.GetIdentityBalanceRequest\x1a\x35.org.dash.platform.dapi.v0.GetIdentityBalanceResponse\x12\x8a\x01\n\x15getIdentitiesBalances\x12\x37.org.dash.platform.dapi.v0.GetIdentitiesBalancesRequest\x1a\x38.org.dash.platform.dapi.v0.GetIdentitiesBalancesResponse\x12\xa2\x01\n\x1dgetIdentityBalanceAndRevision\x12?.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionRequest\x1a@.org.dash.platform.dapi.v0.GetIdentityBalanceAndRevisionResponse\x12\xaf\x01\n#getEvonodesProposedEpochBlocksByIds\x12\x45.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByIdsRequest\x1a\x41.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse\x12\xb3\x01\n%getEvonodesProposedEpochBlocksByRange\x12G.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksByRangeRequest\x1a\x41.org.dash.platform.dapi.v0.GetEvonodesProposedEpochBlocksResponse\x12x\n\x0fgetDataContract\x12\x31.org.dash.platform.dapi.v0.GetDataContractRequest\x1a\x32.org.dash.platform.dapi.v0.GetDataContractResponse\x12\x8d\x01\n\x16getDataContractHistory\x12\x38.org.dash.platform.dapi.v0.GetDataContractHistoryRequest\x1a\x39.org.dash.platform.dapi.v0.GetDataContractHistoryResponse\x12{\n\x10getDataContracts\x12\x32.org.dash.platform.dapi.v0.GetDataContractsRequest\x1a\x33.org.dash.platform.dapi.v0.GetDataContractsResponse\x12\x81\x01\n\x12getDocumentHistory\x12\x34.org.dash.platform.dapi.v0.GetDocumentHistoryRequest\x1a\x35.org.dash.platform.dapi.v0.GetDocumentHistoryResponse\x12o\n\x0cgetDocuments\x12..org.dash.platform.dapi.v0.GetDocumentsRequest\x1a/.org.dash.platform.dapi.v0.GetDocumentsResponse\x12\x99\x01\n\x1agetIdentityByPublicKeyHash\x12<.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashRequest\x1a=.org.dash.platform.dapi.v0.GetIdentityByPublicKeyHashResponse\x12\xb4\x01\n#getIdentityByNonUniquePublicKeyHash\x12\x45.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashRequest\x1a\x46.org.dash.platform.dapi.v0.GetIdentityByNonUniquePublicKeyHashResponse\x12\x9f\x01\n\x1cwaitForStateTransitionResult\x12>.org.dash.platform.dapi.v0.WaitForStateTransitionResultRequest\x1a?.org.dash.platform.dapi.v0.WaitForStateTransitionResultResponse\x12\x81\x01\n\x12getConsensusParams\x12\x34.org.dash.platform.dapi.v0.GetConsensusParamsRequest\x1a\x35.org.dash.platform.dapi.v0.GetConsensusParamsResponse\x12\xa5\x01\n\x1egetProtocolVersionUpgradeState\x12@.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateRequest\x1a\x41.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeStateResponse\x12\xb4\x01\n#getProtocolVersionUpgradeVoteStatus\x12\x45.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusRequest\x1a\x46.org.dash.platform.dapi.v0.GetProtocolVersionUpgradeVoteStatusResponse\x12r\n\rgetEpochsInfo\x12/.org.dash.platform.dapi.v0.GetEpochsInfoRequest\x1a\x30.org.dash.platform.dapi.v0.GetEpochsInfoResponse\x12\x8d\x01\n\x16getFinalizedEpochInfos\x12\x38.org.dash.platform.dapi.v0.GetFinalizedEpochInfosRequest\x1a\x39.org.dash.platform.dapi.v0.GetFinalizedEpochInfosResponse\x12\x8a\x01\n\x15getContestedResources\x12\x37.org.dash.platform.dapi.v0.GetContestedResourcesRequest\x1a\x38.org.dash.platform.dapi.v0.GetContestedResourcesResponse\x12\xa2\x01\n\x1dgetContestedResourceVoteState\x12?.org.dash.platform.dapi.v0.GetContestedResourceVoteStateRequest\x1a@.org.dash.platform.dapi.v0.GetContestedResourceVoteStateResponse\x12\xba\x01\n%getContestedResourceVotersForIdentity\x12G.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityRequest\x1aH.org.dash.platform.dapi.v0.GetContestedResourceVotersForIdentityResponse\x12\xae\x01\n!getContestedResourceIdentityVotes\x12\x43.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesRequest\x1a\x44.org.dash.platform.dapi.v0.GetContestedResourceIdentityVotesResponse\x12\x8a\x01\n\x15getVotePollsByEndDate\x12\x37.org.dash.platform.dapi.v0.GetVotePollsByEndDateRequest\x1a\x38.org.dash.platform.dapi.v0.GetVotePollsByEndDateResponse\x12\xa5\x01\n\x1egetPrefundedSpecializedBalance\x12@.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceRequest\x1a\x41.org.dash.platform.dapi.v0.GetPrefundedSpecializedBalanceResponse\x12\x96\x01\n\x19getTotalCreditsInPlatform\x12;.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformRequest\x1a<.org.dash.platform.dapi.v0.GetTotalCreditsInPlatformResponse\x12x\n\x0fgetPathElements\x12\x31.org.dash.platform.dapi.v0.GetPathElementsRequest\x1a\x32.org.dash.platform.dapi.v0.GetPathElementsResponse\x12\x66\n\tgetStatus\x12+.org.dash.platform.dapi.v0.GetStatusRequest\x1a,.org.dash.platform.dapi.v0.GetStatusResponse\x12\x8a\x01\n\x15getCurrentQuorumsInfo\x12\x37.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoRequest\x1a\x38.org.dash.platform.dapi.v0.GetCurrentQuorumsInfoResponse\x12\x93\x01\n\x18getIdentityTokenBalances\x12:.org.dash.platform.dapi.v0.GetIdentityTokenBalancesRequest\x1a;.org.dash.platform.dapi.v0.GetIdentityTokenBalancesResponse\x12\x99\x01\n\x1agetIdentitiesTokenBalances\x12<.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesRequest\x1a=.org.dash.platform.dapi.v0.GetIdentitiesTokenBalancesResponse\x12\x8a\x01\n\x15getIdentityTokenInfos\x12\x37.org.dash.platform.dapi.v0.GetIdentityTokenInfosRequest\x1a\x38.org.dash.platform.dapi.v0.GetIdentityTokenInfosResponse\x12\x90\x01\n\x17getIdentitiesTokenInfos\x12\x39.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosRequest\x1a:.org.dash.platform.dapi.v0.GetIdentitiesTokenInfosResponse\x12{\n\x10getTokenStatuses\x12\x32.org.dash.platform.dapi.v0.GetTokenStatusesRequest\x1a\x33.org.dash.platform.dapi.v0.GetTokenStatusesResponse\x12\x9f\x01\n\x1cgetTokenDirectPurchasePrices\x12>.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesRequest\x1a?.org.dash.platform.dapi.v0.GetTokenDirectPurchasePricesResponse\x12\x87\x01\n\x14getTokenContractInfo\x12\x36.org.dash.platform.dapi.v0.GetTokenContractInfoRequest\x1a\x37.org.dash.platform.dapi.v0.GetTokenContractInfoResponse\x12\xb1\x01\n\"getTokenPreProgrammedDistributions\x12\x44.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsRequest\x1a\x45.org.dash.platform.dapi.v0.GetTokenPreProgrammedDistributionsResponse\x12\xbd\x01\n&getTokenPerpetualDistributionLastClaim\x12H.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimRequest\x1aI.org.dash.platform.dapi.v0.GetTokenPerpetualDistributionLastClaimResponse\x12\x84\x01\n\x13getTokenTotalSupply\x12\x35.org.dash.platform.dapi.v0.GetTokenTotalSupplyRequest\x1a\x36.org.dash.platform.dapi.v0.GetTokenTotalSupplyResponse\x12o\n\x0cgetGroupInfo\x12..org.dash.platform.dapi.v0.GetGroupInfoRequest\x1a/.org.dash.platform.dapi.v0.GetGroupInfoResponse\x12r\n\rgetGroupInfos\x12/.org.dash.platform.dapi.v0.GetGroupInfosRequest\x1a\x30.org.dash.platform.dapi.v0.GetGroupInfosResponse\x12x\n\x0fgetGroupActions\x12\x31.org.dash.platform.dapi.v0.GetGroupActionsRequest\x1a\x32.org.dash.platform.dapi.v0.GetGroupActionsResponse\x12\x8a\x01\n\x15getGroupActionSigners\x12\x37.org.dash.platform.dapi.v0.GetGroupActionSignersRequest\x1a\x38.org.dash.platform.dapi.v0.GetGroupActionSignersResponse\x12u\n\x0egetAddressInfo\x12\x30.org.dash.platform.dapi.v0.GetAddressInfoRequest\x1a\x31.org.dash.platform.dapi.v0.GetAddressInfoResponse\x12~\n\x11getAddressesInfos\x12\x33.org.dash.platform.dapi.v0.GetAddressesInfosRequest\x1a\x34.org.dash.platform.dapi.v0.GetAddressesInfosResponse\x12\x8d\x01\n\x16getAddressesTrunkState\x12\x38.org.dash.platform.dapi.v0.GetAddressesTrunkStateRequest\x1a\x39.org.dash.platform.dapi.v0.GetAddressesTrunkStateResponse\x12\x90\x01\n\x17getAddressesBranchState\x12\x39.org.dash.platform.dapi.v0.GetAddressesBranchStateRequest\x1a:.org.dash.platform.dapi.v0.GetAddressesBranchStateResponse\x12\xa5\x01\n\x1egetRecentAddressBalanceChanges\x12@.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesRequest\x1a\x41.org.dash.platform.dapi.v0.GetRecentAddressBalanceChangesResponse\x12\xc0\x01\n\'getRecentCompactedAddressBalanceChanges\x12I.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesRequest\x1aJ.org.dash.platform.dapi.v0.GetRecentCompactedAddressBalanceChangesResponse\x12\x96\x01\n\x19getShieldedEncryptedNotes\x12;.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesRequest\x1a<.org.dash.platform.dapi.v0.GetShieldedEncryptedNotesResponse\x12\x81\x01\n\x12getShieldedAnchors\x12\x34.org.dash.platform.dapi.v0.GetShieldedAnchorsRequest\x1a\x35.org.dash.platform.dapi.v0.GetShieldedAnchorsResponse\x12\x9c\x01\n\x1bgetMostRecentShieldedAnchor\x12=.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorRequest\x1a>.org.dash.platform.dapi.v0.GetMostRecentShieldedAnchorResponse\x12\x87\x01\n\x14getShieldedPoolState\x12\x36.org.dash.platform.dapi.v0.GetShieldedPoolStateRequest\x1a\x37.org.dash.platform.dapi.v0.GetShieldedPoolStateResponse\x12\x8a\x01\n\x15getShieldedNotesCount\x12\x37.org.dash.platform.dapi.v0.GetShieldedNotesCountRequest\x1a\x38.org.dash.platform.dapi.v0.GetShieldedNotesCountResponse\x12\x8a\x01\n\x15getShieldedNullifiers\x12\x37.org.dash.platform.dapi.v0.GetShieldedNullifiersRequest\x1a\x38.org.dash.platform.dapi.v0.GetShieldedNullifiersResponseb\x06proto3' , dependencies=[google_dot_protobuf_dot_wrappers__pb2.DESCRIPTOR,google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,]) @@ -62,8 +62,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=65799, - serialized_end=65889, + serialized_start=65831, + serialized_end=65921, ) _sym_db.RegisterEnumDescriptor(_KEYPURPOSE) @@ -345,8 +345,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=28509, - serialized_end=28582, + serialized_start=28541, + serialized_end=28614, ) _sym_db.RegisterEnumDescriptor(_GETCONTESTEDRESOURCEVOTESTATEREQUEST_GETCONTESTEDRESOURCEVOTESTATEREQUESTV0_RESULTTYPE) @@ -375,8 +375,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=29504, - serialized_end=29583, + serialized_start=29536, + serialized_end=29615, ) _sym_db.RegisterEnumDescriptor(_GETCONTESTEDRESOURCEVOTESTATERESPONSE_GETCONTESTEDRESOURCEVOTESTATERESPONSEV0_FINISHEDVOTEINFO_FINISHEDVOTEOUTCOME) @@ -405,8 +405,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=33212, - serialized_end=33273, + serialized_start=33244, + serialized_end=33305, ) _sym_db.RegisterEnumDescriptor(_GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSE_GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSEV0_RESOURCEVOTECHOICE_VOTECHOICETYPE) @@ -430,8 +430,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=51837, - serialized_end=51875, + serialized_start=51869, + serialized_end=51907, ) _sym_db.RegisterEnumDescriptor(_GETGROUPACTIONSREQUEST_ACTIONSTATUS) @@ -455,8 +455,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=53122, - serialized_end=53157, + serialized_start=53154, + serialized_end=53189, ) _sym_db.RegisterEnumDescriptor(_GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_EMERGENCYACTIONEVENT_ACTIONTYPE) @@ -480,8 +480,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=51837, - serialized_end=51875, + serialized_start=51869, + serialized_end=51907, ) _sym_db.RegisterEnumDescriptor(_GETGROUPACTIONSIGNERSREQUEST_ACTIONSTATUS) @@ -4729,6 +4729,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='in_key', full_name='org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.in_key', index=4, + number=5, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], @@ -4745,9 +4752,14 @@ index=0, containing_type=None, create_key=_descriptor._internal_create_key, fields=[]), + _descriptor.OneofDescriptor( + name='_in_key', full_name='org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry._in_key', + index=1, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), ], serialized_start=15681, - serialized_end=15771, + serialized_end=15803, ) _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRIES = _descriptor.Descriptor( @@ -4789,8 +4801,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=15774, - serialized_end=15928, + serialized_start=15806, + serialized_end=15960, ) _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RESULTDATA = _descriptor.Descriptor( @@ -4853,8 +4865,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=15931, - serialized_end=16470, + serialized_start=15963, + serialized_end=16502, ) _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1 = _descriptor.Descriptor( @@ -4904,7 +4916,7 @@ fields=[]), ], serialized_start=14183, - serialized_end=16480, + serialized_end=16512, ) _GETDOCUMENTSRESPONSE = _descriptor.Descriptor( @@ -4947,7 +4959,7 @@ fields=[]), ], serialized_start=13700, - serialized_end=16491, + serialized_end=16523, ) @@ -5020,8 +5032,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=16620, - serialized_end=16855, + serialized_start=16652, + serialized_end=16887, ) _GETDOCUMENTHISTORYREQUEST = _descriptor.Descriptor( @@ -5056,8 +5068,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=16494, - serialized_end=16866, + serialized_start=16526, + serialized_end=16898, ) @@ -5095,8 +5107,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=17272, - serialized_end=17327, + serialized_start=17304, + serialized_end=17359, ) _GETDOCUMENTHISTORYRESPONSE_GETDOCUMENTHISTORYRESPONSEV0_DOCUMENTHISTORY = _descriptor.Descriptor( @@ -5126,8 +5138,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=17330, - serialized_end=17479, + serialized_start=17362, + serialized_end=17511, ) _GETDOCUMENTHISTORYRESPONSE_GETDOCUMENTHISTORYRESPONSEV0 = _descriptor.Descriptor( @@ -5176,8 +5188,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=16998, - serialized_end=17489, + serialized_start=17030, + serialized_end=17521, ) _GETDOCUMENTHISTORYRESPONSE = _descriptor.Descriptor( @@ -5212,8 +5224,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=16869, - serialized_end=17500, + serialized_start=16901, + serialized_end=17532, ) @@ -5251,8 +5263,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=17652, - serialized_end=17729, + serialized_start=17684, + serialized_end=17761, ) _GETIDENTITYBYPUBLICKEYHASHREQUEST = _descriptor.Descriptor( @@ -5287,8 +5299,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=17503, - serialized_end=17740, + serialized_start=17535, + serialized_end=17772, ) @@ -5338,8 +5350,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=17896, - serialized_end=18078, + serialized_start=17928, + serialized_end=18110, ) _GETIDENTITYBYPUBLICKEYHASHRESPONSE = _descriptor.Descriptor( @@ -5374,8 +5386,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=17743, - serialized_end=18089, + serialized_start=17775, + serialized_end=18121, ) @@ -5425,8 +5437,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=18270, - serialized_end=18398, + serialized_start=18302, + serialized_end=18430, ) _GETIDENTITYBYNONUNIQUEPUBLICKEYHASHREQUEST = _descriptor.Descriptor( @@ -5461,8 +5473,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=18092, - serialized_end=18409, + serialized_start=18124, + serialized_end=18441, ) @@ -5498,8 +5510,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19022, - serialized_end=19076, + serialized_start=19054, + serialized_end=19108, ) _GETIDENTITYBYNONUNIQUEPUBLICKEYHASHRESPONSE_GETIDENTITYBYNONUNIQUEPUBLICKEYHASHRESPONSEV0_IDENTITYPROVEDRESPONSE = _descriptor.Descriptor( @@ -5541,8 +5553,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19079, - serialized_end=19245, + serialized_start=19111, + serialized_end=19277, ) _GETIDENTITYBYNONUNIQUEPUBLICKEYHASHRESPONSE_GETIDENTITYBYNONUNIQUEPUBLICKEYHASHRESPONSEV0 = _descriptor.Descriptor( @@ -5591,8 +5603,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=18593, - serialized_end=19255, + serialized_start=18625, + serialized_end=19287, ) _GETIDENTITYBYNONUNIQUEPUBLICKEYHASHRESPONSE = _descriptor.Descriptor( @@ -5627,8 +5639,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=18412, - serialized_end=19266, + serialized_start=18444, + serialized_end=19298, ) @@ -5666,8 +5678,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=19424, - serialized_end=19509, + serialized_start=19456, + serialized_end=19541, ) _WAITFORSTATETRANSITIONRESULTREQUEST = _descriptor.Descriptor( @@ -5702,8 +5714,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19269, - serialized_end=19520, + serialized_start=19301, + serialized_end=19552, ) @@ -5753,8 +5765,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19682, - serialized_end=19921, + serialized_start=19714, + serialized_end=19953, ) _WAITFORSTATETRANSITIONRESULTRESPONSE = _descriptor.Descriptor( @@ -5789,8 +5801,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19523, - serialized_end=19932, + serialized_start=19555, + serialized_end=19964, ) @@ -5828,8 +5840,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=20060, - serialized_end=20120, + serialized_start=20092, + serialized_end=20152, ) _GETCONSENSUSPARAMSREQUEST = _descriptor.Descriptor( @@ -5864,8 +5876,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=19935, - serialized_end=20131, + serialized_start=19967, + serialized_end=20163, ) @@ -5910,8 +5922,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=20262, - serialized_end=20342, + serialized_start=20294, + serialized_end=20374, ) _GETCONSENSUSPARAMSRESPONSE_CONSENSUSPARAMSEVIDENCE = _descriptor.Descriptor( @@ -5955,8 +5967,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=20344, - serialized_end=20442, + serialized_start=20376, + serialized_end=20474, ) _GETCONSENSUSPARAMSRESPONSE_GETCONSENSUSPARAMSRESPONSEV0 = _descriptor.Descriptor( @@ -5993,8 +6005,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=20445, - serialized_end=20663, + serialized_start=20477, + serialized_end=20695, ) _GETCONSENSUSPARAMSRESPONSE = _descriptor.Descriptor( @@ -6029,8 +6041,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=20134, - serialized_end=20674, + serialized_start=20166, + serialized_end=20706, ) @@ -6061,8 +6073,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=20838, - serialized_end=20894, + serialized_start=20870, + serialized_end=20926, ) _GETPROTOCOLVERSIONUPGRADESTATEREQUEST = _descriptor.Descriptor( @@ -6097,8 +6109,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=20677, - serialized_end=20905, + serialized_start=20709, + serialized_end=20937, ) @@ -6129,8 +6141,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=21370, - serialized_end=21520, + serialized_start=21402, + serialized_end=21552, ) _GETPROTOCOLVERSIONUPGRADESTATERESPONSE_GETPROTOCOLVERSIONUPGRADESTATERESPONSEV0_VERSIONENTRY = _descriptor.Descriptor( @@ -6167,8 +6179,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=21522, - serialized_end=21580, + serialized_start=21554, + serialized_end=21612, ) _GETPROTOCOLVERSIONUPGRADESTATERESPONSE_GETPROTOCOLVERSIONUPGRADESTATERESPONSEV0 = _descriptor.Descriptor( @@ -6217,8 +6229,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=21073, - serialized_end=21590, + serialized_start=21105, + serialized_end=21622, ) _GETPROTOCOLVERSIONUPGRADESTATERESPONSE = _descriptor.Descriptor( @@ -6253,8 +6265,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=20908, - serialized_end=21601, + serialized_start=20940, + serialized_end=21633, ) @@ -6299,8 +6311,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=21781, - serialized_end=21884, + serialized_start=21813, + serialized_end=21916, ) _GETPROTOCOLVERSIONUPGRADEVOTESTATUSREQUEST = _descriptor.Descriptor( @@ -6335,8 +6347,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=21604, - serialized_end=21895, + serialized_start=21636, + serialized_end=21927, ) @@ -6367,8 +6379,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=22398, - serialized_end=22573, + serialized_start=22430, + serialized_end=22605, ) _GETPROTOCOLVERSIONUPGRADEVOTESTATUSRESPONSE_GETPROTOCOLVERSIONUPGRADEVOTESTATUSRESPONSEV0_VERSIONSIGNAL = _descriptor.Descriptor( @@ -6405,8 +6417,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=22575, - serialized_end=22628, + serialized_start=22607, + serialized_end=22660, ) _GETPROTOCOLVERSIONUPGRADEVOTESTATUSRESPONSE_GETPROTOCOLVERSIONUPGRADEVOTESTATUSRESPONSEV0 = _descriptor.Descriptor( @@ -6455,8 +6467,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=22079, - serialized_end=22638, + serialized_start=22111, + serialized_end=22670, ) _GETPROTOCOLVERSIONUPGRADEVOTESTATUSRESPONSE = _descriptor.Descriptor( @@ -6491,8 +6503,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=21898, - serialized_end=22649, + serialized_start=21930, + serialized_end=22681, ) @@ -6544,8 +6556,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=22762, - serialized_end=22886, + serialized_start=22794, + serialized_end=22918, ) _GETEPOCHSINFOREQUEST = _descriptor.Descriptor( @@ -6580,8 +6592,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=22652, - serialized_end=22897, + serialized_start=22684, + serialized_end=22929, ) @@ -6612,8 +6624,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=23258, - serialized_end=23375, + serialized_start=23290, + serialized_end=23407, ) _GETEPOCHSINFORESPONSE_GETEPOCHSINFORESPONSEV0_EPOCHINFO = _descriptor.Descriptor( @@ -6678,8 +6690,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=23378, - serialized_end=23544, + serialized_start=23410, + serialized_end=23576, ) _GETEPOCHSINFORESPONSE_GETEPOCHSINFORESPONSEV0 = _descriptor.Descriptor( @@ -6728,8 +6740,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=23014, - serialized_end=23554, + serialized_start=23046, + serialized_end=23586, ) _GETEPOCHSINFORESPONSE = _descriptor.Descriptor( @@ -6764,8 +6776,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=22900, - serialized_end=23565, + serialized_start=22932, + serialized_end=23597, ) @@ -6824,8 +6836,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=23706, - serialized_end=23876, + serialized_start=23738, + serialized_end=23908, ) _GETFINALIZEDEPOCHINFOSREQUEST = _descriptor.Descriptor( @@ -6860,8 +6872,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=23568, - serialized_end=23887, + serialized_start=23600, + serialized_end=23919, ) @@ -6892,8 +6904,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=24313, - serialized_end=24477, + serialized_start=24345, + serialized_end=24509, ) _GETFINALIZEDEPOCHINFOSRESPONSE_GETFINALIZEDEPOCHINFOSRESPONSEV0_FINALIZEDEPOCHINFO = _descriptor.Descriptor( @@ -7007,8 +7019,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=24480, - serialized_end=25023, + serialized_start=24512, + serialized_end=25055, ) _GETFINALIZEDEPOCHINFOSRESPONSE_GETFINALIZEDEPOCHINFOSRESPONSEV0_BLOCKPROPOSER = _descriptor.Descriptor( @@ -7045,8 +7057,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=25025, - serialized_end=25082, + serialized_start=25057, + serialized_end=25114, ) _GETFINALIZEDEPOCHINFOSRESPONSE_GETFINALIZEDEPOCHINFOSRESPONSEV0 = _descriptor.Descriptor( @@ -7095,8 +7107,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=24031, - serialized_end=25092, + serialized_start=24063, + serialized_end=25124, ) _GETFINALIZEDEPOCHINFOSRESPONSE = _descriptor.Descriptor( @@ -7131,8 +7143,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=23890, - serialized_end=25103, + serialized_start=23922, + serialized_end=25135, ) @@ -7170,8 +7182,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=25598, - serialized_end=25667, + serialized_start=25630, + serialized_end=25699, ) _GETCONTESTEDRESOURCESREQUEST_GETCONTESTEDRESOURCESREQUESTV0 = _descriptor.Descriptor( @@ -7267,8 +7279,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=25241, - serialized_end=25701, + serialized_start=25273, + serialized_end=25733, ) _GETCONTESTEDRESOURCESREQUEST = _descriptor.Descriptor( @@ -7303,8 +7315,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=25106, - serialized_end=25712, + serialized_start=25138, + serialized_end=25744, ) @@ -7335,8 +7347,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=26154, - serialized_end=26214, + serialized_start=26186, + serialized_end=26246, ) _GETCONTESTEDRESOURCESRESPONSE_GETCONTESTEDRESOURCESRESPONSEV0 = _descriptor.Descriptor( @@ -7385,8 +7397,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=25853, - serialized_end=26224, + serialized_start=25885, + serialized_end=26256, ) _GETCONTESTEDRESOURCESRESPONSE = _descriptor.Descriptor( @@ -7421,8 +7433,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=25715, - serialized_end=26235, + serialized_start=25747, + serialized_end=26267, ) @@ -7460,8 +7472,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=26748, - serialized_end=26821, + serialized_start=26780, + serialized_end=26853, ) _GETVOTEPOLLSBYENDDATEREQUEST_GETVOTEPOLLSBYENDDATEREQUESTV0_ENDATTIMEINFO = _descriptor.Descriptor( @@ -7498,8 +7510,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=26823, - serialized_end=26890, + serialized_start=26855, + serialized_end=26922, ) _GETVOTEPOLLSBYENDDATEREQUEST_GETVOTEPOLLSBYENDDATEREQUESTV0 = _descriptor.Descriptor( @@ -7584,8 +7596,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=26373, - serialized_end=26949, + serialized_start=26405, + serialized_end=26981, ) _GETVOTEPOLLSBYENDDATEREQUEST = _descriptor.Descriptor( @@ -7620,8 +7632,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=26238, - serialized_end=26960, + serialized_start=26270, + serialized_end=26992, ) @@ -7659,8 +7671,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=27409, - serialized_end=27495, + serialized_start=27441, + serialized_end=27527, ) _GETVOTEPOLLSBYENDDATERESPONSE_GETVOTEPOLLSBYENDDATERESPONSEV0_SERIALIZEDVOTEPOLLSBYTIMESTAMPS = _descriptor.Descriptor( @@ -7697,8 +7709,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=27498, - serialized_end=27713, + serialized_start=27530, + serialized_end=27745, ) _GETVOTEPOLLSBYENDDATERESPONSE_GETVOTEPOLLSBYENDDATERESPONSEV0 = _descriptor.Descriptor( @@ -7747,8 +7759,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=27101, - serialized_end=27723, + serialized_start=27133, + serialized_end=27755, ) _GETVOTEPOLLSBYENDDATERESPONSE = _descriptor.Descriptor( @@ -7783,8 +7795,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=26963, - serialized_end=27734, + serialized_start=26995, + serialized_end=27766, ) @@ -7822,8 +7834,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=28423, - serialized_end=28507, + serialized_start=28455, + serialized_end=28539, ) _GETCONTESTEDRESOURCEVOTESTATEREQUEST_GETCONTESTEDRESOURCEVOTESTATEREQUESTV0 = _descriptor.Descriptor( @@ -7920,8 +7932,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=27896, - serialized_end=28621, + serialized_start=27928, + serialized_end=28653, ) _GETCONTESTEDRESOURCEVOTESTATEREQUEST = _descriptor.Descriptor( @@ -7956,8 +7968,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=27737, - serialized_end=28632, + serialized_start=27769, + serialized_end=28664, ) @@ -8029,8 +8041,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=29132, - serialized_end=29606, + serialized_start=29164, + serialized_end=29638, ) _GETCONTESTEDRESOURCEVOTESTATERESPONSE_GETCONTESTEDRESOURCEVOTESTATERESPONSEV0_CONTESTEDRESOURCECONTENDERS = _descriptor.Descriptor( @@ -8096,8 +8108,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=29609, - serialized_end=30061, + serialized_start=29641, + serialized_end=30093, ) _GETCONTESTEDRESOURCEVOTESTATERESPONSE_GETCONTESTEDRESOURCEVOTESTATERESPONSEV0_CONTENDER = _descriptor.Descriptor( @@ -8151,8 +8163,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=30063, - serialized_end=30170, + serialized_start=30095, + serialized_end=30202, ) _GETCONTESTEDRESOURCEVOTESTATERESPONSE_GETCONTESTEDRESOURCEVOTESTATERESPONSEV0 = _descriptor.Descriptor( @@ -8201,8 +8213,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=28797, - serialized_end=30180, + serialized_start=28829, + serialized_end=30212, ) _GETCONTESTEDRESOURCEVOTESTATERESPONSE = _descriptor.Descriptor( @@ -8237,8 +8249,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=28635, - serialized_end=30191, + serialized_start=28667, + serialized_end=30223, ) @@ -8276,8 +8288,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=28423, - serialized_end=28507, + serialized_start=28455, + serialized_end=28539, ) _GETCONTESTEDRESOURCEVOTERSFORIDENTITYREQUEST_GETCONTESTEDRESOURCEVOTERSFORIDENTITYREQUESTV0 = _descriptor.Descriptor( @@ -8373,8 +8385,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=30378, - serialized_end=30908, + serialized_start=30410, + serialized_end=30940, ) _GETCONTESTEDRESOURCEVOTERSFORIDENTITYREQUEST = _descriptor.Descriptor( @@ -8409,8 +8421,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=30194, - serialized_end=30919, + serialized_start=30226, + serialized_end=30951, ) @@ -8448,8 +8460,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=31459, - serialized_end=31526, + serialized_start=31491, + serialized_end=31558, ) _GETCONTESTEDRESOURCEVOTERSFORIDENTITYRESPONSE_GETCONTESTEDRESOURCEVOTERSFORIDENTITYRESPONSEV0 = _descriptor.Descriptor( @@ -8498,8 +8510,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=31109, - serialized_end=31536, + serialized_start=31141, + serialized_end=31568, ) _GETCONTESTEDRESOURCEVOTERSFORIDENTITYRESPONSE = _descriptor.Descriptor( @@ -8534,8 +8546,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=30922, - serialized_end=31547, + serialized_start=30954, + serialized_end=31579, ) @@ -8573,8 +8585,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=32096, - serialized_end=32193, + serialized_start=32128, + serialized_end=32225, ) _GETCONTESTEDRESOURCEIDENTITYVOTESREQUEST_GETCONTESTEDRESOURCEIDENTITYVOTESREQUESTV0 = _descriptor.Descriptor( @@ -8644,8 +8656,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=31721, - serialized_end=32224, + serialized_start=31753, + serialized_end=32256, ) _GETCONTESTEDRESOURCEIDENTITYVOTESREQUEST = _descriptor.Descriptor( @@ -8680,8 +8692,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=31550, - serialized_end=32235, + serialized_start=31582, + serialized_end=32267, ) @@ -8719,8 +8731,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=32738, - serialized_end=32985, + serialized_start=32770, + serialized_end=33017, ) _GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSE_GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSEV0_RESOURCEVOTECHOICE = _descriptor.Descriptor( @@ -8763,8 +8775,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=32988, - serialized_end=33289, + serialized_start=33020, + serialized_end=33321, ) _GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSE_GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSEV0_CONTESTEDRESOURCEIDENTITYVOTE = _descriptor.Descriptor( @@ -8815,8 +8827,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=33292, - serialized_end=33569, + serialized_start=33324, + serialized_end=33601, ) _GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSE_GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSEV0 = _descriptor.Descriptor( @@ -8865,8 +8877,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=32412, - serialized_end=33579, + serialized_start=32444, + serialized_end=33611, ) _GETCONTESTEDRESOURCEIDENTITYVOTESRESPONSE = _descriptor.Descriptor( @@ -8901,8 +8913,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=32238, - serialized_end=33590, + serialized_start=32270, + serialized_end=33622, ) @@ -8940,8 +8952,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=33754, - serialized_end=33822, + serialized_start=33786, + serialized_end=33854, ) _GETPREFUNDEDSPECIALIZEDBALANCEREQUEST = _descriptor.Descriptor( @@ -8976,8 +8988,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=33593, - serialized_end=33833, + serialized_start=33625, + serialized_end=33865, ) @@ -9027,8 +9039,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34001, - serialized_end=34190, + serialized_start=34033, + serialized_end=34222, ) _GETPREFUNDEDSPECIALIZEDBALANCERESPONSE = _descriptor.Descriptor( @@ -9063,8 +9075,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=33836, - serialized_end=34201, + serialized_start=33868, + serialized_end=34233, ) @@ -9095,8 +9107,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=34350, - serialized_end=34401, + serialized_start=34382, + serialized_end=34433, ) _GETTOTALCREDITSINPLATFORMREQUEST = _descriptor.Descriptor( @@ -9131,8 +9143,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34204, - serialized_end=34412, + serialized_start=34236, + serialized_end=34444, ) @@ -9182,8 +9194,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34565, - serialized_end=34749, + serialized_start=34597, + serialized_end=34781, ) _GETTOTALCREDITSINPLATFORMRESPONSE = _descriptor.Descriptor( @@ -9218,8 +9230,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34415, - serialized_end=34760, + serialized_start=34447, + serialized_end=34792, ) @@ -9264,8 +9276,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=34879, - serialized_end=34948, + serialized_start=34911, + serialized_end=34980, ) _GETPATHELEMENTSREQUEST = _descriptor.Descriptor( @@ -9300,8 +9312,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34763, - serialized_end=34959, + serialized_start=34795, + serialized_end=34991, ) @@ -9332,8 +9344,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=35332, - serialized_end=35360, + serialized_start=35364, + serialized_end=35392, ) _GETPATHELEMENTSRESPONSE_GETPATHELEMENTSRESPONSEV0 = _descriptor.Descriptor( @@ -9382,8 +9394,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=35082, - serialized_end=35370, + serialized_start=35114, + serialized_end=35402, ) _GETPATHELEMENTSRESPONSE = _descriptor.Descriptor( @@ -9418,8 +9430,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=34962, - serialized_end=35381, + serialized_start=34994, + serialized_end=35413, ) @@ -9443,8 +9455,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=35482, - serialized_end=35502, + serialized_start=35514, + serialized_end=35534, ) _GETSTATUSREQUEST = _descriptor.Descriptor( @@ -9479,8 +9491,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=35384, - serialized_end=35513, + serialized_start=35416, + serialized_end=35545, ) @@ -9535,8 +9547,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=36390, - serialized_end=36484, + serialized_start=36422, + serialized_end=36516, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_VERSION_PROTOCOL_TENDERDASH = _descriptor.Descriptor( @@ -9573,8 +9585,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=36717, - serialized_end=36757, + serialized_start=36749, + serialized_end=36789, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_VERSION_PROTOCOL_DRIVE = _descriptor.Descriptor( @@ -9618,8 +9630,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=36759, - serialized_end=36819, + serialized_start=36791, + serialized_end=36851, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_VERSION_PROTOCOL = _descriptor.Descriptor( @@ -9656,8 +9668,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=36487, - serialized_end=36819, + serialized_start=36519, + serialized_end=36851, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_VERSION = _descriptor.Descriptor( @@ -9694,8 +9706,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=36177, - serialized_end=36819, + serialized_start=36209, + serialized_end=36851, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_TIME = _descriptor.Descriptor( @@ -9761,8 +9773,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=36821, - serialized_end=36948, + serialized_start=36853, + serialized_end=36980, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_NODE = _descriptor.Descriptor( @@ -9804,8 +9816,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=36950, - serialized_end=37010, + serialized_start=36982, + serialized_end=37042, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_CHAIN = _descriptor.Descriptor( @@ -9896,8 +9908,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=37013, - serialized_end=37320, + serialized_start=37045, + serialized_end=37352, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_NETWORK = _descriptor.Descriptor( @@ -9941,8 +9953,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=37322, - serialized_end=37389, + serialized_start=37354, + serialized_end=37421, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0_STATESYNC = _descriptor.Descriptor( @@ -10021,8 +10033,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=37392, - serialized_end=37653, + serialized_start=37424, + serialized_end=37685, ) _GETSTATUSRESPONSE_GETSTATUSRESPONSEV0 = _descriptor.Descriptor( @@ -10087,8 +10099,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=35618, - serialized_end=37653, + serialized_start=35650, + serialized_end=37685, ) _GETSTATUSRESPONSE = _descriptor.Descriptor( @@ -10123,8 +10135,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=35516, - serialized_end=37664, + serialized_start=35548, + serialized_end=37696, ) @@ -10148,8 +10160,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=37801, - serialized_end=37833, + serialized_start=37833, + serialized_end=37865, ) _GETCURRENTQUORUMSINFOREQUEST = _descriptor.Descriptor( @@ -10184,8 +10196,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=37667, - serialized_end=37844, + serialized_start=37699, + serialized_end=37876, ) @@ -10230,8 +10242,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=37984, - serialized_end=38054, + serialized_start=38016, + serialized_end=38086, ) _GETCURRENTQUORUMSINFORESPONSE_VALIDATORSETV0 = _descriptor.Descriptor( @@ -10282,8 +10294,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=38057, - serialized_end=38232, + serialized_start=38089, + serialized_end=38264, ) _GETCURRENTQUORUMSINFORESPONSE_GETCURRENTQUORUMSINFORESPONSEV0 = _descriptor.Descriptor( @@ -10341,8 +10353,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=38235, - serialized_end=38509, + serialized_start=38267, + serialized_end=38541, ) _GETCURRENTQUORUMSINFORESPONSE = _descriptor.Descriptor( @@ -10377,8 +10389,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=37847, - serialized_end=38520, + serialized_start=37879, + serialized_end=38552, ) @@ -10423,8 +10435,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=38666, - serialized_end=38756, + serialized_start=38698, + serialized_end=38788, ) _GETIDENTITYTOKENBALANCESREQUEST = _descriptor.Descriptor( @@ -10459,8 +10471,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=38523, - serialized_end=38767, + serialized_start=38555, + serialized_end=38799, ) @@ -10503,8 +10515,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=39206, - serialized_end=39277, + serialized_start=39238, + serialized_end=39309, ) _GETIDENTITYTOKENBALANCESRESPONSE_GETIDENTITYTOKENBALANCESRESPONSEV0_TOKENBALANCES = _descriptor.Descriptor( @@ -10534,8 +10546,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=39280, - serialized_end=39434, + serialized_start=39312, + serialized_end=39466, ) _GETIDENTITYTOKENBALANCESRESPONSE_GETIDENTITYTOKENBALANCESRESPONSEV0 = _descriptor.Descriptor( @@ -10584,8 +10596,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=38917, - serialized_end=39444, + serialized_start=38949, + serialized_end=39476, ) _GETIDENTITYTOKENBALANCESRESPONSE = _descriptor.Descriptor( @@ -10620,8 +10632,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=38770, - serialized_end=39455, + serialized_start=38802, + serialized_end=39487, ) @@ -10666,8 +10678,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=39607, - serialized_end=39699, + serialized_start=39639, + serialized_end=39731, ) _GETIDENTITIESTOKENBALANCESREQUEST = _descriptor.Descriptor( @@ -10702,8 +10714,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=39458, - serialized_end=39710, + serialized_start=39490, + serialized_end=39742, ) @@ -10746,8 +10758,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=40178, - serialized_end=40260, + serialized_start=40210, + serialized_end=40292, ) _GETIDENTITIESTOKENBALANCESRESPONSE_GETIDENTITIESTOKENBALANCESRESPONSEV0_IDENTITYTOKENBALANCES = _descriptor.Descriptor( @@ -10777,8 +10789,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=40263, - serialized_end=40446, + serialized_start=40295, + serialized_end=40478, ) _GETIDENTITIESTOKENBALANCESRESPONSE_GETIDENTITIESTOKENBALANCESRESPONSEV0 = _descriptor.Descriptor( @@ -10827,8 +10839,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=39866, - serialized_end=40456, + serialized_start=39898, + serialized_end=40488, ) _GETIDENTITIESTOKENBALANCESRESPONSE = _descriptor.Descriptor( @@ -10863,8 +10875,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=39713, - serialized_end=40467, + serialized_start=39745, + serialized_end=40499, ) @@ -10909,8 +10921,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=40604, - serialized_end=40691, + serialized_start=40636, + serialized_end=40723, ) _GETIDENTITYTOKENINFOSREQUEST = _descriptor.Descriptor( @@ -10945,8 +10957,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=40470, - serialized_end=40702, + serialized_start=40502, + serialized_end=40734, ) @@ -10977,8 +10989,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=41116, - serialized_end=41156, + serialized_start=41148, + serialized_end=41188, ) _GETIDENTITYTOKENINFOSRESPONSE_GETIDENTITYTOKENINFOSRESPONSEV0_TOKENINFOENTRY = _descriptor.Descriptor( @@ -11020,8 +11032,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=41159, - serialized_end=41335, + serialized_start=41191, + serialized_end=41367, ) _GETIDENTITYTOKENINFOSRESPONSE_GETIDENTITYTOKENINFOSRESPONSEV0_TOKENINFOS = _descriptor.Descriptor( @@ -11051,8 +11063,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=41338, - serialized_end=41476, + serialized_start=41370, + serialized_end=41508, ) _GETIDENTITYTOKENINFOSRESPONSE_GETIDENTITYTOKENINFOSRESPONSEV0 = _descriptor.Descriptor( @@ -11101,8 +11113,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=40843, - serialized_end=41486, + serialized_start=40875, + serialized_end=41518, ) _GETIDENTITYTOKENINFOSRESPONSE = _descriptor.Descriptor( @@ -11137,8 +11149,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=40705, - serialized_end=41497, + serialized_start=40737, + serialized_end=41529, ) @@ -11183,8 +11195,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=41640, - serialized_end=41729, + serialized_start=41672, + serialized_end=41761, ) _GETIDENTITIESTOKENINFOSREQUEST = _descriptor.Descriptor( @@ -11219,8 +11231,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=41500, - serialized_end=41740, + serialized_start=41532, + serialized_end=41772, ) @@ -11251,8 +11263,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=41116, - serialized_end=41156, + serialized_start=41148, + serialized_end=41188, ) _GETIDENTITIESTOKENINFOSRESPONSE_GETIDENTITIESTOKENINFOSRESPONSEV0_TOKENINFOENTRY = _descriptor.Descriptor( @@ -11294,8 +11306,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=42227, - serialized_end=42410, + serialized_start=42259, + serialized_end=42442, ) _GETIDENTITIESTOKENINFOSRESPONSE_GETIDENTITIESTOKENINFOSRESPONSEV0_IDENTITYTOKENINFOS = _descriptor.Descriptor( @@ -11325,8 +11337,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=42413, - serialized_end=42564, + serialized_start=42445, + serialized_end=42596, ) _GETIDENTITIESTOKENINFOSRESPONSE_GETIDENTITIESTOKENINFOSRESPONSEV0 = _descriptor.Descriptor( @@ -11375,8 +11387,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=41887, - serialized_end=42574, + serialized_start=41919, + serialized_end=42606, ) _GETIDENTITIESTOKENINFOSRESPONSE = _descriptor.Descriptor( @@ -11411,8 +11423,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=41743, - serialized_end=42585, + serialized_start=41775, + serialized_end=42617, ) @@ -11450,8 +11462,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=42707, - serialized_end=42768, + serialized_start=42739, + serialized_end=42800, ) _GETTOKENSTATUSESREQUEST = _descriptor.Descriptor( @@ -11486,8 +11498,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=42588, - serialized_end=42779, + serialized_start=42620, + serialized_end=42811, ) @@ -11530,8 +11542,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=43169, - serialized_end=43237, + serialized_start=43201, + serialized_end=43269, ) _GETTOKENSTATUSESRESPONSE_GETTOKENSTATUSESRESPONSEV0_TOKENSTATUSES = _descriptor.Descriptor( @@ -11561,8 +11573,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=43240, - serialized_end=43376, + serialized_start=43272, + serialized_end=43408, ) _GETTOKENSTATUSESRESPONSE_GETTOKENSTATUSESRESPONSEV0 = _descriptor.Descriptor( @@ -11611,8 +11623,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=42905, - serialized_end=43386, + serialized_start=42937, + serialized_end=43418, ) _GETTOKENSTATUSESRESPONSE = _descriptor.Descriptor( @@ -11647,8 +11659,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=42782, - serialized_end=43397, + serialized_start=42814, + serialized_end=43429, ) @@ -11686,8 +11698,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=43555, - serialized_end=43628, + serialized_start=43587, + serialized_end=43660, ) _GETTOKENDIRECTPURCHASEPRICESREQUEST = _descriptor.Descriptor( @@ -11722,8 +11734,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=43400, - serialized_end=43639, + serialized_start=43432, + serialized_end=43671, ) @@ -11761,8 +11773,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=44129, - serialized_end=44180, + serialized_start=44161, + serialized_end=44212, ) _GETTOKENDIRECTPURCHASEPRICESRESPONSE_GETTOKENDIRECTPURCHASEPRICESRESPONSEV0_PRICINGSCHEDULE = _descriptor.Descriptor( @@ -11792,8 +11804,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=44183, - serialized_end=44350, + serialized_start=44215, + serialized_end=44382, ) _GETTOKENDIRECTPURCHASEPRICESRESPONSE_GETTOKENDIRECTPURCHASEPRICESRESPONSEV0_TOKENDIRECTPURCHASEPRICEENTRY = _descriptor.Descriptor( @@ -11842,8 +11854,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=44353, - serialized_end=44581, + serialized_start=44385, + serialized_end=44613, ) _GETTOKENDIRECTPURCHASEPRICESRESPONSE_GETTOKENDIRECTPURCHASEPRICESRESPONSEV0_TOKENDIRECTPURCHASEPRICES = _descriptor.Descriptor( @@ -11873,8 +11885,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=44584, - serialized_end=44784, + serialized_start=44616, + serialized_end=44816, ) _GETTOKENDIRECTPURCHASEPRICESRESPONSE_GETTOKENDIRECTPURCHASEPRICESRESPONSEV0 = _descriptor.Descriptor( @@ -11923,8 +11935,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=43801, - serialized_end=44794, + serialized_start=43833, + serialized_end=44826, ) _GETTOKENDIRECTPURCHASEPRICESRESPONSE = _descriptor.Descriptor( @@ -11959,8 +11971,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=43642, - serialized_end=44805, + serialized_start=43674, + serialized_end=44837, ) @@ -11998,8 +12010,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=44939, - serialized_end=45003, + serialized_start=44971, + serialized_end=45035, ) _GETTOKENCONTRACTINFOREQUEST = _descriptor.Descriptor( @@ -12034,8 +12046,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=44808, - serialized_end=45014, + serialized_start=44840, + serialized_end=45046, ) @@ -12073,8 +12085,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=45426, - serialized_end=45503, + serialized_start=45458, + serialized_end=45535, ) _GETTOKENCONTRACTINFORESPONSE_GETTOKENCONTRACTINFORESPONSEV0 = _descriptor.Descriptor( @@ -12123,8 +12135,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=45152, - serialized_end=45513, + serialized_start=45184, + serialized_end=45545, ) _GETTOKENCONTRACTINFORESPONSE = _descriptor.Descriptor( @@ -12159,8 +12171,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=45017, - serialized_end=45524, + serialized_start=45049, + serialized_end=45556, ) @@ -12215,8 +12227,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=45957, - serialized_end=46111, + serialized_start=45989, + serialized_end=46143, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSREQUEST_GETTOKENPREPROGRAMMEDDISTRIBUTIONSREQUESTV0 = _descriptor.Descriptor( @@ -12277,8 +12289,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=45701, - serialized_end=46139, + serialized_start=45733, + serialized_end=46171, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSREQUEST = _descriptor.Descriptor( @@ -12313,8 +12325,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=45527, - serialized_end=46150, + serialized_start=45559, + serialized_end=46182, ) @@ -12352,8 +12364,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=46661, - serialized_end=46723, + serialized_start=46693, + serialized_end=46755, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSE_GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSEV0_TOKENTIMEDDISTRIBUTIONENTRY = _descriptor.Descriptor( @@ -12390,8 +12402,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=46726, - serialized_end=46938, + serialized_start=46758, + serialized_end=46970, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSE_GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSEV0_TOKENDISTRIBUTIONS = _descriptor.Descriptor( @@ -12421,8 +12433,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=46941, - serialized_end=47136, + serialized_start=46973, + serialized_end=47168, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSE_GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSEV0 = _descriptor.Descriptor( @@ -12471,8 +12483,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=46331, - serialized_end=47146, + serialized_start=46363, + serialized_end=47178, ) _GETTOKENPREPROGRAMMEDDISTRIBUTIONSRESPONSE = _descriptor.Descriptor( @@ -12507,8 +12519,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=46153, - serialized_end=47157, + serialized_start=46185, + serialized_end=47189, ) @@ -12546,8 +12558,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=47346, - serialized_end=47419, + serialized_start=47378, + serialized_end=47451, ) _GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMREQUEST_GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMREQUESTV0 = _descriptor.Descriptor( @@ -12603,8 +12615,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=47422, - serialized_end=47663, + serialized_start=47454, + serialized_end=47695, ) _GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMREQUEST = _descriptor.Descriptor( @@ -12639,8 +12651,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=47160, - serialized_end=47674, + serialized_start=47192, + serialized_end=47706, ) @@ -12697,8 +12709,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=48195, - serialized_end=48315, + serialized_start=48227, + serialized_end=48347, ) _GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMRESPONSE_GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMRESPONSEV0 = _descriptor.Descriptor( @@ -12747,8 +12759,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=47867, - serialized_end=48325, + serialized_start=47899, + serialized_end=48357, ) _GETTOKENPERPETUALDISTRIBUTIONLASTCLAIMRESPONSE = _descriptor.Descriptor( @@ -12783,8 +12795,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=47677, - serialized_end=48336, + serialized_start=47709, + serialized_end=48368, ) @@ -12822,8 +12834,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=48467, - serialized_end=48530, + serialized_start=48499, + serialized_end=48562, ) _GETTOKENTOTALSUPPLYREQUEST = _descriptor.Descriptor( @@ -12858,8 +12870,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=48339, - serialized_end=48541, + serialized_start=48371, + serialized_end=48573, ) @@ -12904,8 +12916,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=48962, - serialized_end=49082, + serialized_start=48994, + serialized_end=49114, ) _GETTOKENTOTALSUPPLYRESPONSE_GETTOKENTOTALSUPPLYRESPONSEV0 = _descriptor.Descriptor( @@ -12954,8 +12966,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=48676, - serialized_end=49092, + serialized_start=48708, + serialized_end=49124, ) _GETTOKENTOTALSUPPLYRESPONSE = _descriptor.Descriptor( @@ -12990,8 +13002,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=48544, - serialized_end=49103, + serialized_start=48576, + serialized_end=49135, ) @@ -13036,8 +13048,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=49213, - serialized_end=49305, + serialized_start=49245, + serialized_end=49337, ) _GETGROUPINFOREQUEST = _descriptor.Descriptor( @@ -13072,8 +13084,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=49106, - serialized_end=49316, + serialized_start=49138, + serialized_end=49348, ) @@ -13111,8 +13123,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=49674, - serialized_end=49726, + serialized_start=49706, + serialized_end=49758, ) _GETGROUPINFORESPONSE_GETGROUPINFORESPONSEV0_GROUPINFOENTRY = _descriptor.Descriptor( @@ -13149,8 +13161,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=49729, - serialized_end=49881, + serialized_start=49761, + serialized_end=49913, ) _GETGROUPINFORESPONSE_GETGROUPINFORESPONSEV0_GROUPINFO = _descriptor.Descriptor( @@ -13185,8 +13197,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=49884, - serialized_end=50022, + serialized_start=49916, + serialized_end=50054, ) _GETGROUPINFORESPONSE_GETGROUPINFORESPONSEV0 = _descriptor.Descriptor( @@ -13235,8 +13247,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=49430, - serialized_end=50032, + serialized_start=49462, + serialized_end=50064, ) _GETGROUPINFORESPONSE = _descriptor.Descriptor( @@ -13271,8 +13283,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=49319, - serialized_end=50043, + serialized_start=49351, + serialized_end=50075, ) @@ -13310,8 +13322,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=50156, - serialized_end=50273, + serialized_start=50188, + serialized_end=50305, ) _GETGROUPINFOSREQUEST_GETGROUPINFOSREQUESTV0 = _descriptor.Descriptor( @@ -13372,8 +13384,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=50276, - serialized_end=50528, + serialized_start=50308, + serialized_end=50560, ) _GETGROUPINFOSREQUEST = _descriptor.Descriptor( @@ -13408,8 +13420,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=50046, - serialized_end=50539, + serialized_start=50078, + serialized_end=50571, ) @@ -13447,8 +13459,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=49674, - serialized_end=49726, + serialized_start=49706, + serialized_end=49758, ) _GETGROUPINFOSRESPONSE_GETGROUPINFOSRESPONSEV0_GROUPPOSITIONINFOENTRY = _descriptor.Descriptor( @@ -13492,8 +13504,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=50960, - serialized_end=51155, + serialized_start=50992, + serialized_end=51187, ) _GETGROUPINFOSRESPONSE_GETGROUPINFOSRESPONSEV0_GROUPINFOS = _descriptor.Descriptor( @@ -13523,8 +13535,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=51158, - serialized_end=51288, + serialized_start=51190, + serialized_end=51320, ) _GETGROUPINFOSRESPONSE_GETGROUPINFOSRESPONSEV0 = _descriptor.Descriptor( @@ -13573,8 +13585,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=50656, - serialized_end=51298, + serialized_start=50688, + serialized_end=51330, ) _GETGROUPINFOSRESPONSE = _descriptor.Descriptor( @@ -13609,8 +13621,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=50542, - serialized_end=51309, + serialized_start=50574, + serialized_end=51341, ) @@ -13648,8 +13660,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=51428, - serialized_end=51504, + serialized_start=51460, + serialized_end=51536, ) _GETGROUPACTIONSREQUEST_GETGROUPACTIONSREQUESTV0 = _descriptor.Descriptor( @@ -13724,8 +13736,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=51507, - serialized_end=51835, + serialized_start=51539, + serialized_end=51867, ) _GETGROUPACTIONSREQUEST = _descriptor.Descriptor( @@ -13761,8 +13773,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=51312, - serialized_end=51886, + serialized_start=51344, + serialized_end=51918, ) @@ -13812,8 +13824,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52268, - serialized_end=52359, + serialized_start=52300, + serialized_end=52391, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_BURNEVENT = _descriptor.Descriptor( @@ -13862,8 +13874,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52361, - serialized_end=52452, + serialized_start=52393, + serialized_end=52484, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_FREEZEEVENT = _descriptor.Descriptor( @@ -13905,8 +13917,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52454, - serialized_end=52528, + serialized_start=52486, + serialized_end=52560, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_UNFREEZEEVENT = _descriptor.Descriptor( @@ -13948,8 +13960,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52530, - serialized_end=52606, + serialized_start=52562, + serialized_end=52638, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_DESTROYFROZENFUNDSEVENT = _descriptor.Descriptor( @@ -13998,8 +14010,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52608, - serialized_end=52710, + serialized_start=52640, + serialized_end=52742, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_SHAREDENCRYPTEDNOTE = _descriptor.Descriptor( @@ -14043,8 +14055,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=52712, - serialized_end=52812, + serialized_start=52744, + serialized_end=52844, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_PERSONALENCRYPTEDNOTE = _descriptor.Descriptor( @@ -14088,8 +14100,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=52814, - serialized_end=52937, + serialized_start=52846, + serialized_end=52969, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_EMERGENCYACTIONEVENT = _descriptor.Descriptor( @@ -14132,8 +14144,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52940, - serialized_end=53173, + serialized_start=52972, + serialized_end=53205, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_TOKENCONFIGUPDATEEVENT = _descriptor.Descriptor( @@ -14175,8 +14187,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=53175, - serialized_end=53275, + serialized_start=53207, + serialized_end=53307, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_UPDATEDIRECTPURCHASEPRICEEVENT_PRICEFORQUANTITY = _descriptor.Descriptor( @@ -14213,8 +14225,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=44129, - serialized_end=44180, + serialized_start=44161, + serialized_end=44212, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_UPDATEDIRECTPURCHASEPRICEEVENT_PRICINGSCHEDULE = _descriptor.Descriptor( @@ -14244,8 +14256,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=53567, - serialized_end=53739, + serialized_start=53599, + serialized_end=53771, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_UPDATEDIRECTPURCHASEPRICEEVENT = _descriptor.Descriptor( @@ -14299,8 +14311,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=53278, - serialized_end=53764, + serialized_start=53310, + serialized_end=53796, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_GROUPACTIONEVENT = _descriptor.Descriptor( @@ -14349,8 +14361,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=53767, - serialized_end=54147, + serialized_start=53799, + serialized_end=54179, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_DOCUMENTEVENT = _descriptor.Descriptor( @@ -14385,8 +14397,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=54150, - serialized_end=54289, + serialized_start=54182, + serialized_end=54321, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_DOCUMENTCREATEEVENT = _descriptor.Descriptor( @@ -14416,8 +14428,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=54291, - serialized_end=54338, + serialized_start=54323, + serialized_end=54370, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_CONTRACTUPDATEEVENT = _descriptor.Descriptor( @@ -14447,8 +14459,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=54340, - serialized_end=54387, + serialized_start=54372, + serialized_end=54419, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_CONTRACTEVENT = _descriptor.Descriptor( @@ -14483,8 +14495,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=54390, - serialized_end=54529, + serialized_start=54422, + serialized_end=54561, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_TOKENEVENT = _descriptor.Descriptor( @@ -14568,8 +14580,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=54532, - serialized_end=55509, + serialized_start=54564, + serialized_end=55541, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_GROUPACTIONENTRY = _descriptor.Descriptor( @@ -14606,8 +14618,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=55512, - serialized_end=55659, + serialized_start=55544, + serialized_end=55691, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0_GROUPACTIONS = _descriptor.Descriptor( @@ -14637,8 +14649,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=55662, - serialized_end=55794, + serialized_start=55694, + serialized_end=55826, ) _GETGROUPACTIONSRESPONSE_GETGROUPACTIONSRESPONSEV0 = _descriptor.Descriptor( @@ -14687,8 +14699,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=52009, - serialized_end=55804, + serialized_start=52041, + serialized_end=55836, ) _GETGROUPACTIONSRESPONSE = _descriptor.Descriptor( @@ -14723,8 +14735,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=51889, - serialized_end=55815, + serialized_start=51921, + serialized_end=55847, ) @@ -14783,8 +14795,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=55953, - serialized_end=56159, + serialized_start=55985, + serialized_end=56191, ) _GETGROUPACTIONSIGNERSREQUEST = _descriptor.Descriptor( @@ -14820,8 +14832,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=55818, - serialized_end=56210, + serialized_start=55850, + serialized_end=56242, ) @@ -14859,8 +14871,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=56642, - serialized_end=56695, + serialized_start=56674, + serialized_end=56727, ) _GETGROUPACTIONSIGNERSRESPONSE_GETGROUPACTIONSIGNERSRESPONSEV0_GROUPACTIONSIGNERS = _descriptor.Descriptor( @@ -14890,8 +14902,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=56698, - serialized_end=56843, + serialized_start=56730, + serialized_end=56875, ) _GETGROUPACTIONSIGNERSRESPONSE_GETGROUPACTIONSIGNERSRESPONSEV0 = _descriptor.Descriptor( @@ -14940,8 +14952,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=56351, - serialized_end=56853, + serialized_start=56383, + serialized_end=56885, ) _GETGROUPACTIONSIGNERSRESPONSE = _descriptor.Descriptor( @@ -14976,8 +14988,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=56213, - serialized_end=56864, + serialized_start=56245, + serialized_end=56896, ) @@ -15015,8 +15027,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=56980, - serialized_end=57037, + serialized_start=57012, + serialized_end=57069, ) _GETADDRESSINFOREQUEST = _descriptor.Descriptor( @@ -15051,8 +15063,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=56867, - serialized_end=57048, + serialized_start=56899, + serialized_end=57080, ) @@ -15095,8 +15107,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=57051, - serialized_end=57184, + serialized_start=57083, + serialized_end=57216, ) @@ -15134,8 +15146,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=57186, - serialized_end=57235, + serialized_start=57218, + serialized_end=57267, ) @@ -15166,8 +15178,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=57237, - serialized_end=57332, + serialized_start=57269, + serialized_end=57364, ) @@ -15217,8 +15229,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=57334, - serialized_end=57443, + serialized_start=57366, + serialized_end=57475, ) @@ -15256,8 +15268,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=57445, - serialized_end=57565, + serialized_start=57477, + serialized_end=57597, ) @@ -15288,8 +15300,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=57567, - serialized_end=57674, + serialized_start=57599, + serialized_end=57706, ) @@ -15339,8 +15351,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=57794, - serialized_end=58019, + serialized_start=57826, + serialized_end=58051, ) _GETADDRESSINFORESPONSE = _descriptor.Descriptor( @@ -15375,8 +15387,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=57677, - serialized_end=58030, + serialized_start=57709, + serialized_end=58062, ) @@ -15414,8 +15426,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=58155, - serialized_end=58217, + serialized_start=58187, + serialized_end=58249, ) _GETADDRESSESINFOSREQUEST = _descriptor.Descriptor( @@ -15450,8 +15462,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=58033, - serialized_end=58228, + serialized_start=58065, + serialized_end=58260, ) @@ -15501,8 +15513,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=58357, - serialized_end=58589, + serialized_start=58389, + serialized_end=58621, ) _GETADDRESSESINFOSRESPONSE = _descriptor.Descriptor( @@ -15537,8 +15549,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=58231, - serialized_end=58600, + serialized_start=58263, + serialized_end=58632, ) @@ -15562,8 +15574,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=58740, - serialized_end=58773, + serialized_start=58772, + serialized_end=58805, ) _GETADDRESSESTRUNKSTATEREQUEST = _descriptor.Descriptor( @@ -15598,8 +15610,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=58603, - serialized_end=58784, + serialized_start=58635, + serialized_end=58816, ) @@ -15637,8 +15649,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=58928, - serialized_end=59074, + serialized_start=58960, + serialized_end=59106, ) _GETADDRESSESTRUNKSTATERESPONSE = _descriptor.Descriptor( @@ -15673,8 +15685,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=58787, - serialized_end=59085, + serialized_start=58819, + serialized_end=59117, ) @@ -15719,8 +15731,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=59228, - serialized_end=59317, + serialized_start=59260, + serialized_end=59349, ) _GETADDRESSESBRANCHSTATEREQUEST = _descriptor.Descriptor( @@ -15755,8 +15767,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=59088, - serialized_end=59328, + serialized_start=59120, + serialized_end=59360, ) @@ -15787,8 +15799,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=59474, - serialized_end=59529, + serialized_start=59506, + serialized_end=59561, ) _GETADDRESSESBRANCHSTATERESPONSE = _descriptor.Descriptor( @@ -15823,8 +15835,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=59331, - serialized_end=59540, + serialized_start=59363, + serialized_end=59572, ) @@ -15869,8 +15881,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=59704, - serialized_end=59818, + serialized_start=59736, + serialized_end=59850, ) _GETRECENTADDRESSBALANCECHANGESREQUEST = _descriptor.Descriptor( @@ -15905,8 +15917,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=59543, - serialized_end=59829, + serialized_start=59575, + serialized_end=59861, ) @@ -15956,8 +15968,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=59997, - serialized_end=60261, + serialized_start=60029, + serialized_end=60293, ) _GETRECENTADDRESSBALANCECHANGESRESPONSE = _descriptor.Descriptor( @@ -15992,8 +16004,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=59832, - serialized_end=60272, + serialized_start=59864, + serialized_end=60304, ) @@ -16031,8 +16043,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=60274, - serialized_end=60345, + serialized_start=60306, + serialized_end=60377, ) @@ -16082,8 +16094,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=60348, - serialized_end=60524, + serialized_start=60380, + serialized_end=60556, ) @@ -16114,8 +16126,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=60526, - serialized_end=60618, + serialized_start=60558, + serialized_end=60650, ) @@ -16160,8 +16172,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=60621, - serialized_end=60795, + serialized_start=60653, + serialized_end=60827, ) @@ -16192,8 +16204,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=60798, - serialized_end=60933, + serialized_start=60830, + serialized_end=60965, ) @@ -16231,8 +16243,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=61125, - serialized_end=61222, + serialized_start=61157, + serialized_end=61254, ) _GETRECENTCOMPACTEDADDRESSBALANCECHANGESREQUEST = _descriptor.Descriptor( @@ -16267,8 +16279,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=60936, - serialized_end=61233, + serialized_start=60968, + serialized_end=61265, ) @@ -16318,8 +16330,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=61429, - serialized_end=61721, + serialized_start=61461, + serialized_end=61753, ) _GETRECENTCOMPACTEDADDRESSBALANCECHANGESRESPONSE = _descriptor.Descriptor( @@ -16354,8 +16366,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=61236, - serialized_end=61732, + serialized_start=61268, + serialized_end=61764, ) @@ -16400,8 +16412,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=61881, - serialized_end=61968, + serialized_start=61913, + serialized_end=62000, ) _GETSHIELDEDENCRYPTEDNOTESREQUEST = _descriptor.Descriptor( @@ -16436,8 +16448,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=61735, - serialized_end=61979, + serialized_start=61767, + serialized_end=62011, ) @@ -16489,8 +16501,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=62426, - serialized_end=62513, + serialized_start=62458, + serialized_end=62545, ) _GETSHIELDEDENCRYPTEDNOTESRESPONSE_GETSHIELDEDENCRYPTEDNOTESRESPONSEV0_ENCRYPTEDNOTES = _descriptor.Descriptor( @@ -16520,8 +16532,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=62516, - serialized_end=62661, + serialized_start=62548, + serialized_end=62693, ) _GETSHIELDEDENCRYPTEDNOTESRESPONSE_GETSHIELDEDENCRYPTEDNOTESRESPONSEV0 = _descriptor.Descriptor( @@ -16570,8 +16582,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=62132, - serialized_end=62671, + serialized_start=62164, + serialized_end=62703, ) _GETSHIELDEDENCRYPTEDNOTESRESPONSE = _descriptor.Descriptor( @@ -16606,8 +16618,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=61982, - serialized_end=62682, + serialized_start=62014, + serialized_end=62714, ) @@ -16638,8 +16650,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=62810, - serialized_end=62854, + serialized_start=62842, + serialized_end=62886, ) _GETSHIELDEDANCHORSREQUEST = _descriptor.Descriptor( @@ -16674,8 +16686,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=62685, - serialized_end=62865, + serialized_start=62717, + serialized_end=62897, ) @@ -16706,8 +16718,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=63254, - serialized_end=63280, + serialized_start=63286, + serialized_end=63312, ) _GETSHIELDEDANCHORSRESPONSE_GETSHIELDEDANCHORSRESPONSEV0 = _descriptor.Descriptor( @@ -16756,8 +16768,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=62997, - serialized_end=63290, + serialized_start=63029, + serialized_end=63322, ) _GETSHIELDEDANCHORSRESPONSE = _descriptor.Descriptor( @@ -16792,8 +16804,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=62868, - serialized_end=63301, + serialized_start=62900, + serialized_end=63333, ) @@ -16824,8 +16836,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=63456, - serialized_end=63509, + serialized_start=63488, + serialized_end=63541, ) _GETMOSTRECENTSHIELDEDANCHORREQUEST = _descriptor.Descriptor( @@ -16860,8 +16872,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=63304, - serialized_end=63520, + serialized_start=63336, + serialized_end=63552, ) @@ -16911,8 +16923,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=63679, - serialized_end=63860, + serialized_start=63711, + serialized_end=63892, ) _GETMOSTRECENTSHIELDEDANCHORRESPONSE = _descriptor.Descriptor( @@ -16947,8 +16959,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=63523, - serialized_end=63871, + serialized_start=63555, + serialized_end=63903, ) @@ -16979,8 +16991,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=64005, - serialized_end=64051, + serialized_start=64037, + serialized_end=64083, ) _GETSHIELDEDPOOLSTATEREQUEST = _descriptor.Descriptor( @@ -17015,8 +17027,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=63874, - serialized_end=64062, + serialized_start=63906, + serialized_end=64094, ) @@ -17066,8 +17078,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64200, - serialized_end=64385, + serialized_start=64232, + serialized_end=64417, ) _GETSHIELDEDPOOLSTATERESPONSE = _descriptor.Descriptor( @@ -17102,8 +17114,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64065, - serialized_end=64396, + serialized_start=64097, + serialized_end=64428, ) @@ -17134,8 +17146,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=64533, - serialized_end=64580, + serialized_start=64565, + serialized_end=64612, ) _GETSHIELDEDNOTESCOUNTREQUEST = _descriptor.Descriptor( @@ -17170,8 +17182,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64399, - serialized_end=64591, + serialized_start=64431, + serialized_end=64623, ) @@ -17221,8 +17233,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64732, - serialized_end=64922, + serialized_start=64764, + serialized_end=64954, ) _GETSHIELDEDNOTESCOUNTRESPONSE = _descriptor.Descriptor( @@ -17257,8 +17269,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64594, - serialized_end=64933, + serialized_start=64626, + serialized_end=64965, ) @@ -17296,8 +17308,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=65070, - serialized_end=65137, + serialized_start=65102, + serialized_end=65169, ) _GETSHIELDEDNULLIFIERSREQUEST = _descriptor.Descriptor( @@ -17332,8 +17344,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=64936, - serialized_end=65148, + serialized_start=64968, + serialized_end=65180, ) @@ -17371,8 +17383,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=65577, - serialized_end=65631, + serialized_start=65609, + serialized_end=65663, ) _GETSHIELDEDNULLIFIERSRESPONSE_GETSHIELDEDNULLIFIERSRESPONSEV0_NULLIFIERSTATUSES = _descriptor.Descriptor( @@ -17402,8 +17414,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=65634, - serialized_end=65776, + serialized_start=65666, + serialized_end=65808, ) _GETSHIELDEDNULLIFIERSRESPONSE_GETSHIELDEDNULLIFIERSRESPONSEV0 = _descriptor.Descriptor( @@ -17452,8 +17464,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=65289, - serialized_end=65786, + serialized_start=65321, + serialized_end=65818, ) _GETSHIELDEDNULLIFIERSRESPONSE = _descriptor.Descriptor( @@ -17488,8 +17500,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=65151, - serialized_end=65797, + serialized_start=65183, + serialized_end=65829, ) _GETIDENTITYREQUEST_GETIDENTITYREQUESTV0.containing_type = _GETIDENTITYREQUEST @@ -17939,6 +17951,9 @@ _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.oneofs_by_name['value'].fields.append( _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.fields_by_name['avg']) _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.fields_by_name['avg'].containing_oneof = _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.oneofs_by_name['value'] +_GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.oneofs_by_name['_in_key'].fields.append( + _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.fields_by_name['in_key']) +_GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.fields_by_name['in_key'].containing_oneof = _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY.oneofs_by_name['_in_key'] _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRIES.fields_by_name['entries'].message_type = _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRY _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRIES.containing_type = _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1 _GETDOCUMENTSRESPONSE_GETDOCUMENTSRESPONSEV1_RANKEDENTRIES.oneofs_by_name['_skipped'].fields.append( @@ -22480,8 +22495,8 @@ index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=65892, - serialized_end=74674, + serialized_start=65924, + serialized_end=74706, methods=[ _descriptor.MethodDescriptor( name='broadcastStateTransition', diff --git a/packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts index f66a16306e0..fa2743f5ff4 100644 --- a/packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts +++ b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts @@ -3224,6 +3224,13 @@ export namespace GetDocumentsResponse { getAvg(): number; setAvg(value: number): void; + hasInKey(): boolean; + clearInKey(): void; + getInKey(): Uint8Array | string; + getInKey_asU8(): Uint8Array; + getInKey_asB64(): string; + setInKey(value: Uint8Array | string): void; + getValueCase(): RankedEntry.ValueCase; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): RankedEntry.AsObject; @@ -3241,6 +3248,7 @@ export namespace GetDocumentsResponse { count: string, sum: string, avg: number, + inKey: Uint8Array | string, } export enum ValueCase { diff --git a/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js index ad740f8e1c9..fb135455827 100644 --- a/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js +++ b/packages/dapi-grpc/clients/platform/v0/web/platform_pb.js @@ -30660,7 +30660,8 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank key: msg.getKey_asB64(), count: jspb.Message.getFieldWithDefault(msg, 2, "0"), sum: jspb.Message.getFieldWithDefault(msg, 3, "0"), - avg: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0) + avg: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), + inKey: msg.getInKey_asB64() }; if (includeInstance) { @@ -30713,6 +30714,10 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank var value = /** @type {number} */ (reader.readDouble()); msg.setAvg(value); break; + case 5: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setInKey(value); + break; default: reader.skipField(); break; @@ -30770,6 +30775,13 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank f ); } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 5)); + if (f != null) { + writer.writeBytes( + 5, + f + ); + } }; @@ -30923,6 +30935,66 @@ proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.Rank }; +/** + * optional bytes in_key = 5; + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** + * optional bytes in_key = 5; + * This is a type-conversion wrapper around `getInKey()` + * @return {string} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getInKey())); +}; + + +/** + * optional bytes in_key = 5; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getInKey()` + * @return {!Uint8Array} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.getInKey_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getInKey())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.setInKey = function(value) { + return jspb.Message.setField(this, 5, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry} returns this + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.clearInKey = function() { + return jspb.Message.setField(this, 5, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.org.dash.platform.dapi.v0.GetDocumentsResponse.GetDocumentsResponseV1.RankedEntry.prototype.hasInKey = function() { + return jspb.Message.getField(this, 5) != null; +}; + + /** * List of repeated fields within this message type. diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index adb8f565c9d..8ba1b18a812 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -905,16 +905,16 @@ message GetDocumentsRequest { // - a is the In field AND b is the range field, in that order → existing compound distinct shape; entries carry both `in_key` (= a's value) and `key` (= b's value). // // `select=, group_by=[p], order_by=[]` (protocol v14+) — **ranked mode**: - // - exactly one `group_by` property, exactly one `order_by` clause naming the select's aggregate (`f` for `SUM(f)` / `AVG(f)`, the `$count` sentinel for `COUNT(*)`), a `limit` in `1 ..= 100`, an optional `offset`, and no `having` / `start_at`, on an index declaring the matching `rankedCountable` / `rankedSummable` / `rankedAverageable` axis → ranked executor, answered in `ResultData.ranked`. On a single-property ranked index no `where` is accepted; on a compound ranked index every leading index property must be pinned with an `EQUAL` where clause (one per property, `group_by` names the trailing property), selecting which prefix's ranking is read. + // - exactly one `group_by` property, exactly one `order_by` clause naming the select's aggregate (`f` for `SUM(f)` / `AVG(f)`, the `$count` sentinel for `COUNT(*)`), a `limit` in `1 ..= 100`, an optional `offset`, and no `having` / `start_at`, on an index declaring the matching `rankedCountable` / `rankedSummable` / `rankedAverageable` axis → ranked executor, answered in `ResultData.ranked`. On a single-property ranked index no `where` is accepted; on a compound ranked index every leading index property must be pinned (one clause per property, `group_by` names the trailing property) — `EQUAL` pins one prefix, and **at most one** clause may be `IN` (2..=10 distinct elements, `null` legal; a single-element `IN` normalizes to the equality pin; an element whose prefix was never written — at any depth of its pinned chain — contributes an empty branch, union semantics), fanning the walk out across one prefix branch per element and merging by `(aggregate, encoded prefix, group key)`; merged entries carry `in_key`. A non-zero `offset` is rejected together with `IN` (rank-skip is per-secondary; `OFFSET 0` is the offset-free request). // - `DESC` is the "top n" reading (walk the axis from the largest aggregate down), `ASC` the "bottom n" reading. Worked example: `SELECT AVG(grade) GROUP BY restaurantId ORDER BY grade DESC LIMIT 1 OFFSET 4` is the 5th-best restaurant. // // `select=, group_by=[p], having=[ ]` (protocol v14+) — **having-range mode**: - // - exactly one `group_by` property, exactly one `having` clause whose aggregate is the select's aggregate, an operator describing one contiguous range (`EQUAL`, `GREATER_THAN[_OR_EQUALS]`, `LESS_THAN[_OR_EQUALS]`, `BETWEEN*`; `NOT_EQUAL` / `IN` rejected), a `limit` in `1 ..= 100`, an optional `order_by` naming the same aggregate (walk direction; ascending by default), and no `offset` / `start_at` / `start_after`, on an index declaring the matching ranked axis → having-range executor, answered in `ResultData.ranked`. `where` follows the same rule as ranked mode: none on a single-property ranked index; exactly one `EQUAL` pin per leading index property on a compound ranked index. + // - exactly one `group_by` property, exactly one `having` clause whose aggregate is the select's aggregate, an operator describing one contiguous range (`EQUAL`, `GREATER_THAN[_OR_EQUALS]`, `LESS_THAN[_OR_EQUALS]`, `BETWEEN*`; `NOT_EQUAL` / `IN` rejected), a `limit` in `1 ..= 100`, an optional `order_by` naming the same aggregate (walk direction; ascending by default), and no `offset` / `start_at` / `start_after`, on an index declaring the matching ranked axis → having-range executor, answered in `ResultData.ranked`. `where` follows the same rule as ranked mode: none on a single-property ranked index; exactly one pin per leading index property on a compound ranked index, at most one of them an `IN` (2..=10 distinct elements; a never-written element contributes an empty branch) that fans the bound out across prefix branches and merges, entries carrying `in_key`. // - no offset or cursor pagination: a page cut at `limit` continues only by tightening the bound past the last *distinct* aggregate value seen; a cut inside a tie (several groups sharing the boundary aggregate) cannot be continued, so size `limit` above the widest expected tie. // // **Rejected shapes** (return `Unsupported`): - // - any non-empty `having` on protocol v13 and earlier; at v14+, any `having` shape outside having-range mode above (multiple clauses, an aggregate other than the select's, `NOT_EQUAL` / `IN`, a `where` shape other than the compound-index equality pins above, or a carried `offset` / cursor). - // - at v14+: a ranked-shaped request carrying a `where` shape other than the compound-index equality pins above (a non-`EQUAL` operator, a repeated or non-leading property, or a missing pin), a `start_at` / `start_after` cursor, more than one `order_by`, or an `order_by` naming anything but the selected aggregate. + // - any non-empty `having` on protocol v13 and earlier; at v14+, any `having` shape outside having-range mode above (multiple clauses, an aggregate other than the select's, `NOT_EQUAL` / `IN` as the having operator, a `where` shape other than the compound-index prefix pins above — equality pins plus at most one bounded `IN` — or a carried `offset` / cursor). + // - at v14+: a ranked-shaped request carrying a `where` shape other than the compound-index prefix pins above (an operator other than `EQUAL` or the one permitted `IN`, more than one `IN`, a `null` pin combined with an `IN`, a repeated or non-leading property, or a missing pin), a `start_at` / `start_after` cursor, more than one `order_by`, or an `order_by` naming anything but the selected aggregate. // - `select=DOCUMENTS` with non-empty `group_by`. // - `select=COUNT` with `group_by` on a field that is not constrained by an `In` or range where clause. // - `select=COUNT` with `group_by.len() > 2`. @@ -1406,6 +1406,14 @@ message GetDocumentsResponse { // than hardcoding the literal. double avg = 4; } + // The prefix branch this entry came from, set **only** on an + // `IN`-pinned request (see the supported-shape table): the + // encoded index-key bytes of the `IN` property's pinned value — + // empty bytes for the `null` (absent-value) branch. Absent on + // single-prefix responses. The same group key can legally appear + // under two prefixes, so `(in_key, key)` is the entry's identity + // on a merged page, exactly as on `CountEntry`. + optional bytes in_key = 5; } // Ranked result entries. **Entry order IS the ranking order** — diff --git a/packages/dash-platform-queries/src/documents/document_having_entries.rs b/packages/dash-platform-queries/src/documents/document_having_entries.rs index 33b4997895f..e4d88a9f335 100644 --- a/packages/dash-platform-queries/src/documents/document_having_entries.rs +++ b/packages/dash-platform-queries/src/documents/document_having_entries.rs @@ -22,10 +22,16 @@ //! a contiguous-range operator (`=`, `>`, `>=`, `<`, `<=`, `BETWEEN*` — //! `!=` and `IN` are rejected), and a `LIMIT`. `ORDER BY` is optional: //! omitted means ascending by the aggregate; naming the selected -//! aggregate sets the direction. `where` clauses are equality pins on a -//! covering compound ranked index's leading properties (one per leading +//! aggregate sets the direction. `where` clauses are pins on a covering +//! compound ranked index's leading properties (one per leading //! property, selecting which prefix's groups the bound reads) — absent -//! for a single-property index. No `offset`, no `start_at`. +//! for a single-property index. Each pin is an equality, except that +//! **at most one** may be an `IN` of 2..=10 distinct elements: the +//! bound fans out across one prefix branch per element and merges, +//! entries carrying the encoded branch segment in `in_key` (unset on +//! single-branch responses; a single-element `IN` normalizes to the +//! equality pin; a `null` pin on another property cannot combine with +//! the `IN`). No `offset`, no `start_at`. //! //! ## Contract prerequisites //! @@ -33,7 +39,8 @@ //! `rankedCountable` / `rankedSummable` / `rankedAverageable` //! (meta-schema v3, **protocol version 14+**). The index may be //! single-property (`group_by` its property, no `where`) or compound -//! (`group_by` its trailing property, equality-pin every leading one). +//! (`group_by` its trailing property, pin every leading one — equality +//! pins, at most one of them an `IN`). //! Against a pre-v14 node the request is refused with "HAVING clause //! is not yet implemented" — the intended activation gate. //! @@ -306,8 +313,9 @@ mod tests { /// HAVING limits are a hard inclusive range, `1..=100`: `0` (the /// unset sentinel) and anything above `MAX_HAVING_LIMIT` are - /// rejected client side rather than clamped, because the limit is - /// echoed in the proof envelope and re-checked by the verifier. + /// rejected client side rather than clamped, because the limit + /// bounds the coverage the verifier's rebuilt `PathQuery` demands + /// of the proof. #[test] fn limit_is_required_and_capped_client_side() { for limit in [0u32, 101] { diff --git a/packages/dash-platform-queries/src/documents/document_ranked_entries.rs b/packages/dash-platform-queries/src/documents/document_ranked_entries.rs index 1f351f2c4b9..1b382da45f9 100644 --- a/packages/dash-platform-queries/src/documents/document_ranked_entries.rs +++ b/packages/dash-platform-queries/src/documents/document_ranked_entries.rs @@ -18,13 +18,19 @@ //! //! Exactly one aggregate `select`, exactly one `group_by` property, //! exactly one `ORDER BY` clause naming that select's aggregate, and a -//! `LIMIT` — plus an optional `OFFSET`. `where` clauses are equality -//! pins on a covering compound ranked index's leading properties (one -//! per leading property, selecting which prefix's own ranking the walk -//! reads) — absent for a single-property index. No `having`, no -//! `start_at`: each of those is rejected rather than ignored, on both -//! sides, because a ranked walk cannot honour them and silently -//! answering a different question is worse than an error. +//! `LIMIT` — plus an optional `OFFSET`. `where` clauses are pins on a +//! covering compound ranked index's leading properties (one per leading +//! property, selecting which prefix's own ranking the walk reads) — +//! absent for a single-property index. Each pin is an equality, except +//! that **at most one** may be an `IN` of 2..=10 distinct elements: one +//! walk per element, merged by `(aggregate, encoded pin, group key)`, +//! with each merged entry carrying the encoded branch segment in +//! `in_key` (unset on single-branch responses; a single-element `IN` +//! normalizes to the equality pin). A non-zero `OFFSET` cannot combine +//! with the `IN`, nor can a `null` pin on another property. No +//! `having`, no `start_at`: each of those is rejected rather than +//! ignored, on both sides, because a ranked walk cannot honour them and +//! silently answering a different question is worse than an error. //! //! [`DocumentQuery::order_by_selected_aggregate`] builds the ordering //! clause, deriving the ordered field from the `select` through @@ -37,8 +43,9 @@ //! The index must opt in with `rankedCountable` / `rankedSummable` / //! `rankedAverageable` (meta-schema v3, **protocol version 14+**). The //! index may be single-property (`group_by` its property, no `where`) -//! or compound (`group_by` its trailing property, equality-pin every -//! leading one). Against a protocol-version-13 +//! or compound (`group_by` its trailing property, pin every leading one +//! — equality pins, at most one of them an `IN`, as above). Against a +//! protocol-version-13 //! node the request is refused — v13's query table has no ranked path //! and rejects the ordering as `Unsupported`. That is the intended //! activation gate, not a bug: a v13 node and a v14 node must disagree @@ -494,9 +501,9 @@ mod tests { } /// The ranking's `n` rides `limit`, and an out-of-range one is - /// rejected rather than clamped: `k` is echoed inside the proof - /// envelope and re-checked by the verifier, so a silent clamp would - /// produce a proof the client's own reconstruction rejects. + /// rejected rather than clamped: `k` is part of the traversal the + /// client rebuilds to verify, so a silent clamp would produce a + /// page the client's reconstruction did not ask for. #[test] fn assert_ranked_shape_rejects_an_out_of_range_limit() { // `0` is `DocumentQuery`'s "unset" sentinel, and a ranking with diff --git a/packages/dash-platform-queries/src/documents/having_proof_helpers.rs b/packages/dash-platform-queries/src/documents/having_proof_helpers.rs index 91b2e5339d8..8d1ee4c0b0a 100644 --- a/packages/dash-platform-queries/src/documents/having_proof_helpers.rs +++ b/packages/dash-platform-queries/src/documents/having_proof_helpers.rs @@ -72,8 +72,10 @@ pub(super) fn assert_having_shape( `.with_having()` and `.with_limit(n)`, optionally \ `.order_by_selected_aggregate()`, with no offset and no start_at; \ - where clauses, when present, must be equality pins on the covering compound \ - index's leading properties." + where clauses, when present, pin the covering compound index's leading \ + properties — one equality pin per property, of which at most one may instead \ + be an `IN` of 2..=10 elements (merged entries then carry `in_key`; a null pin \ + on another property is rejected with `IN`)." ), }) } @@ -153,7 +155,8 @@ pub(super) fn verify_having_query( "document type `{}` cannot serve this having-range query: {e}. Ranked indexes \ are opt-in contract grammar (meta-schema v3, protocol version 14+); a pinned \ (compound-index) bound additionally needs every leading index property pinned \ - by an equality where clause.", + by a where clause — equality pins, of which at most one may be an `IN` of \ + 2..=10 elements.", request.document_type_name, ), })?; diff --git a/packages/dash-platform-queries/src/documents/ranked_proof_helpers.rs b/packages/dash-platform-queries/src/documents/ranked_proof_helpers.rs index 7ab5980bb60..754c0f4c50a 100644 --- a/packages/dash-platform-queries/src/documents/ranked_proof_helpers.rs +++ b/packages/dash-platform-queries/src/documents/ranked_proof_helpers.rs @@ -12,10 +12,10 @@ //! `(axis, k, descending, offset)` tuple by construction rather than by //! two copies of a grammar agreeing. //! -//! Unlike the count helper there is no per-shape dispatch: the ranked -//! surface has exactly one proof primitive (grovedb's unified -//! `prove_query` over `PathQuery::new_axis_top_k`), and all of a -//! request's variation is carried *inside* the query struct. +//! Unlike the count helper there is no per-shape dispatch: every +//! ranked request proves as one `RankedPage` axis traversal through +//! grovedb's unified `prove_query`, and all of a request's variation is +//! carried *inside* the query struct. //! //! [`DocumentRankedEntries`]: drive_proof_verifier::DocumentRankedEntries @@ -89,8 +89,10 @@ pub(super) fn assert_ranked_shape( `.with_select()`, `.with_group_by()`, \ `.order_by_selected_aggregate()` and `.with_limit(n)`, \ optionally `.with_offset(m)`, with no having and no start_at; where clauses, \ - when present, must be equality pins on the covering compound index's leading \ - properties." + when present, pin the covering compound index's leading properties — one \ + equality pin per property, of which at most one may instead be an `IN` of \ + 2..=10 elements (merged entries then carry `in_key`; a non-zero offset and a \ + null pin on another property are rejected with `IN`)." ), }) } @@ -186,7 +188,8 @@ pub(super) fn verify_ranked_query( "document type `{}` cannot serve this ranked query: {e}. Ranked indexes are \ opt-in contract grammar (meta-schema v3, protocol version 14+); a pinned \ (compound-index) ranking additionally needs every leading index property \ - pinned by an equality where clause.", + pinned by a where clause — equality pins, of which at most one may be an \ + `IN` of 2..=10 elements.", request.document_type_name, ), })?; diff --git a/packages/rs-dpp/src/data_contract/document_type/index/mod.rs b/packages/rs-dpp/src/data_contract/document_type/index/mod.rs index 168081ce4cf..a169a61e404 100644 --- a/packages/rs-dpp/src/data_contract/document_type/index/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/index/mod.rs @@ -1405,7 +1405,8 @@ impl Index { // `identityId` value — each ordering that identity's `class` groups // by the group aggregate. There is deliberately no global // cross-prefix ordering; the query surfaces require every leading - // property to be pinned by an equality `where` clause. + // property to be pinned by a `where` clause (`==`, at most one of + // them a bounded `IN` whose branches are merged client-visibly). // // One compound shape stays structurally impossible and is rejected // per document type (all indexes are needed to see it): a compound diff --git a/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs b/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs index 5a0361b66ac..e85bd7dc285 100644 --- a/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs +++ b/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs @@ -13,7 +13,6 @@ use crate::rpc::core::CoreRPCLike; use dpp::dashcore::hashes::Hash; use dpp::dashcore::Network; use dpp::version::TryIntoPlatformVersioned; -use drive::grovedb::error::Error as GroveDBError; use tenderdash_abci::proto::abci as proto; use tenderdash_abci::proto::abci::tx_record::TxAction; use tenderdash_abci::proto::abci::{ExecTxResult, TxRecord}; @@ -142,7 +141,7 @@ where ); if let Some(tx) = transaction_guard.as_ref() { tx.rollback_to_savepoint() - .map_err(GroveDBError::StorageError)?; + .map_err(drive::grovedb::error::Error::StorageError)?; tx.set_savepoint(); } transaction_guard diff --git a/packages/rs-drive-abci/src/abci/handler/process_proposal.rs b/packages/rs-drive-abci/src/abci/handler/process_proposal.rs index db05c55fb77..69bae1c5fcd 100644 --- a/packages/rs-drive-abci/src/abci/handler/process_proposal.rs +++ b/packages/rs-drive-abci/src/abci/handler/process_proposal.rs @@ -14,7 +14,6 @@ use crate::platform_types::state_transitions_processing_result::StateTransitionE use crate::rpc::core::CoreRPCLike; use dpp::dashcore::Network; use dpp::version::TryIntoPlatformVersioned; -use drive::grovedb::error::Error as GroveDBError; use tenderdash_abci::proto::abci as proto; use tenderdash_abci::proto::abci::tx_record::TxAction; @@ -173,7 +172,7 @@ where ); if let Some(tx) = transaction_guard.as_ref() { tx.rollback_to_savepoint() - .map_err(GroveDBError::StorageError)?; + .map_err(drive::grovedb::error::Error::StorageError)?; tx.set_savepoint(); } transaction_guard diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs index 2a17f38dba9..bca8b67d3aa 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs @@ -222,9 +222,9 @@ where // in the state the app hash is computed over. A rollback // failure means the proposal can no longer match the // block — fail it rather than continue on leaked state. - transaction.rollback_to_savepoint().map_err(|e| { - drive::grovedb::error::Error::StorageError(e) - })?; + transaction + .rollback_to_savepoint() + .map_err(drive::grovedb::error::Error::StorageError)?; } StateTransitionExecutionResult::SuccessfulExecution { .. } | StateTransitionExecutionResult::PaidConsensusError { .. } => { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs index 8e97e3826b1..fb5226372ea 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs @@ -39,10 +39,7 @@ use dpp::state_transition::batch_transition::document_delete_transition::Documen use dpp::state_transition::batch_transition::BatchTransitionV1; use dpp::state_transition::StateTransition; use drive::drive::RootTree; -use drive::grovedb::element::IndexAxis; -use drive::grovedb::operations::proof::indexed_axis::AxisEntries; -use drive::grovedb::query_result_type::QueryResultType; -use drive::grovedb::{Element, PathQuery, PathQueryRun}; +use drive::grovedb::Element; /// The group that gets emptied. const G: &str = "beta"; @@ -78,7 +75,19 @@ fn ranked_count_groups( path: &[Vec], platform_version: &PlatformVersion, ) -> Vec<(u64, String)> { - let path_query = PathQuery::new_axis_top_k(path.to_vec(), IndexAxis::Count, 100, 0, true); + use drive::grovedb::query_result_type::QueryResultType; + use drive::grovedb::{AxisKeys, PathQuery, PathQueryRun}; + + let path_query = PathQuery::new_axis( + path.to_vec(), + drive::grovedb_query::AxisQuery::top_k( + drive::grovedb_query::IndexAxis::Count, + 100, + 0, + true, + ) + .keys_only(), + ); let run = platform .drive .grove @@ -87,22 +96,21 @@ fn ranked_count_groups( true, true, true, - QueryResultType::QueryPathKeyElementTrioResultType, + QueryResultType::QueryKeyElementPairResultType, None, &platform_version.drive.grove_version, ) .unwrap() .expect("the ranked count read must succeed"); - let PathQueryRun::AxisEntries { - entries: AxisEntries::Count(entries), + let PathQueryRun::AxisKeys { + keys: AxisKeys::Count(pairs), .. } = run else { - panic!("expected count axis entries"); + panic!("expected a keys-only count page, got {run:?}"); }; - entries + pairs .into_iter() - .map(|entry| entry.key_pair()) .map(|(count, key)| { ( count, diff --git a/packages/rs-drive-abci/src/query/document_query/v1/dispatch/mod.rs b/packages/rs-drive-abci/src/query/document_query/v1/dispatch/mod.rs index df6b78461cf..4e2bbc216b2 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/dispatch/mod.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/dispatch/mod.rs @@ -34,6 +34,10 @@ use drive::query::{RankedEntry as DriveRankedEntry, RankedEntryValue}; /// silent about). fn into_v1_ranked_entry(e: DriveRankedEntry) -> RankedEntry { RankedEntry { + // Set exactly when the request carried an `IN` prefix pin — + // drive's merge tags entries with their branch, single-branch + // responses stay untagged. + in_key: e.in_key, key: e.key, value: Some(match e.value { RankedEntryValue::Count(count) => ranked_entry::Value::Count(count), diff --git a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs index a1ddb2363ea..fa34310ca4e 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs @@ -2805,8 +2805,8 @@ mod ranked_tests { } /// `limit` is **required** on the ranked path — it is the `k` the - /// proof envelope echoes, so there is no server default a verifying - /// client could reproduce. What this pins is that drive's + /// verifier rebuilds its `PathQuery` around, so there is no server + /// default a verifying client could reproduce. What this pins is that drive's /// `Error::Query` reaches the caller as a query error on the /// validation result, rather than being swallowed into an internal /// error by the dispatcher's `Err(e) => Err(e.into())` arm. @@ -3427,7 +3427,7 @@ mod having_range_tests { QueryError::Query(QuerySyntaxError::InvalidParameter(message)) => { assert!( message.contains("exactly one `group_by` property") - && message.contains("equality `where` clause"), + && message.contains("pin every leading index property"), "the rejection must steer to the pinned-prefix form, got: {message}" ); } @@ -3480,6 +3480,88 @@ mod having_range_tests { } } + /// The `IN`-pinned form end to end on the wire: `WHERE identityId + /// IN [X, Y] GROUP BY class HAVING AVG(grade) > 80 LIMIT 10` fans + /// out across both identities' secondaries and answers one merged + /// `ResultData.ranked` page whose entries carry `in_key`; the + /// proved variant returns the unified branched `PathQuery` envelope as its Proof + /// payload. Merge/proof semantics are pinned in rs-drive's suites; + /// this pins the wire encoding, routing, and `in_key` mapping. + #[test] + fn in_pinned_having_is_served_end_to_end() { + let (platform, state, version) = setup_platform(None, Network::Testnet, None); + let contract = register_grades_compound(&platform, version); + let identity_x = [1u8; 32]; + let identity_y = [2u8; 32]; + insert_grade_docs( + &platform, + &contract, + 21_000, + &[ + (identity_x, "art", 90), + (identity_x, "math", 60), + (identity_y, "science", 95), + ], + version, + ); + + let mut request = having_request( + &contract, + "grade", + select(v1_select::Function::Avg, "grade"), + hc( + having_aggregate::Function::Avg, + "grade", + having_clause::Operator::GreaterThan, + Value::U64(80), + ), + Vec::new(), + Some(10), + false, + ); + request.group_by = vec!["class".to_string()]; + request.where_clauses = vec![wc( + "identityId", + ProtoWhereOperator::In, + Value::Array(vec![ + Value::Bytes(identity_y.to_vec()), + Value::Bytes(identity_x.to_vec()), + ]), + )]; + + let page = ranked_page(&platform, &state, request.clone(), version); + assert_eq!( + group_keys(&page.entries), + vec!["art", "science"], + "merged ascending: X's art (90) then Y's science (95); X's math \ + (60) misses the bound" + ); + assert_eq!( + page.entries + .iter() + .map(|e| e.in_key.clone()) + .collect::>(), + vec![Some(identity_x.to_vec()), Some(identity_y.to_vec())], + "merged entries carry their branch's in_key on the wire" + ); + + // The proved variant answers with a Proof payload (the unified + // branched `PathQuery` envelope — verified client-side, pinned + // in rs-drive's tamper suite). + request.prove = true; + let result = platform + .query_documents_v1(request, &state, version) + .expect("query should succeed"); + assert!(result.errors.is_empty(), "got {:?}", result.errors); + match result.data { + Some(GetDocumentsResponseV1 { + result: Some(get_documents_response_v1::Result::Proof(proof)), + .. + }) => assert!(!proof.grovedb_proof.is_empty()), + other => panic!("expected a Proof response, got {:?}", other), + } + } + /// The pinned-prefix form end to end on the wire: `WHERE identityId /// = X GROUP BY class HAVING AVG(grade) > 80 LIMIT 10` routes to /// the same having executor, descends to X's terminal `class` tree, @@ -3831,7 +3913,7 @@ mod having_trust_boundary { DocumentHavingRequest, DocumentHavingResponse, }; use drive::query::drive_document_having_query::mode_detection::detect_having_mode; - use drive::query::drive_document_ranked_query::index_picker::find_ranked_index_for_axis; + use drive::query::drive_document_having_query::resolve_having_query_for_mode; use drive::query::having::{ HavingAggregate, HavingAggregateFunction, HavingClause, HavingOperator, HavingRightOperand, }; @@ -3999,30 +4081,21 @@ mod having_trust_boundary { platform_version(), ) .expect("the case is well-formed"); - let index = find_ranked_index_for_axis( + resolve_having_query_for_mode( + contract.id_ref().to_buffer(), + contract + .document_type_for_name("grade") + .expect("grade doctype exists"), + "grade".to_string(), contract .document_types() .get("grade") .expect("grade doctype exists") .indexes(), - &mode.group_by_property, - &[], - mode.bounds.axis(), - &mode.aggregate_field, + &mode, + PlatformVersion::latest(), ) - .expect("the fixture declares the avg axis"); - DriveDocumentHavingQuery { - document_type: contract - .document_type_for_name("grade") - .expect("grade doctype exists"), - contract_id: contract.id_ref().to_buffer(), - document_type_name: "grade".to_string(), - index, - bounds: mode.bounds, - equality_prefix_values: Vec::new(), - descending: mode.descending, - limit: mode.limit, - } + .expect("the fixture declares the avg axis") } /// Prove the having request against the live Drive and return diff --git a/packages/rs-drive-proof-verifier/src/proof/document_having.rs b/packages/rs-drive-proof-verifier/src/proof/document_having.rs index ea6069a6c10..b8208100071 100644 --- a/packages/rs-drive-proof-verifier/src/proof/document_having.rs +++ b/packages/rs-drive-proof-verifier/src/proof/document_having.rs @@ -151,10 +151,13 @@ impl DocumentHavingEntries { /// (which does the merk-level verification). Both sides derive the /// proved subtree from the same /// `DriveDocumentHavingQuery::indexed_property_name_tree_path` and the -/// secondary query from the same `AxisRangeBounds::merk_query`, so -/// prover and verifier cannot drift on *which bound over which tree* is -/// being checked, and grovedb re-checks the echoed query and limit — a -/// proof of one bound does not verify as another. +/// bounded traversal from the same +/// `AxisRangeBounds::inclusive_bounds_i128`, so prover and verifier +/// cannot drift on *which bound over which tree* is being checked, and +/// grovedb re-executes the proof against that reconstruction — a proof +/// of one bound does not cover another (the limit binds as a cap: an +/// exhausted proof verifies under any admitting cap, a truncated one +/// fails a larger cap for missing coverage). /// /// ## The root hash is the whole point /// @@ -236,6 +239,7 @@ mod tests { fn count_entry(key: &str, count: u64) -> ProtoRankedEntry { ProtoRankedEntry { + in_key: None, key: key.as_bytes().to_vec(), value: Some(ranked_entry::Value::Count(count)), } diff --git a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs index 24f06524e7b..e75f22a1bec 100644 --- a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs +++ b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs @@ -275,6 +275,9 @@ pub(crate) fn ranked_entry_from_proto(entry: &ProtoRankedEntry) -> Result Result ProtoRankedEntry { ProtoRankedEntry { + in_key: None, key: key.as_bytes().to_vec(), value: Some(ranked_entry::Value::Count(count)), } @@ -424,6 +428,7 @@ mod tests { /// that no fixed point maps to. fn avg_entry_raw(key: &str, avg: f64) -> ProtoRankedEntry { ProtoRankedEntry { + in_key: None, key: key.as_bytes().to_vec(), value: Some(ranked_entry::Value::Avg(avg)), } @@ -566,6 +571,7 @@ mod tests { #[test] fn decodes_signed_sum_entries() { let response = ranked_response(vec![ProtoRankedEntry { + in_key: None, key: b"refunds".to_vec(), value: Some(ranked_entry::Value::Sum(-1_000)), }]); @@ -666,6 +672,7 @@ mod tests { #[test] fn rejects_an_entry_with_no_value() { let response = ranked_response(vec![ProtoRankedEntry { + in_key: None, key: b"alpha".to_vec(), value: None, }]); @@ -734,10 +741,12 @@ mod tests { fn from_verified_carries_both_halves_of_the_page() { let entries = vec![ RankedEntry { + in_key: None, key: b"gamma".to_vec(), value: RankedEntryValue::Count(9), }, RankedEntry { + in_key: None, key: b"alpha".to_vec(), value: RankedEntryValue::Count(2), }, diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/batched_group_drain.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/batched_group_drain.rs index a158e287623..816551e6833 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/batched_group_drain.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/batched_group_drain.rs @@ -376,7 +376,7 @@ fn verified_page( ) .expect("the fixture declares this axis"), axis: axis.ranked, - equality_prefix_values: Vec::new(), + prefix_branches: vec![Vec::new()], descending: !ascending, k: 100, offset: 0, diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index d59306b7e26..9710bbbf70e 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -72,10 +72,7 @@ use dpp::prelude::DataContract; use dpp::tests::json_document::json_document_to_contract; use dpp::version::PlatformVersion; use grovedb::element::indexed::AVG_FIXED_POINT_SCALE; -use grovedb::element::IndexAxis; -use grovedb::query_result_type::QueryResultType; -use grovedb::{AxisKeys, Element, PathQuery, PathQueryRun}; -use grovedb_query::AxisQuery; +use grovedb::Element; /// The one index property every doctype in the fixture ranks by. const GROUP_PROPERTY: &str = "restaurantId"; @@ -274,18 +271,16 @@ fn group_keys(entries: &[(T, Vec)]) -> Vec { .collect() } -/// Run a keys-only top-k axis read through the unified PathQuery -/// surface — the same route the ranked no-proof executor takes. -fn run_top_k_keys( +fn axis_top_k_keys( drive: &Drive, path: &[Vec], - axis: IndexAxis, + axis: grovedb_query::IndexAxis, k: u16, descending: bool, -) -> AxisKeys { - let path_query = PathQuery::new_axis( +) -> grovedb::AxisKeys { + let path_query = grovedb::PathQuery::new_axis( path.to_vec(), - AxisQuery::top_k(axis, k, 0, descending).keys_only(), + grovedb_query::AxisQuery::top_k(axis, k, 0, descending).keys_only(), ); match drive .grove @@ -294,36 +289,36 @@ fn run_top_k_keys( true, true, true, - QueryResultType::QueryPathKeyElementTrioResultType, + grovedb::query_result_type::QueryResultType::QueryKeyElementPairResultType, None, &platform_version().drive.grove_version, ) .unwrap() - .expect("top-k axis read must succeed") + .expect("the keys-only axis read must succeed") { - PathQueryRun::AxisKeys { keys, .. } => keys, - other => panic!("expected AxisKeys, got {other:?}"), + grovedb::PathQueryRun::AxisKeys { keys, .. } => keys, + other => panic!("expected a keys-only axis page, got {other:?}"), } } fn avg_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(i128, Vec)> { - match run_top_k_keys(drive, path, IndexAxis::Avg, k, descending) { - AxisKeys::Avg(pairs) => pairs, - other => panic!("expected avg pairs, got {other:?}"), + match axis_top_k_keys(drive, path, grovedb_query::IndexAxis::Avg, k, descending) { + grovedb::AxisKeys::Avg(pairs) => pairs, + other => panic!("expected avg keys, got {other:?}"), } } fn count_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(u64, Vec)> { - match run_top_k_keys(drive, path, IndexAxis::Count, k, descending) { - AxisKeys::Count(pairs) => pairs, - other => panic!("expected count pairs, got {other:?}"), + match axis_top_k_keys(drive, path, grovedb_query::IndexAxis::Count, k, descending) { + grovedb::AxisKeys::Count(pairs) => pairs, + other => panic!("expected count keys, got {other:?}"), } } fn sum_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(i64, Vec)> { - match run_top_k_keys(drive, path, IndexAxis::Sum, k, descending) { - AxisKeys::Sum(pairs) => pairs, - other => panic!("expected sum pairs, got {other:?}"), + match axis_top_k_keys(drive, path, grovedb_query::IndexAxis::Sum, k, descending) { + grovedb::AxisKeys::Sum(pairs) => pairs, + other => panic!("expected sum keys, got {other:?}"), } } @@ -1514,11 +1509,11 @@ fn ranked_avg_page( ascending: false, }], where_clauses: &[], + resolved_time_ranges: &[], limit: Some(limit), offset: Some(offset), has_start_at: false, prove: false, - resolved_time_ranges: &[], }, None, platform_version(), @@ -1561,11 +1556,11 @@ fn verified_ranked_avg_page( ascending: false, }], where_clauses: &[], + resolved_time_ranges: &[], limit: Some(limit), offset: Some(offset), has_start_at: false, prove: true, - resolved_time_ranges: &[], }, None, platform_version(), @@ -1588,7 +1583,7 @@ fn verified_ranked_avg_page( document_type_name: "review".to_string(), index: find_ranked_index_for_axis(indexes, GROUP_PROPERTY, &[], RankedAxis::Avg, "grade") .expect("the fixture declares rankedAverageable on grade"), - equality_prefix_values: vec![], + prefix_branches: vec![vec![]], axis: RankedAxis::Avg, descending: true, k: limit as u16, diff --git a/packages/rs-drive/src/lib.rs b/packages/rs-drive/src/lib.rs index f5c5426bec3..a53a3216012 100644 --- a/packages/rs-drive/src/lib.rs +++ b/packages/rs-drive/src/lib.rs @@ -30,6 +30,10 @@ pub use dpp; #[cfg(any(feature = "server", feature = "verify"))] pub use grovedb; +/// Re-exported so downstream crates can build `AxisQuery`-shaped +/// [`grovedb::PathQuery`]s without a direct dependency. +pub use grovedb_query; + #[cfg(feature = "server")] pub use grovedb_path; diff --git a/packages/rs-drive/src/query/drive_document_having_query/drive_dispatcher.rs b/packages/rs-drive/src/query/drive_document_having_query/drive_dispatcher.rs index 77887a6b729..92797b73022 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/drive_dispatcher.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/drive_dispatcher.rs @@ -45,8 +45,10 @@ pub struct DocumentHavingRequest<'a> { /// one, naming the selected aggregate. pub order_by: &'a [OrderClause], /// Structured `where` clauses. Empty for the single-property form; - /// equality pins on the covering compound index's leading - /// properties for the pinned-prefix form. + /// pins on the covering compound index's leading properties for + /// the pinned-prefix form: one equality pin per property, of which + /// at most one may instead be a bounded `IN` (one branch per + /// element, merged; entries then carry `in_key`). pub where_clauses: &'a [WhereClause], /// The fields among `where_clauses` whose equality clause was produced by /// `IN_TIME_RANGE` resolution (see diff --git a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs index fa02f06804a..12e732da890 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs @@ -11,14 +11,17 @@ //! Whole module is gated `feature = "server"` via the parent's //! `pub mod execute_range;` declaration. -use super::super::drive_document_ranked_query::{RankedAxis, RankedEntry, RankedEntryValue}; +use super::super::drive_document_ranked_query::branches::{ + axis_keys_to_ranked, decompose_branch_paths, read_branched_union, +}; +use super::super::drive_document_ranked_query::RankedEntry; use super::DriveDocumentHavingQuery; use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; use dpp::version::PlatformVersion; use grovedb::query_result_type::QueryResultType; -use grovedb::{AxisKeys, PathQuery, PathQueryRun, TransactionArg}; +use grovedb::{PathQuery, PathQueryRun, TransactionArg}; use grovedb_costs::CostContext; use grovedb_query::AxisQuery; @@ -29,97 +32,92 @@ impl DriveDocumentHavingQuery<'_> { /// /// Fewer than `limit` entries is normal (fewer groups match) and is /// not an error; exactly `limit` entries may mean the match set was - /// cut. A missing path *is* an error rather than an empty result, - /// for the same reason as on the ranked surface: the indexed - /// property-name tree is created at contract registration, so its - /// absence means the contract-level state is not what the request - /// claims. (An index with no documents has the tree, with an empty - /// secondary, and yields an empty entry list.) + /// cut. + /// + /// Missing paths follow the ranked surface's rule. Under a single + /// `==` pin (or no pins) a missing path *is* an error rather than an + /// empty result: the indexed property-name tree is created at + /// contract registration, so its absence means the contract-level + /// state is not what the request claims. On an `IN`-pinned request, + /// an element whose branch chain is missing at ANY depth — the + /// branch key, or any deeper pinned segment under a *present* key — + /// contributes an **empty branch** instead (union semantics, exactly + /// as the proved envelope authenticates it), and the union is served + /// from one committed state (a `None` read runs under a grovedb + /// snapshot read transaction). An index with no documents has the + /// tree, with an empty secondary, and yields an empty entry list. pub fn execute_range_no_proof( &self, drive: &Drive, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result, Error> { - let grove_version = &platform_version.drive.grove_version; - let path = self.indexed_property_name_tree_path()?; - - // The same bounded axis PathQuery the prove path uses, with the - // keys-only projection: the matching pairs are read straight off - // the pinned secondary view, no primary values resolved. - let (lo, hi) = self.bounds.i128_bounds(); - let path_query = PathQuery::new_axis( - path, - AxisQuery::bounded( - self.bounds.axis().into(), - lo, - hi, - self.limit, + if self.prefix_branches.len() > 1 { + // ONE grovedb call for the whole union, pinned to one + // committed state — the entire sequence lives in the ranked + // surface's `branches::read_branched_union`, shared with the + // ranked executor so the two cannot drift. + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let axis = self.bounds.axis(); + let (lo, hi) = self.bounds.inclusive_bounds_i128(); + return read_branched_union( + &drive.grove, + "having", + &self.prefix_branches, + &paths, + axis, + AxisQuery::bounded(axis.into(), lo, hi, self.limit, self.descending), + self.limit as usize, self.descending, - ) - .keys_only(), - ); + transaction, + &platform_version.drive.grove_version, + ); + } + self.execute_range_no_proof_branch(0, drive, transaction, platform_version) + } + + /// One branch's in-bound page — the entire pre-`IN` executor, + /// parameterized by which prefix branch's terminal tree it walks. + fn execute_range_no_proof_branch( + &self, + branch: usize, + drive: &Drive, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let grove_version = &platform_version.drive.grove_version; + let path = self.indexed_property_name_tree_path(branch)?; + let axis = self.bounds.axis(); + let (lo, hi) = self.bounds.inclusive_bounds_i128(); // Costs are destructured away rather than `.unwrap()`-ed, same // as the ranked executors: `CostContext::unwrap` is infallible // but reads like a panicking unwrap at the call site. + let path_query = PathQuery::new_axis( + path, + AxisQuery::bounded(axis.into(), lo, hi, self.limit, self.descending).keys_only(), + ); let CostContext { value, cost: _ } = drive.grove.run_path_query( &path_query, true, true, true, - QueryResultType::QueryPathKeyElementTrioResultType, + QueryResultType::QueryKeyElementPairResultType, transaction, grove_version, ); - // `skipped` is the paginated traversal's field and is `None` - // for bounded ones — nothing to check here. - let PathQueryRun::AxisKeys { keys, skipped: _ } = - value.map_err(|e| Error::GroveDB(Box::new(e)))? - else { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having {:?} range read ran to a non-axis-keys result shape", - self.bounds.axis() - )))); - }; - - let entries = match (self.bounds.axis(), keys) { - (RankedAxis::Count, AxisKeys::Count(pairs)) => pairs - .into_iter() - .map(|(count, key)| RankedEntry { - key, - value: RankedEntryValue::Count(count), - }) - .collect::>(), - (RankedAxis::Sum, AxisKeys::Sum(pairs)) => pairs - .into_iter() - .map(|(sum, key)| RankedEntry { - key, - value: RankedEntryValue::Sum(sum), - }) - .collect::>(), - (RankedAxis::Avg, AxisKeys::Avg(pairs)) => pairs - .into_iter() - .map(|(avg, key)| RankedEntry { - key, - value: RankedEntryValue::AvgFixedPoint(avg), - }) - .collect::>(), - (axis, other) => { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having {axis:?} range read returned {} pairs of a different axis shape", - other.len() - )))); - } + let run = value.map_err(|e| Error::GroveDB(Box::new(e)))?; + let PathQueryRun::AxisKeys { keys, skipped: _ } = run else { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a keys-only having read returned a different result shape".to_string(), + ))); }; - - // The limit is the contract with the caller, and on the prove - // path it is re-checked inside the proof envelope. Asserting it - // here keeps the no-proof and prove responses shape-identical. + let entries = axis_keys_to_ranked(axis, keys)?; if entries.len() > self.limit as usize { return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having {:?} range read returned {} entries for limit = {}", - self.bounds.axis(), + "having {axis:?} read returned {} entries for limit = {}", entries.len(), self.limit )))); @@ -127,35 +125,77 @@ impl DriveDocumentHavingQuery<'_> { Ok(entries) } - /// Generate the bounded-axis proof for this query, through the - /// unified `PathQuery` surface (grovedb's only public proof surface - /// for indexed-axis reads): the query is - /// [`PathQuery::new_axis_bounded`] and the envelope is a GroveDBProof - /// V1 carrying an axis descent into the queried secondary. + /// Generate the grovedb indexed-axis range proof for this query. /// /// The envelope commits the in-range secondary entries, the - /// primary's root hash, the sibling axes' root hashes, and the - /// ordinary layer chain up to the grovedb root — so the client - /// reconstructs the platform root hash from it. Nothing is echoed: - /// the verifier takes the client's own reconstruction of the same - /// `PathQuery` as input, and grovedb lowers its bounds into the - /// secondary's keyspace through one function shared by both proof - /// sides — which is why the bounds are validated rather than clamped - /// upstream, and why completeness needs no extra machinery: a Merk - /// range proof over a sorted keyspace commits its boundaries, so an - /// in-range group the server omitted fails reconstruction. + /// primary's root hash, the sibling axes' root hashes, and a + /// per-ancestor attestation chain up to the grovedb root — so the + /// client reconstructs the platform root hash from it. The bounds, + /// direction and limit bind by RECONSTRUCTION: the verifier rebuilds + /// the same `Bounded` axis `PathQuery` from the request + /// ([`AxisRangeBounds::inclusive_bounds_i128`]) and re-executes the + /// proof against it — which is why the bounds are validated rather + /// than clamped upstream, and why completeness needs no extra + /// machinery: a Merk range proof over a sorted keyspace commits its + /// boundaries, so an in-range group the server omitted fails + /// reconstruction. /// /// Verified by /// [`DriveDocumentHavingQuery::verify_having_range_proof`](crate::query::DriveDocumentHavingQuery::verify_having_range_proof). pub fn execute_range_with_proof( &self, drive: &Drive, - _transaction: TransactionArg, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + // Same fail-closed rule as the ranked prover: grovedb's + // `prove_query` proves committed state only and cannot see the + // caller's transaction — single-prefix and branched alike. + if transaction.is_some() { + return Err(Error::Drive(DriveError::NotSupported( + "a having-range proof is generated from committed state only: grovedb's \ + prove_query cannot see the caller's transaction — commit first", + ))); + } + if self.prefix_branches.len() > 1 { + // One grovedb **branched** envelope — see the ranked + // executor's multi-branch arm for the shape. + let grove_version = &platform_version.drive.grove_version; + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let (prefix, keys, suffix) = decompose_branch_paths(&paths)?; + let (lo, hi) = self.bounds.inclusive_bounds_i128(); + let path_query = PathQuery::new_branched_axis( + prefix, + keys, + suffix, + AxisQuery::bounded( + self.bounds.axis().into(), + lo, + hi, + self.limit, + self.descending, + ), + ); + let CostContext { value, cost: _ } = + drive.grove.prove_query(&path_query, None, grove_version); + return value.map_err(|e| Error::GroveDB(Box::new(e))); + } + self.execute_range_with_proof_branch(0, drive, platform_version) + } + + /// One branch's proof — the entire pre-`IN` prover, parameterized by + /// the prefix branch. + fn execute_range_with_proof_branch( + &self, + branch: usize, + drive: &Drive, platform_version: &PlatformVersion, ) -> Result, Error> { let grove_version = &platform_version.drive.grove_version; - let path = self.indexed_property_name_tree_path()?; - let (lo, hi) = self.bounds.i128_bounds(); + let path = self.indexed_property_name_tree_path(branch)?; + let (lo, hi) = self.bounds.inclusive_bounds_i128(); let path_query = PathQuery::new_axis_bounded( path, self.bounds.axis().into(), @@ -164,12 +204,6 @@ impl DriveDocumentHavingQuery<'_> { self.limit, self.descending, ); - - // The unified prover proves committed state — it takes no - // transaction. The parameter is kept for signature stability with - // the no-proof executor; the query dispatch passes `None` on this - // surface anyway (queries answer from committed state). - // // Same destructure-don't-unwrap rationale as the no-proof arm. let CostContext { value, cost: _ } = drive.grove.prove_query(&path_query, None, grove_version); diff --git a/packages/rs-drive/src/query/drive_document_having_query/mod.rs b/packages/rs-drive/src/query/drive_document_having_query/mod.rs index dcf17d0b883..8efc4949d87 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/mod.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/mod.rs @@ -37,13 +37,12 @@ //! rank `offset`); having-range addresses them by **value bound** //! (`aggregate ∈ [lo, hi]`). Three consequences: //! -//! 1. **The bound is part of the proof contract.** Both proof sides -//! build the same [`grovedb::PathQuery::new_axis_bounded`] from the -//! request's bounds ([`AxisRangeBounds::i128_bounds`]), and grovedb -//! lowers those bounds into the secondary's keyspace through one -//! shared function on both the prover and the verifier — so the two -//! sides cannot drift on which range a proof is about, exactly as -//! they share the grove path. Completeness comes from the Merk range +//! 1. **The bound is part of the proof contract.** Prover and verifier +//! build the same `Bounded` axis `PathQuery` from the request's +//! inclusive bounds ([`AxisRangeBounds::inclusive_bounds_i128`]), +//! and grovedb re-executes the proof against that traversal — so the +//! two sides share one bounds-to-query translation, exactly as they +//! share the grove path. Completeness comes from the Merk range //! proof: the boundary commitments show no in-range group was //! omitted. //! 2. **No `OFFSET`, no `start_at` — and no full pagination.** The @@ -67,26 +66,28 @@ #[cfg(any(feature = "server", feature = "verify"))] use dpp::data_contract::document_type::{DocumentTypeRef, Index}; #[cfg(any(feature = "server", feature = "verify"))] -use dpp::platform_value::Value; -#[cfg(any(feature = "server", feature = "verify"))] use dpp::version::PlatformVersion; #[cfg(any(feature = "server", feature = "verify"))] use std::collections::BTreeMap; #[cfg(any(feature = "server", feature = "verify"))] use super::drive_document_ranked_query::index_picker::{ - encode_equality_prefix_values, find_ranked_index_for_axis, no_covering_index_message, + encode_prefix_branches, find_ranked_index_for_axis, no_covering_index_message, }; #[cfg(any(feature = "server", feature = "verify"))] use super::drive_document_ranked_query::{ - path::indexed_property_name_tree_path_for_index, RankedAxis, + path::indexed_property_name_tree_path_for_index, PrefixPin, RankedAxis, }; #[cfg(any(feature = "server", feature = "verify"))] +use crate::error::drive::DriveError; +#[cfg(any(feature = "server", feature = "verify"))] use crate::error::query::QuerySyntaxError; #[cfg(any(feature = "server", feature = "verify"))] use crate::error::Error; #[cfg(any(feature = "server", feature = "verify"))] use grovedb::element::indexed::{encode_avg_sort_key, encode_count_sort_key, encode_sum_sort_key}; +#[cfg(any(feature = "server", feature = "verify"))] +use grovedb::Query; #[cfg(any(feature = "server", feature = "verify"))] pub mod mode_detection; @@ -109,8 +110,9 @@ mod tests; /// rationale as [`super::drive_document_ranked_query::MAX_RANKED_LIMIT`]: /// the proof commits one secondary entry per returned group, so proof /// bytes grow linearly in the limit, and the ceiling is a hard rejection -/// rather than a clamp because the limit is echoed in the proof envelope -/// and re-checked by the verifier. +/// rather than a clamp because the limit is part of the traversal the +/// verifier re-executes: a server-side clamp would truncate the walk +/// and fail coverage under the client's own reconstruction. #[cfg(any(feature = "server", feature = "verify"))] pub const MAX_HAVING_LIMIT: u16 = 100; @@ -163,6 +165,18 @@ impl AxisRangeBounds { } } + /// The bounds as inclusive `i128` values in the axis's own domain — + /// the form `AxisTraversal::Bounded` carries in the unified + /// `PathQuery`. Count and sum widen losslessly; avg is already + /// `i128` fixed point. + pub fn inclusive_bounds_i128(&self) -> (i128, i128) { + match *self { + AxisRangeBounds::Count { lo, hi } => (lo as i128, hi as i128), + AxisRangeBounds::Sum { lo, hi } => (lo as i128, hi as i128), + AxisRangeBounds::Avg { lo, hi } => (lo, hi), + } + } + /// The bounds as a byte range over the axis secondary's keyspace: /// `(inclusive_lower, exclusive_upper)`, with `None` for an upper /// bound at the axis's type maximum (no representable successor — @@ -196,26 +210,23 @@ impl AxisRangeBounds { } } - /// The inclusive bounds widened to `i128` — the domain - /// [`grovedb::PathQuery::new_axis_bounded`] takes for every axis. + /// The Merk query over the axis secondary that reads exactly these + /// bounds, walking in the requested direction. /// - /// The prover/verifier-agreement artifact of the having surface is - /// grovedb's own bounded-axis lowering: both proof sides lower the - /// same `PathQuery` bounds into the secondary's keyspace through one - /// shared function inside grovedb, producing exactly the byte range - /// [`Self::secondary_key_bounds`] documents — inclusive at - /// `encode(lo)`, exclusive at `encode(hi + 1)`, open-ended at the - /// axis maximum. Platform no longer builds the Merk query itself, so - /// the two sides cannot drift on which range a proof is about. - /// - /// Widening is lossless: `u64` and `i64` both embed in `i128`, and - /// grovedb clamps back to each axis's own domain when lowering. - pub fn i128_bounds(&self) -> (i128, i128) { - match *self { - AxisRangeBounds::Count { lo, hi } => (lo as i128, hi as i128), - AxisRangeBounds::Sum { lo, hi } => (lo as i128, hi as i128), - AxisRangeBounds::Avg { lo, hi } => (lo, hi), + /// This is the **prover/verifier-agreement artifact** of the having + /// surface: grovedb's range-proof envelope is generated against this + /// query and verified against the verifier's own reconstruction of + /// it, so both sides must build it from the same bounds through this + /// one function — a divergence surfaces as a failed verification, + /// not a wrong answer. + pub fn merk_query(&self, descending: bool) -> Query { + let (lower, upper) = self.secondary_key_bounds(); + let mut query = Query::new_with_direction(!descending); + match upper { + Some(upper) => query.insert_range(lower..upper), + None => query.insert_range_from(lower..), } + query } } @@ -226,7 +237,7 @@ impl AxisRangeBounds { /// Produced by [`mode_detection::detect_having_mode`]. Parallels /// [`super::drive_document_ranked_query::DocumentRankedMode`]. /// -/// Not `Eq`: the equality pins carry [`Value`]s, whose float variant +/// Not `Eq`: the prefix pins carry [`Value`]s, whose float variant /// keeps the type at `PartialEq`. #[derive(Debug, Clone, PartialEq)] #[cfg(any(feature = "server", feature = "verify"))] @@ -246,19 +257,21 @@ pub struct DocumentHavingMode { /// The field the aggregate applies to. Empty for `COUNT(*)`; the /// index's `summable` property for `SUM` / `AVG`. pub aggregate_field: String, - /// The equality `where` pins, `(property, value)` per clause — - /// exactly one per leading property of the covering compound index, - /// in request order (the resolver re-orders them into index order - /// when it encodes the path). Empty for the single-property form. - pub equality_pins: Vec<(String, Value)>, + /// The `where` prefix pins — one [`PrefixPin`] per clause, exactly + /// one per leading property of the covering compound index, in + /// request order (the resolver re-orders them into index order when + /// it encodes the path). A pin normally carries one value (an `==` + /// clause); at most one pin carries several (the single permitted + /// branching `IN`, whose elements fan the bound out across one + /// prefix branch each). Empty for the single-property form. + pub prefix_pins: Vec, } /// A resolved having-range query. Shared by the prover and the verifier — /// both build the grove path through /// [`DriveDocumentHavingQuery::indexed_property_name_tree_path`] and the -/// same bounded axis `PathQuery` through -/// [`AxisRangeBounds::i128_bounds`], so the two cannot drift on which -/// subtree or which range the proof is about. +/// secondary query through [`AxisRangeBounds::merk_query`], so the two +/// cannot drift on which subtree or which range the proof is about. #[derive(Debug, Clone)] #[cfg(any(feature = "server", feature = "verify"))] pub struct DriveDocumentHavingQuery<'a> { @@ -271,15 +284,18 @@ pub struct DriveDocumentHavingQuery<'a> { pub document_type_name: String, /// The covering ranked index. Its **last** property is the `GROUP /// BY` property and the final path segment; any leading properties - /// are pinned by [`Self::equality_prefix_values`]. + /// are pinned by [`Self::prefix_branches`]. pub index: &'a Index, - /// Encoded index-key bytes of each leading index property's pinned - /// value, in index-property order — empty for a single-property - /// index. Part of the prover/verifier agreement exactly as on the - /// ranked surface: the segments feed straight into the shared path - /// builder. Produced by - /// [`super::drive_document_ranked_query::index_picker::encode_equality_prefix_values`]. - pub equality_prefix_values: Vec>, + /// The prefix **branches** — one inner `Vec>` of encoded + /// path segments per branch, in index-property order; always at + /// least one branch, several exactly when the request carried a + /// multi-element `IN` pin. Part of the prover/verifier agreement + /// exactly as on the ranked surface. Produced by + /// [`super::drive_document_ranked_query::index_picker::encode_prefix_branches`] + /// — crate-private so the resolver is the only public constructor + /// and the encoder's invariants hold on every externally obtainable + /// value. + pub(crate) prefix_branches: Vec>>, /// Inclusive bounds on the aggregate. Carry the axis; the index must /// declare the matching `ranked_*` flag. pub bounds: AxisRangeBounds, @@ -301,17 +317,35 @@ pub struct DriveDocumentHavingQuery<'a> { #[cfg(any(feature = "server", feature = "verify"))] impl DriveDocumentHavingQuery<'_> { - /// Path of the terminal property-name tree the axis secondary hangs - /// off — identical to the ranked surface's path (including the - /// pinned-prefix segments of a compound index), because both read - /// the same indexed tree. See + /// The resolved prefix branches, in canonical order — one per `IN` + /// element (a single branch without an `IN`). Read-only: the field is + /// crate-private so the resolver's encoder invariants cannot be + /// bypassed by construction or mutation. + pub fn prefix_branches(&self) -> &[Vec>] { + &self.prefix_branches + } +} + +#[cfg(any(feature = "server", feature = "verify"))] +impl DriveDocumentHavingQuery<'_> { + /// Path of one branch's terminal property-name tree — identical to + /// the ranked surface's path (including the pinned-prefix segments + /// of a compound index), because both read the same indexed + /// tree(s). See /// [`DriveDocumentRankedQuery::indexed_property_name_tree_path`](super::drive_document_ranked_query::DriveDocumentRankedQuery::indexed_property_name_tree_path). - pub fn indexed_property_name_tree_path(&self) -> Result>, Error> { + pub fn indexed_property_name_tree_path(&self, branch: usize) -> Result>, Error> { + let prefix_values = + self.prefix_branches + .get(branch) + .ok_or(Error::Drive(DriveError::NotSupported( + "ranked and having-range queries addressed a prefix branch outside the \ + query's resolved branch set", + )))?; indexed_property_name_tree_path_for_index( &self.contract_id, &self.document_type_name, self.index, - &self.equality_prefix_values, + prefix_values, ) } } @@ -319,8 +353,9 @@ impl DriveDocumentHavingQuery<'_> { /// Resolve a validated [`DocumentHavingMode`] against a document type's /// indexes into the executable [`DriveDocumentHavingQuery`]: pick the /// covering index (shared with the ranked surface — both read the same -/// indexed tree), encode the equality pins into prefix-value path -/// segments, and assemble the query. +/// indexed tree), encode the prefix pins into prefix **branches** (one +/// branch for all-`==` pins, one branch per element of the single +/// permitted `IN`), and assemble the query. /// /// The **one** resolution path for the having surface, mirroring /// [`super::drive_document_ranked_query::index_picker::resolve_ranked_query_for_mode`]: @@ -342,9 +377,9 @@ pub fn resolve_having_query_for_mode<'a>( ) -> Result, Error> { let axis = mode.bounds.axis(); let pin_fields: Vec = mode - .equality_pins + .prefix_pins .iter() - .map(|(field, _)| field.clone()) + .map(|pin| pin.field.clone()) .collect(); let index = find_ranked_index_for_axis( indexes, @@ -359,19 +394,19 @@ pub fn resolve_having_query_for_mode<'a>( "having-range", axis, &mode.group_by_property, - &mode.equality_pins, + &mode.prefix_pins, &mode.aggregate_field, ), )) })?; - let equality_prefix_values = - encode_equality_prefix_values(document_type, index, &mode.equality_pins, platform_version)?; + let prefix_branches = + encode_prefix_branches(document_type, index, &mode.prefix_pins, platform_version)?; Ok(DriveDocumentHavingQuery { document_type, contract_id, document_type_name, index, - equality_prefix_values, + prefix_branches, bounds: mode.bounds, descending: mode.descending, limit: mode.limit, diff --git a/packages/rs-drive/src/query/drive_document_having_query/mode_detection/mod.rs b/packages/rs-drive/src/query/drive_document_having_query/mode_detection/mod.rs index 274dda5a872..957f69ee608 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/mode_detection/mod.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/mode_detection/mod.rs @@ -8,8 +8,9 @@ //! indexes. Available under `server` and `verify` for the same reason as //! [`super::super::drive_document_ranked_query::mode_detection`]: both //! sides must agree on which requests are well-formed and on the exact -//! bounds a well-formed one resolves to, because the bounds are echoed -//! (as a Merk query) inside the proof envelope. +//! bounds a well-formed one resolves to, because the verifier rebuilds +//! the bounded traversal from those bounds and re-executes the proof +//! against it. //! //! Versioned through //! `platform_version.drive.methods.document.query.detect_having_mode` — diff --git a/packages/rs-drive/src/query/drive_document_having_query/mode_detection/v0/mod.rs b/packages/rs-drive/src/query/drive_document_having_query/mode_detection/v0/mod.rs index 9cefa259130..4bcfe7fbf8d 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/mode_detection/v0/mod.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/mode_detection/v0/mod.rs @@ -7,7 +7,7 @@ use super::{AxisRangeBounds, DocumentHavingMode, MAX_HAVING_LIMIT}; use crate::error::query::QuerySyntaxError; use crate::error::Error; use crate::query::drive_document_ranked_query::mode_detection::{ - equality_pins_from_where_clauses, ranked_order_key, + prefix_pins_from_where_clauses, ranked_order_key, }; use crate::query::drive_document_ranked_query::{RankedAxis, RankedPaginationInputs}; use crate::query::having::{ @@ -29,10 +29,12 @@ use grovedb::element::indexed::AVG_FIXED_POINT_SCALE; /// ``` /// /// with no `OFFSET`, no `START AT` / `START AFTER`, exactly one -/// `GROUP BY` property, `WHERE` clauses (when present) that are -/// equality pins on distinct properties — one per leading property of a -/// covering compound ranked index, selecting which prefix's secondary -/// the bound reads — exactly one `HAVING` clause whose aggregate +/// `GROUP BY` property, `WHERE` clauses (when present) that pin the +/// covering compound ranked index's leading properties — one clause per +/// property, each an equality except that **at most one** clause may be +/// an `IN` whose elements fan the bound out across one prefix branch +/// per element (merged deterministically; see the ranked surface's +/// `prefix_pins_from_where_clauses`) — exactly one `HAVING` clause whose aggregate /// **is the selected aggregate** (same function, same field), an operator /// from the contiguous-range family (`=`, `>`, `>=`, `<`, `<=`, and the /// four `BETWEEN*` variants — `!=` and `IN` describe non-contiguous @@ -79,13 +81,15 @@ pub fn detect_having_mode_v0( // covering index's LAST property, whose distinct values are the // secondary's group keys. A compound ranked index filters each // prefix's groups separately — its leading properties are pinned by - // equality `where` clauses, never grouped over. + // `where` clauses (`==`, at most one of them a bounded `IN`), never + // grouped over. if group_by.len() != 1 { return Err(Error::Query(QuerySyntaxError::InvalidParameter(format!( "having-range queries require exactly one `group_by` property (the covering \ ranked index's trailing property); got {}. A compound ranked index bounds \ - each prefix's groups separately — pin every leading index property with an \ - equality `where` clause and `group_by` the trailing property.", + each prefix's groups separately — pin every leading index property with a \ + `where` clause (`==`, or a bounded `IN` on at most one of them) and \ + `group_by` the trailing property.", group_by.len() )))); } @@ -237,28 +241,29 @@ pub fn detect_having_mode_v0( } }; - // ---- WHERE: equality pins on the compound prefix ------------------ + // ---- WHERE: pins on the compound prefix --------------------------- // // Identical contract to the ranked surface: empty for the - // single-property form; for a compound ranked index, one equality - // pin per leading property selects which prefix's secondary the - // bound reads. Shape-only here; the index picker enforces the - // exact-cover rule. - let equality_pins = equality_pins_from_where_clauses(where_clauses)?; + // single-property form; for a compound ranked index, one pin per + // leading property — `==`, except at most one bounded branching + // `IN` — selects which prefix secondary or secondaries the bound + // reads. Shape-only here; the index picker enforces the exact-cover + // rule. + let prefix_pins = prefix_pins_from_where_clauses(where_clauses)?; // ---- LIMIT: required, 1 ..= MAX_HAVING_LIMIT --------------------- // // Required rather than defaulted for the same reason as ranked: the - // limit is echoed inside the proof envelope and re-checked by the - // verifier, so there is no server default a client could reproduce. + // limit is part of the traversal the verifier re-executes the proof + // against, so there is no server default a client could reproduce. // Required *especially* here, because a threshold can match // unboundedly many groups. let limit = pagination.limit.ok_or_else(|| { Error::Query(QuerySyntaxError::InvalidLimit(format!( "having-range queries require an explicit `limit` (1 ..= {MAX_HAVING_LIMIT}): \ a bound can match any number of groups, the walk stops at `limit`, and the \ - limit is echoed in the proof envelope and re-checked by the verifier, so \ - there is no server-side default a client could reproduce." + verifier re-executes the proof under the limit it rebuilds from the request, \ + so there is no server-side default a client could reproduce." ))) })?; if limit == 0 { @@ -270,9 +275,10 @@ pub fn detect_having_mode_v0( return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!( "`LIMIT {limit}` exceeds the having-range ceiling of {MAX_HAVING_LIMIT}; the \ proof commits one secondary entry per returned group, so its size grows \ - linearly in the limit. The ceiling is a hard limit, not a clamp, because the \ - limit is echoed in the proof envelope and re-checked by the verifier. Narrow \ - the bound to shrink the result set." + linearly in the limit. The ceiling is a hard limit, not a clamp, because \ + the limit is part of the traversal the client rebuilds to verify — a clamped \ + walk is one the client's reconstruction did not ask for. Narrow the bound to \ + shrink the result set." )))); } // Bounded by MAX_HAVING_LIMIT (a u16) immediately above. @@ -312,7 +318,7 @@ pub fn detect_having_mode_v0( limit, group_by_property, aggregate_field, - equality_pins, + prefix_pins, }) } diff --git a/packages/rs-drive/src/query/drive_document_having_query/tests.rs b/packages/rs-drive/src/query/drive_document_having_query/tests.rs index 702fb5d7277..7123569eee2 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/tests.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/tests.rs @@ -336,7 +336,7 @@ mod grammar { let message = format!("{error}"); assert!( message.contains("exactly one `group_by` property") - && message.contains("equality `where` clause"), + && message.contains("pin every leading index property"), "the rejection must steer to the pinned-prefix form, got: {error}" ); } @@ -557,27 +557,10 @@ mod bounds { } #[test] - fn i128_bounds_widen_every_axis_losslessly() { - assert_eq!( - AxisRangeBounds::Count { - lo: 101, - hi: u64::MAX - } - .i128_bounds(), - (101, u64::MAX as i128) - ); - assert_eq!( - AxisRangeBounds::Sum { - lo: i64::MIN, - hi: -5 - } - .i128_bounds(), - (i64::MIN as i128, -5) - ); - assert_eq!( - AxisRangeBounds::Avg { lo: -5, hi: 5 }.i128_bounds(), - (-5, 5) - ); + fn merk_query_direction_follows_descending() { + let bounds = AxisRangeBounds::Count { lo: 101, hi: 200 }; + assert!(bounds.merk_query(false).left_to_right); + assert!(!bounds.merk_query(true).left_to_right); } } @@ -1065,16 +1048,15 @@ mod execution { fn an_empty_match_set_reads_empty_and_proves_empty() { let (drive, contract) = setup_restaurants(); - // Empty secondary (no documents at all): the unproven read - // returns the empty list, and the unified bounded-axis prover - // proves it — an empty secondary is carried as empty proof - // bytes that the verifier resolves to a NULL_HASH secondary - // root, so the parent binding only passes when the element - // genuinely commits an empty secondary. (The old standalone - // range prover refused this state outright with "Cannot create - // proof for empty tree", which is why drive-abci still keeps - // `empty_ranking_proof_rejection` as a backstop for that error - // class.) + // Empty secondary (no documents at all): both the unproven read + // and — since the unified PathQuery prover replaced grovedb's + // standalone range prover — the PROVED read serve the empty + // answer: the envelope commits the element's empty secondary + // (NULL_HASH convention), so complete absence is authenticated + // rather than refused, exactly as on the ranked surface. (The + // retired standalone prover refused with "Cannot create proof + // for empty tree"; drive-abci's InvalidArgument mapping for + // that class is vestigial on this surface now.) let case = HavingCase::count(HavingOperator::GreaterThan, Value::U64(100), 10); let entries = entries_of(run(&drive, &contract, &case, false).expect("read succeeds")); assert!(entries.is_empty()); @@ -1107,14 +1089,6 @@ mod execution { /// verify under both — correctly, because the range boundaries /// prove both claims — so the distinguishing group is the point of /// the fixture. - /// - /// The unified verifier is query-as-input rather than echo-checked: - /// the invariant it holds is "a proof can never make the client - /// believe a wrong answer to the client's own query", not "a proof - /// only verifies under the exact request it was built for". A - /// tampered query whose answer the proof also correctly attests - /// (the limit case at the end) therefore verifies — to that - /// query's own correct answer. #[test] fn a_proof_does_not_verify_under_different_bounds() { let (drive, contract) = setup_restaurants(); @@ -1159,27 +1133,43 @@ mod execution { "a proof of `> 2` must not verify as `> 1`" ); - // Nor under a different direction. + // Nor under a different direction or limit. tampered_query = client_side_query(&contract, &over_two); tampered_query.descending = true; assert!(tampered_query .verify_having_range_proof(&proof, platform_version()) .is_err()); - // A different limit is NOT an echo check under the unified - // query-as-input verifier: the same bytes verify under any limit - // they can correctly answer. Here exactly one group matches - // `> 2`, so limit 5 and limit 10 have the same answer and the - // proof verifies to it — the sound outcome, since the entries - // ARE the right answer to the limit-5 question. What can never - // happen is an over-long answer: the verifier's own shape check - // caps entries at the query's limit. + // The limit is a CAP, not an identity parameter: the unified + // verifier re-executes the proof under the queried limit rather + // than comparing echoes, so a proof whose walk EXHAUSTED the + // bound (one entry here) is a complete, valid answer under any + // cap that admits it — the `limit 10` proof verifies under + // `limit 5` too, and that is sound. tampered_query = client_side_query(&contract, &over_two); tampered_query.limit = 5; - let (_, entries) = tampered_query - .verify_having_range_proof(&proof, platform_version()) - .expect("a proof whose answer also answers the smaller limit verifies under it"); - assert_eq!(keys_of(&entries), vec!["beta"]); + assert!( + tampered_query + .verify_having_range_proof(&proof, platform_version()) + .is_ok(), + "an exhausted-walk proof is a complete answer under a smaller cap too" + ); + + // What must NOT verify is the truncation direction: a proof cut + // by a SMALLER limit (2 of the 3 groups matching `> 0`, with + // more in range) misrepresents completeness under a larger cap, + // and the reconstruction rejects it for missing coverage of the + // rest of the bound. + let truncated = HavingCase::count(HavingOperator::GreaterThan, Value::U64(0), 2); + let truncated_proof = + proof_of(run(&drive, &contract, &truncated, true).expect("prove succeeds")); + let widened = HavingCase::count(HavingOperator::GreaterThan, Value::U64(0), 10); + assert!( + client_side_query(&contract, &widened) + .verify_having_range_proof(&truncated_proof, platform_version()) + .is_err(), + "a limit-truncated proof must not verify under a larger cap" + ); } /// A `having` on an axis no index declares is refused with the @@ -1527,10 +1517,12 @@ mod identifier_group_keys { entries, vec![ RankedEntry { + in_key: None, key: just_above.to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(161, 2)), }, RankedEntry { + in_key: None, key: well_above.to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(180, 2)), }, @@ -1849,10 +1841,12 @@ mod pinned_prefix { entries, vec![ RankedEntry { + in_key: None, key: b"english".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(161, 2)), }, RankedEntry { + in_key: None, key: b"art".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(180, 2)), }, @@ -1884,10 +1878,12 @@ mod pinned_prefix { y_entries, vec![ RankedEntry { + in_key: None, key: b"math".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(185, 2)), }, RankedEntry { + in_key: None, key: b"science".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(190, 2)), }, @@ -1920,34 +1916,59 @@ mod pinned_prefix { } } - /// `IN` on the prefix is rejected at detection with the - /// not-yet-supported message (v1 pins are equality-only), and a pin - /// on a property that is not the index's leading property fails - /// resolution. + /// `IN` on the leading prefix property resolves to one branch per + /// element, each bounded separately and merged (entries carrying + /// `in_key`), while a pin on a property that is not the index's + /// leading property still fails resolution. #[test] - fn in_prefix_and_wrong_pins_are_rejected() { + fn in_prefix_merges_branches_and_wrong_pins_are_rejected() { let (drive, contract) = setup_grades_compound_ranked(); insert_two_identities(&drive, &contract); + // `identityId IN [X, Y]` bounds each identity's own secondary + // and merges: ascending aggregate order, with every X entry + // (in_key = X's 32 bytes, all `1`s) sorting before Y's on the + // branch tie-break where aggregates tie. Bound is AVG > 80. let in_clause = vec![WhereClause { field: PREFIX_PROPERTY.to_string(), operator: WhereOperator::In, value: Value::Array(vec![ - Value::Identifier(IDENTITY_X), Value::Identifier(IDENTITY_Y), + Value::Identifier(IDENTITY_X), ]), }]; - let error = run(&drive, &contract, &in_clause, &[], false) - .expect_err("IN prefixes are not yet supported"); - match error { - Error::Query(QuerySyntaxError::Unsupported(message)) => { - assert!( - message.contains("IN") && message.contains("not yet supported"), - "the IN rejection must say it is a not-yet capability, got: {message}" - ); - } - other => panic!("expected Unsupported for an IN prefix, got {other:?}"), - } + let entries = entries_of( + run(&drive, &contract, &in_clause, &[], false).expect("the IN bound is served"), + ); + assert_eq!( + entries, + vec![ + RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"english".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(161, 2)), + }, + RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"art".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(180, 2)), + }, + RankedEntry { + in_key: Some(IDENTITY_Y.to_vec()), + key: b"math".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(185, 2)), + }, + RankedEntry { + in_key: Some(IDENTITY_Y.to_vec()), + key: b"science".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(190, 2)), + }, + ], + "the merged bound: X's english 80.5 and art 90, then Y's math \ + 92.5 and science 95, in ascending aggregate order with \ + branch-tagged entries — element order in the request must \ + not matter" + ); // A pin on the wrong property: `grade` is not the index's // leading property, so nothing covers [grade, class]. @@ -2082,6 +2103,7 @@ mod pinned_prefix { assert_eq!( entries, vec![RankedEntry { + in_key: None, key: b"math".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(170, 2)), }], @@ -2124,8 +2146,8 @@ mod pinned_prefix { ) .expect("the compound index covers the null-pinned request"); assert_eq!( - query.equality_prefix_values, - vec![Vec::::new()], + query.prefix_branches, + vec![vec![Vec::::new()]], "a null pin must encode as the write path's empty segment" ); let (root_hash, verified) = query @@ -2141,4 +2163,194 @@ mod pinned_prefix { .expect("root hash must be readable"), ); } + + /// A `null` element mixed with a real value in one `IN` pin: the + /// null branch is the write path's empty segment, which sorts + /// **first** in canonical branch order, and the merged bound covers + /// both subtrees — proved through the branched envelope. + #[test] + fn a_mixed_null_in_pin_bounds_both_prefixes_and_proves() { + const TAGGED_DOCTYPE: &str = "taggedGrade"; + let (drive, contract) = setup_grades_compound_ranked(); + let pv = platform_version(); + let document_type = contract + .document_type_for_name(TAGGED_DOCTYPE) + .expect("taggedGrade doctype exists"); + + for (i, (tag, class, grade)) in [ + (None, "math", 90i64), + (Some("honors"), "math", 95), + (Some("honors"), "art", 60), + ] + .iter() + .enumerate() + { + let mut doc: Document = document_type + .random_document(Some(9000 + i as u64), pv) + .expect("random document"); + let mut props = BTreeMap::new(); + if let Some(tag) = tag { + props.insert("tag".to_string(), Value::Text(tag.to_string())); + } + props.insert(GROUP_PROPERTY.to_string(), Value::Text(class.to_string())); + props.insert("grade".to_string(), Value::I64(*grade)); + doc.set_properties(props); + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&doc, None)), + owner_id: None, + }, + contract: &contract, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + pv, + None, + ) + .expect("expected to insert a tagged grade document"); + } + + let mixed_pin = vec![WhereClause { + field: "tag".to_string(), + operator: WhereOperator::In, + value: Value::Array(vec![Value::Text("honors".to_string()), Value::Null]), + }]; + let group_by = vec![GROUP_PROPERTY.to_string()]; + let having = vec![clause( + HavingAggregateFunction::Avg, + "grade", + HavingOperator::GreaterThan, + Value::U64(80), + )]; + let request = |prove: bool| DocumentHavingRequest { + contract: &contract, + document_type, + group_by: &group_by, + select: SelectProjection::avg("grade"), + having: &having, + order_by: &[], + where_clauses: &mixed_pin, + resolved_time_ranges: &[], + limit: Some(10), + offset: None, + has_start_at: false, + prove, + }; + + let entries = match drive + .execute_document_having_request(request(false), None, pv) + .expect("the mixed-null IN bound is served") + { + DocumentHavingResponse::Entries(entries) => entries, + DocumentHavingResponse::Proof(_) => panic!("expected entries, got a proof"), + }; + assert_eq!( + entries, + vec![ + RankedEntry { + in_key: Some(Vec::new()), + key: b"math".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }, + RankedEntry { + in_key: Some(b"honors".to_vec()), + key: b"math".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(95, 1)), + }, + ], + "both prefixes' in-bound groups, ascending; the same group key \ + appears twice, disambiguated by in_key, with the null branch's \ + empty in_key present (not None)" + ); + + let proof = match drive + .execute_document_having_request(request(true), None, pv) + .expect("the mixed-null IN prove succeeds") + { + DocumentHavingResponse::Proof(proof) => proof, + DocumentHavingResponse::Entries(_) => panic!("expected a proof, got entries"), + }; + let mode = detect_having_mode( + &SelectProjection::avg("grade"), + &group_by, + &having, + &[], + &mixed_pin, + RankedPaginationInputs { + limit: Some(10), + offset: None, + has_start_at: false, + }, + pv, + ) + .expect("the mixed-null case is well-formed"); + let query = resolve_having_query_for_mode( + contract.id_ref().to_buffer(), + document_type, + TAGGED_DOCTYPE.to_string(), + contract + .document_types() + .get(TAGGED_DOCTYPE) + .expect("taggedGrade doctype exists") + .indexes(), + &mode, + pv, + ) + .expect("the compound index covers the mixed-null request"); + assert_eq!( + query.prefix_branches, + vec![vec![Vec::new()], vec![b"honors".to_vec()]], + "canonical branch order: the empty (null) segment first" + ); + let (root_hash, verified) = query + .verify_having_range_proof(&proof, pv) + .expect("the mixed-null branched envelope must verify"); + assert_eq!(verified, entries); + assert_eq!( + root_hash, + drive + .grove + .root_hash(None, &pv.drive.grove_version) + .unwrap() + .expect("root hash must be readable"), + ); + } + + /// The having sibling of the ranked surface's absent-element test: + /// `identityId IN [X, never-written]` bounds X's groups and treats + /// the absent branch as empty, on both the read and the proved + /// path. + #[test] + fn an_absent_in_element_contributes_an_empty_branch() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades(&drive, &contract, 3000, &[(IDENTITY_X, "art", 90)]); + + let never_written = [9u8; 32]; + let pins = vec![WhereClause { + field: PREFIX_PROPERTY.to_string(), + operator: WhereOperator::In, + value: Value::Array(vec![ + Value::Identifier(IDENTITY_X), + Value::Identifier(never_written), + ]), + }]; + let entries = + entries_of(run(&drive, &contract, &pins, &[], false).expect("the read succeeds")); + assert_eq!( + entries, + vec![RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"art".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }], + "only the existing branch is bounded; the absent one is empty, not an error" + ); + + assert_proof_round_trips(&drive, &contract, &pins, &[], &entries); + } } diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/branches.rs b/packages/rs-drive/src/query/drive_document_ranked_query/branches.rs new file mode 100644 index 00000000000..8f7e9c24bcb --- /dev/null +++ b/packages/rs-drive/src/query/drive_document_ranked_query/branches.rs @@ -0,0 +1,386 @@ +//! Branch mechanics for `IN`-pinned ranked / having-range queries — +//! shared by the executors (walk one secondary per branch, merge) and +//! the verifiers (verify one branched envelope, re-merge), so both +//! sides implement one comparator and one grove-path decomposition. +//! +//! ## Why a merge needs no proof of its own +//! +//! Each branch's walk is proved complete inside one grovedb **branched +//! envelope** (shared ancestor layers once, one multi-key proof at the +//! branching level, one secondary proof per branch, one root hash), +//! and the merged page is a *deterministic function* of the branch +//! pages: any union entry that precedes a returned entry in the merge +//! order is preceded, within its own branch, by fewer than `limit` +//! entries — so it is in its branch's returned page. The union's first +//! `limit` entries are therefore contained in the union of the branch +//! pages, and re-merging verified branch pages reconstructs a +//! complete, correctly ordered page. (This is the same argument for +//! both surfaces: "first `limit` in merge order restricted to the +//! branch" is top-k for ranked and the in-bound range prefix for +//! having.) +//! +//! ## The merge order +//! +//! `(aggregate in walk direction, branch segment ascending, group key in +//! walk direction)`. The middle term is the canonical branch tie-break: +//! the **encoded** segment bytes of the branch's `IN` position, fixed +//! ascending regardless of walk direction, independent of the caller's +//! element order — which also makes `null` (the empty segment) sort +//! first deterministically. + +use super::{RankedAxis, RankedEntry, RankedEntryValue}; +use crate::error::drive::DriveError; +use crate::error::Error; +use grovedb::operations::proof::indexed_axis::AxisEntries; +#[cfg(feature = "server")] +use grovedb::{ + query_result_type::QueryResultType, AxisKeys, GroveDb, PathQuery, PathQueryRun, TransactionArg, +}; +#[cfg(feature = "server")] +use grovedb_costs::CostContext; +#[cfg(feature = "server")] +use grovedb_query::AxisQuery; +#[cfg(feature = "server")] +use grovedb_version::version::GroveVersion; +use std::cmp::Ordering; + +/// The position (index into a branch's segment list) at which the +/// branches differ — the `IN` pin's position in the covering index's +/// leading properties. `None` when there is a single branch (no `IN`), +/// or when branches are degenerate duplicates (the encoder rejects +/// those, so it is unreachable off the resolver path). +pub fn varying_position(branches: &[Vec>]) -> Option { + let first = branches.first()?; + for other in &branches[1..] { + for (position, (a, b)) in first.iter().zip(other.iter()).enumerate() { + if a != b { + return Some(position); + } + } + } + None +} + +/// The `in_key` for one branch: the encoded segment at the varying +/// position. `None` for single-branch queries — entries of an un-branched +/// response carry no discriminator, keeping the shape byte-identical to +/// the pre-`IN` surface. +pub fn branch_in_key(branches: &[Vec>], branch: usize) -> Option> { + if branches.len() < 2 { + return None; + } + let position = varying_position(branches)?; + branches.get(branch)?.get(position).cloned() +} + +/// Compare two entries' aggregates on the same axis. The executors and +/// verifiers only ever merge entries of one axis, so a variant mismatch +/// is corrupted state, reported rather than ordered. +fn aggregate_cmp(a: &RankedEntryValue, b: &RankedEntryValue) -> Result { + match (a, b) { + (RankedEntryValue::Count(a), RankedEntryValue::Count(b)) => Ok(a.cmp(b)), + (RankedEntryValue::Sum(a), RankedEntryValue::Sum(b)) => Ok(a.cmp(b)), + (RankedEntryValue::AvgFixedPoint(a), RankedEntryValue::AvgFixedPoint(b)) => Ok(a.cmp(b)), + _ => Err(Error::Drive(DriveError::CorruptedDriveState( + "branch merge compared entries from different aggregate axes".to_string(), + ))), + } +} + +/// Merge per-branch pages into the final page: tag each entry with its +/// branch's `in_key`, order by the merge comparator, cut at `limit`. +/// +/// `per_branch` must be indexed identically to `branches` (the resolver +/// produces both in canonical branch order). Branch pages arrive sorted +/// by the walk; the merged set is small (≤ branches × limit), so a +/// plain total sort is used instead of a k-way heap — simpler to keep +/// byte-identical between server and verifier. +pub fn merge_branch_pages( + per_branch: Vec>, + branches: &[Vec>], + descending: bool, + limit: usize, +) -> Result, Error> { + let mut merged: Vec = Vec::with_capacity(per_branch.iter().map(Vec::len).sum()); + for (branch, entries) in per_branch.into_iter().enumerate() { + let in_key = branch_in_key(branches, branch); + merged.extend(entries.into_iter().map(|mut entry| { + entry.in_key = in_key.clone(); + entry + })); + } + let mut comparison_error: Option = None; + merged.sort_by(|a, b| { + let aggregate = match aggregate_cmp(&a.value, &b.value) { + Ok(ordering) => ordering, + Err(e) => { + comparison_error.get_or_insert(e); + Ordering::Equal + } + }; + let directional = |ordering: Ordering| { + if descending { + ordering.reverse() + } else { + ordering + } + }; + directional(aggregate) + .then_with(|| a.in_key.cmp(&b.in_key)) + .then_with(|| directional(a.key.cmp(&b.key))) + }); + if let Some(e) = comparison_error { + return Err(e); + } + merged.truncate(limit); + Ok(merged) +} + +/// The `(shared prefix, branch keys, shared suffix)` decomposition of a +/// branch path set — the triple `PathQuery::new_branched_axis` takes. +pub type BranchPathDecomposition = (Vec>, Vec>, Vec>); + +/// Decompose per-branch grove paths into the `(shared prefix, branch +/// keys, shared suffix)` triple grovedb's branched proof primitives +/// take. The paths differ at exactly one segment position by +/// construction (one `IN` pin); anything else is an internal +/// resolution error. +pub fn decompose_branch_paths(paths: &[Vec>]) -> Result { + let first = paths.first().ok_or_else(|| { + Error::Drive(DriveError::CorruptedDriveState( + "branch decomposition over zero paths".to_string(), + )) + })?; + let mut varying: Option = None; + for other in &paths[1..] { + if other.len() != first.len() { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "branch paths of different lengths".to_string(), + ))); + } + for (position, (a, b)) in first.iter().zip(other.iter()).enumerate() { + if a != b { + match varying { + None => varying = Some(position), + Some(existing) if existing == position => {} + Some(_) => { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "branch paths differ at more than one segment".to_string(), + ))); + } + } + } + } + } + let position = varying.ok_or_else(|| { + Error::Drive(DriveError::CorruptedDriveState( + "branch paths are identical; the encoder rejects duplicate branches".to_string(), + )) + })?; + let prefix = first[..position].to_vec(); + let keys = paths + .iter() + .map(|path| path[position].clone()) + .collect::>(); + let suffix = first[position + 1..].to_vec(); + Ok((prefix, keys, suffix)) +} + +/// Translate one branch's keys-only [`AxisKeys`] page into drive entries +/// on the requested axis — the unproved twin of [`axis_entries_to_ranked`], +/// used by the single-snapshot branched reads. +#[cfg(feature = "server")] +pub(crate) fn axis_keys_to_ranked( + axis: RankedAxis, + keys: AxisKeys, +) -> Result, Error> { + match (axis, keys) { + (RankedAxis::Count, AxisKeys::Count(pairs)) => Ok(pairs + .into_iter() + .map(|(count, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::Count(count), + }) + .collect()), + (RankedAxis::Sum, AxisKeys::Sum(pairs)) => Ok(pairs + .into_iter() + .map(|(sum, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::Sum(sum), + }) + .collect()), + (RankedAxis::Avg, AxisKeys::Avg(pairs)) => Ok(pairs + .into_iter() + .map(|(avg, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::AvgFixedPoint(avg), + }) + .collect()), + (axis, _) => Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branch of a {axis:?} read returned keys of a different axis shape" + )))), + } +} + +/// The entire unproved branched-read sequence, shared by the ranked and +/// having-range executors so the read/prove contract has exactly ONE +/// implementation: decompose the branch paths, run one branched +/// keys-only grovedb call, validate the run shape and the branch-set +/// identity, translate and cap each branch's page, and merge with the +/// shared comparator. +/// +/// The union is served from **one committed state**: the call always +/// runs under a grovedb snapshot read transaction taken here, so every +/// per-branch absence probe and axis walk inside grovedb's branched arm +/// reads the same RocksDB snapshot and a block commit landing mid-read +/// cannot mix committed states into the merged page. +/// +/// A caller-supplied transaction is **rejected**, mirroring the +/// branched provers: an ordinary grovedb transaction reads the latest +/// committed state on every operation, so forwarding it would reopen +/// the exact tear the snapshot closes — and there is no way to tell an +/// ordinary transaction from a snapshot-pinned one at this boundary. +/// Transactional callers read per prefix element (each single-branch +/// read is one grovedb operation and honors the transaction exactly), +/// or commit first. +/// +/// `page_cap` is the surface's page bound (`k` for ranked, `limit` for +/// having-range): each branch may return at most that many entries and +/// the merged union is cut at it. `surface` names the caller in the +/// corrupted-state messages. +#[cfg(feature = "server")] +#[allow(clippy::too_many_arguments)] +pub(crate) fn read_branched_union( + grove: &GroveDb, + surface: &'static str, + prefix_branches: &[Vec>], + paths: &[Vec>], + axis: RankedAxis, + axis_query: AxisQuery, + page_cap: usize, + descending: bool, + transaction: TransactionArg, + grove_version: &GroveVersion, +) -> Result, Error> { + if transaction.is_some() { + return Err(Error::Drive(DriveError::NotSupported( + "an IN-pinned (branched) unproved read under a caller transaction is not \ + supported: an ordinary grovedb transaction reads the latest committed state on \ + every operation, so a concurrent commit could tear the union across branches — \ + read per prefix element under the transaction, or pass no transaction (the read \ + then runs under an internal snapshot)", + ))); + } + let (prefix, keys, suffix) = decompose_branch_paths(paths)?; + let path_query = + PathQuery::new_branched_axis(prefix, keys.clone(), suffix, axis_query.keys_only()); + let snapshot_transaction = grove.start_snapshot_read_transaction(); + // Test-only seam: lets a regression test land a commit deterministically + // INSIDE the window — after the snapshot is taken, before the read runs — + // proving the automatic snapshot selection on the production `None` path. + #[cfg(test)] + test_hooks::AFTER_BRANCHED_SNAPSHOT.with(|hook| { + if let Some(hook) = hook.borrow_mut().as_mut() { + hook(); + } + }); + let CostContext { value, cost: _ } = grove.run_path_query( + &path_query, + true, + true, + true, + QueryResultType::QueryKeyElementPairResultType, + Some(&snapshot_transaction), + grove_version, + ); + let run = value.map_err(|e| Error::GroveDB(Box::new(e)))?; + let PathQueryRun::BranchedAxisKeys(branches) = run else { + return Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branched keys-only {surface} read returned a non-branched shape" + )))); + }; + if branches.len() != keys.len() || branches.iter().map(|(key, _)| key).ne(keys.iter()) { + return Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branched {surface} read returned a different branch set than the request resolved" + )))); + } + let per_branch = branches + .into_iter() + .map(|(_key, page)| { + let entries = match page { + None => Vec::new(), + Some(page) => axis_keys_to_ranked(axis, page)?, + }; + if entries.len() > page_cap { + return Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branch of a {surface} read returned {} entries for a page cap of \ + {page_cap}", + entries.len(), + )))); + } + Ok(entries) + }) + .collect::, Error>>()?; + merge_branch_pages(per_branch, prefix_branches, descending, page_cap) +} + +/// Translate one branch's verified [`AxisEntries`] into drive entries +/// on the requested axis — the same mapping the single-path verifiers +/// perform, shared here so both surfaces' branched verifiers agree. +pub fn axis_entries_to_ranked( + axis: RankedAxis, + entries: AxisEntries, +) -> Result, Error> { + match (axis, entries) { + (RankedAxis::Count, AxisEntries::Count(entries)) => Ok(entries + .into_iter() + .map(|entry| entry.key_pair()) + .map(|(count, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::Count(count), + }) + .collect()), + (RankedAxis::Sum, AxisEntries::Sum(entries)) => Ok(entries + .into_iter() + .map(|entry| entry.key_pair()) + .map(|(sum, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::Sum(sum), + }) + .collect()), + (RankedAxis::Avg, AxisEntries::Avg(entries)) => Ok(entries + .into_iter() + .map(|entry| entry.key_pair()) + .map(|(avg, key)| RankedEntry { + in_key: None, + key, + value: RankedEntryValue::AvgFixedPoint(avg), + }) + .collect()), + (axis, other) => Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branch of a {axis:?} proof verified to {} entries of a different axis shape", + other.len() + )))), + } +} + +/// Test-only seam for [`read_branched_union`]: invoked after the +/// internal snapshot transaction is taken and before the branched call +/// runs, so a test can land a commit deterministically inside the +/// window and prove the production `None` path's automatic snapshot +/// selection end-to-end. Last in the file: clippy's +/// `items_after_test_module` forbids items after a `#[cfg(test)]` +/// module. +#[cfg(test)] +pub(crate) mod test_hooks { + use std::cell::RefCell; + thread_local! { + pub(crate) static AFTER_BRANCHED_SNAPSHOT: RefCell>> = + const { RefCell::new(None) }; + } +} diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/drive_dispatcher.rs b/packages/rs-drive/src/query/drive_document_ranked_query/drive_dispatcher.rs index 912f4ab4a70..bfba7788bef 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/drive_dispatcher.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/drive_dispatcher.rs @@ -63,8 +63,10 @@ pub struct DocumentRankedRequest<'a> { /// field); its direction is the ranking direction. pub order_by: &'a [OrderClause], /// Structured `where` clauses. Empty for the single-property form; - /// equality pins on the covering compound index's leading - /// properties for the pinned-prefix form. + /// pins on the covering compound index's leading properties for + /// the pinned-prefix form: one equality pin per property, of which + /// at most one may instead be a bounded `IN` (one branch per + /// element, merged; entries then carry `in_key`). pub where_clauses: &'a [WhereClause], /// The fields among `where_clauses` whose equality clause was produced by /// `IN_TIME_RANGE` resolution (see diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs index e24b07b3aa8..b049038174f 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs @@ -10,13 +10,14 @@ //! Whole module is gated `feature = "server"` via the parent's //! `pub mod execute_top_k;` declaration. -use super::{DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue, RankedPage}; +use super::branches::{axis_keys_to_ranked, decompose_branch_paths, read_branched_union}; +use super::{DriveDocumentRankedQuery, RankedPage}; use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; use dpp::version::PlatformVersion; use grovedb::query_result_type::QueryResultType; -use grovedb::{AxisKeys, PathQuery, PathQueryRun, TransactionArg}; +use grovedb::{PathQuery, PathQueryRun, TransactionArg}; use grovedb_costs::CostContext; use grovedb_query::AxisQuery; @@ -27,13 +28,22 @@ impl DriveDocumentRankedQuery<'_> { /// the direction and the tie contract. /// /// Fewer than `k` entries is normal (the index simply has fewer - /// groups than `offset + k`) and is not an error. A missing path - /// *is* an error rather than an empty result: the indexed - /// property-name tree is created when the contract is registered, so - /// its absence means the contract-level state is not what the - /// request claims, not that the ranking is empty. (An index with no - /// documents yet has the tree, with an empty secondary, and yields - /// an empty entry list.) + /// groups than `offset + k`) and is not an error. On an `IN`-pinned + /// request, an element whose branch chain is missing at ANY depth — + /// the branch key itself, or any deeper pinned segment under a + /// *present* key — contributes an **empty branch** (union + /// semantics), exactly as the proved envelope authenticates it, and + /// the union is served from **one committed state**: the branched + /// read always runs under a grovedb snapshot read transaction, so + /// every per-branch probe and walk reads the same RocksDB snapshot + /// (a caller transaction is rejected on this shape, mirroring the + /// branched prover — read per prefix element under a transaction). + /// A missing path under a single `==` pin *is* an error rather than + /// an empty result: the indexed property-name tree is created when + /// the contract is registered, so its absence means the + /// contract-level state is not what the request claims, not that + /// the ranking is empty. (An index with no documents yet has the + /// tree, with an empty secondary, and yields an empty entry list.) /// /// The paginated grovedb primitive is used unconditionally, with /// `offset = 0` standing in for an unpaginated request, so the @@ -72,13 +82,61 @@ impl DriveDocumentRankedQuery<'_> { drive: &Drive, transaction: TransactionArg, platform_version: &PlatformVersion, + ) -> Result { + self.reject_offset_with_branches()?; + if self.prefix_branches.len() > 1 { + // ONE grovedb call for the whole union, pinned to one + // committed state and merged with the shared comparator — + // the entire sequence lives in + // `branches::read_branched_union`, shared with the + // having-range surface so the two cannot drift. `offset` is + // grammar-rejected with `IN`, so `skipped` is always 0 here. + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let entries = read_branched_union( + &drive.grove, + "ranked", + &self.prefix_branches, + &paths, + self.axis, + AxisQuery::top_k( + self.axis.into(), + self.k, + self.offset as u64, + self.descending, + ), + self.k as usize, + self.descending, + transaction, + &platform_version.drive.grove_version, + )?; + return Ok(RankedPage { + skipped: 0, + entries, + }); + } + self.execute_top_k_no_proof_branch(0, drive, transaction, platform_version) + } + + /// One branch's page — the entire pre-`IN` executor, parameterized + /// by which prefix branch's terminal tree it walks. + fn execute_top_k_no_proof_branch( + &self, + branch: usize, + drive: &Drive, + transaction: TransactionArg, + platform_version: &PlatformVersion, ) -> Result { let grove_version = &platform_version.drive.grove_version; - let path = self.indexed_property_name_tree_path()?; + let path = self.indexed_property_name_tree_path(branch)?; - // The same axis PathQuery the prove path uses, with the - // keys-only projection: the ranking pairs are read straight off - // the pinned secondary view, no primary values resolved. + // The cost is dropped rather than `.unwrap()`-ed: + // `CostContext::unwrap` is infallible (it drops the cost field) + // but reads like a panicking unwrap at the call site. Dropping it + // is all there is to do with it — nothing meters a query on this + // surface. grovedb computes the `OperationCost` because its API + // always does, and it ends here. let path_query = PathQuery::new_axis( path, AxisQuery::top_k( @@ -89,69 +147,30 @@ impl DriveDocumentRankedQuery<'_> { ) .keys_only(), ); - - // The cost is dropped rather than `.unwrap()`-ed: - // `CostContext::unwrap` is infallible (it drops the cost field) - // but reads like a panicking unwrap at the call site. Dropping it - // is all there is to do with it — nothing meters a query on this - // surface: neither this executor's caller nor the dispatcher - // above it accumulates or charges the cost, and no credit is - // debited for a read. grovedb computes the `OperationCost` - // because its API always does, and it ends here. let CostContext { value, cost: _ } = drive.grove.run_path_query( &path_query, true, true, true, - QueryResultType::QueryPathKeyElementTrioResultType, + QueryResultType::QueryKeyElementPairResultType, transaction, grove_version, ); - let PathQueryRun::AxisKeys { keys, skipped } = - value.map_err(|e| Error::GroveDB(Box::new(e)))? - else { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked {:?} read ran to a non-axis-keys result shape", - self.axis - )))); + let run = value.map_err(|e| Error::GroveDB(Box::new(e)))?; + let PathQueryRun::AxisKeys { keys, skipped } = run else { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a keys-only ranked read returned a different result shape".to_string(), + ))); }; + let entries = axis_keys_to_ranked(self.axis, keys)?; + // A `RankedPage` traversal always attests its skip; its absence + // would mean grovedb answered a different traversal than asked. let skipped = skipped.ok_or_else(|| { - Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked {:?} read carried no skip count for a paginated walk", - self.axis - ))) + Error::Drive(DriveError::CorruptedDriveState( + "a paginated ranked read carried no skip attestation".to_string(), + )) })?; - let entries = match (self.axis, keys) { - (RankedAxis::Count, AxisKeys::Count(pairs)) => pairs - .into_iter() - .map(|(count, key)| RankedEntry { - key, - value: RankedEntryValue::Count(count), - }) - .collect::>(), - (RankedAxis::Sum, AxisKeys::Sum(pairs)) => pairs - .into_iter() - .map(|(sum, key)| RankedEntry { - key, - value: RankedEntryValue::Sum(sum), - }) - .collect::>(), - (RankedAxis::Avg, AxisKeys::Avg(pairs)) => pairs - .into_iter() - .map(|(avg, key)| RankedEntry { - key, - value: RankedEntryValue::AvgFixedPoint(avg), - }) - .collect::>(), - (axis, other) => { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked {axis:?} read returned {} pairs of a different axis shape", - other.len() - )))); - } - }; - // `k` is the contract with the caller, and on the prove path it // is re-checked inside the proof envelope. Asserting it here too // keeps the no-proof and prove responses shape-identical: a @@ -168,25 +187,23 @@ impl DriveDocumentRankedQuery<'_> { Ok(RankedPage { skipped, entries }) } - /// Generate the axis-ordered top-k proof for this query, through the - /// unified `PathQuery` surface (grovedb's only public proof surface - /// for indexed-axis reads): the query is - /// [`PathQuery::new_axis_top_k`] and the envelope is a GroveDBProof - /// V1 carrying an axis descent into the queried secondary. + /// Generate the grovedb indexed-axis paginated top-k proof for this + /// query. /// /// The envelope commits the walked secondary entries, the number of /// entries skipped to reach them, the primary's root hash, the - /// sibling axes' root hashes, and the ordinary layer chain up to the - /// grovedb root — so the client reconstructs the platform root hash - /// from it. `(axis, k, offset, descending)` are **not echoed** in the - /// envelope: the verifier takes the client's own reconstruction of - /// the same `PathQuery` as input, so a proof generated for a - /// different ranking — or a different page — fails verification - /// rather than being silently reinterpreted. That is why `k` is - /// validated rather than clamped upstream (a clamped `k` would - /// produce a proof the client's own query rejects). + /// sibling axes' root hashes, and a per-ancestor attestation chain + /// up to the grovedb root — so the client reconstructs the platform + /// root hash from it. `(axis, k, offset, descending)` bind by + /// RECONSTRUCTION, not echo: the verifier rebuilds the same + /// `PathQuery` from the request and + /// [`grovedb::GroveDb::verify_path_query`] re-executes the proof + /// against that traversal, so a proof for a different ranking or a + /// different page fails to cover it; that is why `k` is validated + /// rather than clamped upstream (a clamped `k` would produce a page + /// the client's reconstruction did not ask for). /// - /// The paginated traversal is used unconditionally, with + /// The paginated primitive is used unconditionally, with /// `offset = 0` for offset-free requests, so there is exactly one /// proof shape on this surface: a client never has to guess which of /// two envelope formats a server produced. @@ -206,11 +223,64 @@ impl DriveDocumentRankedQuery<'_> { pub fn execute_top_k_with_proof( &self, drive: &Drive, - _transaction: TransactionArg, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + self.reject_offset_with_branches()?; + // grovedb's `prove_query` — since the indexed-axis prover + // retirement, the only proof surface — proves COMMITTED state + // only: it takes one internal snapshot and threads it through + // every proof layer, and cannot see the caller's transaction. + // Serving a proof for a different snapshot than the unproved + // read would silently desynchronize the two paths, so a + // transactional prove fails closed, single-prefix and branched + // alike. + if transaction.is_some() { + return Err(Error::Drive(DriveError::NotSupported( + "a ranked proof is generated from committed state only: grovedb's \ + prove_query cannot see the caller's transaction — commit first", + ))); + } + if self.prefix_branches.len() > 1 { + // One grovedb **branched** envelope: shared ancestor layers + // once, one multi-key proof at the branching level, one + // secondary proof per branch — a single proof with a single + // root hash. The verifier re-derives the branch set from + // the request, so a dropped, duplicated, or reordered + // branch fails there. + let grove_version = &platform_version.drive.grove_version; + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let (prefix, keys, suffix) = decompose_branch_paths(&paths)?; + let path_query = PathQuery::new_branched_axis( + prefix, + keys, + suffix, + AxisQuery::top_k( + self.axis.into(), + self.k, + self.offset as u64, + self.descending, + ), + ); + let CostContext { value, cost: _ } = + drive.grove.prove_query(&path_query, None, grove_version); + return value.map_err(|e| Error::GroveDB(Box::new(e))); + } + self.execute_top_k_with_proof_branch(0, drive, platform_version) + } + + /// One branch's proof — the entire pre-`IN` prover, parameterized by + /// the prefix branch. + fn execute_top_k_with_proof_branch( + &self, + branch: usize, + drive: &Drive, platform_version: &PlatformVersion, ) -> Result, Error> { let grove_version = &platform_version.drive.grove_version; - let path = self.indexed_property_name_tree_path()?; + let path = self.indexed_property_name_tree_path(branch)?; let path_query = PathQuery::new_axis_top_k( path, self.axis.into(), @@ -218,12 +288,6 @@ impl DriveDocumentRankedQuery<'_> { self.offset as u64, self.descending, ); - - // The unified prover proves committed state — it takes no - // transaction. The parameter is kept for signature stability with - // the no-proof executor; the query dispatch passes `None` on this - // surface anyway (queries answer from committed state). - // // Same destructure-don't-unwrap rationale as the no-proof arm. let CostContext { value, cost: _ } = drive.grove.prove_query(&path_query, None, grove_version); diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/index_picker.rs b/packages/rs-drive/src/query/drive_document_ranked_query/index_picker.rs index 86fdaf4414b..bce9c36351e 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/index_picker.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/index_picker.rs @@ -7,12 +7,11 @@ //! and the SDK verifier both call these so they land on the same index //! (and therefore the same grove path) for the same request. -use super::{DocumentRankedMode, DriveDocumentRankedQuery, RankedAxis}; +use super::{DocumentRankedMode, DriveDocumentRankedQuery, PrefixPin, RankedAxis}; use crate::error::query::QuerySyntaxError; use crate::error::Error; use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; use dpp::data_contract::document_type::{DocumentTypeRef, Index}; -use dpp::platform_value::Value; use dpp::version::PlatformVersion; use std::collections::BTreeMap; @@ -28,7 +27,7 @@ use std::collections::BTreeMap; /// - every **leading** property is pinned: each appears (by name) among /// `equality_pin_fields`. Lengths matching plus the pins being /// distinct (enforced upstream by -/// [`super::mode_detection::equality_pins_from_where_clauses`]) makes +/// [`super::mode_detection::prefix_pins_from_where_clauses`]) makes /// this set equality, so no pin is left over either; /// - it declares the ranking keyword for `axis` /// ([`RankedAxis::required_index_keyword`]); @@ -113,9 +112,9 @@ pub fn find_ranked_index_for_mode<'b>( mode: &DocumentRankedMode, ) -> Option<&'b Index> { let pin_fields: Vec = mode - .equality_pins + .prefix_pins .iter() - .map(|(field, _)| field.clone()) + .map(|pin| pin.field.clone()) .collect(); find_ranked_index_for_axis( indexes, @@ -128,8 +127,9 @@ pub fn find_ranked_index_for_mode<'b>( /// Resolve a validated [`DocumentRankedMode`] against a document type's /// indexes into the executable [`DriveDocumentRankedQuery`]: pick the -/// covering index, encode the equality pins into prefix-value path -/// segments, and assemble the query. +/// covering index, encode the prefix pins into prefix **branches** (one +/// branch for all-`==` pins, one branch per element of the single +/// permitted `IN`), and assemble the query. /// /// This is the **one** resolution path — the server's executors and the /// SDK's proof helpers both call it, which is what guarantees a proof @@ -161,19 +161,19 @@ pub fn resolve_ranked_query_for_mode<'a>( "ranked", mode.axis, &mode.group_by_property, - &mode.equality_pins, + &mode.prefix_pins, &mode.aggregate_field, ), )) })?; - let equality_prefix_values = - encode_equality_prefix_values(document_type, index, &mode.equality_pins, platform_version)?; + let prefix_branches = + encode_prefix_branches(document_type, index, &mode.prefix_pins, platform_version)?; Ok(DriveDocumentRankedQuery { document_type, contract_id, document_type_name, index, - equality_prefix_values, + prefix_branches, axis: mode.axis, descending: mode.descending, k: mode.k, @@ -191,32 +191,32 @@ pub fn no_covering_index_message( surface: &str, axis: RankedAxis, group_by_property: &str, - equality_pins: &[(String, Value)], + prefix_pins: &[PrefixPin], aggregate_field: &str, ) -> String { let pin_fields = || { - equality_pins + prefix_pins .iter() - .map(|(field, _)| field.as_str()) + .map(|pin| pin.field.as_str()) .collect::>() .join(", ") }; - let index_shape = if equality_pins.is_empty() { + let index_shape = if prefix_pins.is_empty() { format!("a single-property index on `{group_by_property}`") } else { format!( "a compound index on [{}, {group_by_property}] (every leading property pinned \ - by an equality `where` clause, the trailing property grouped over)", + by an equality or `IN` `where` clause, the trailing property grouped over)", pin_fields() ) }; format!( "no ranked index covers `group_by = [{group_by_property}]`{} on the {axis:?} axis \ for this {surface} query: the document type needs {index_shape} declaring `{}`{}", - if equality_pins.is_empty() { + if prefix_pins.is_empty() { String::new() } else { - format!(" with equality pins on [{}]", pin_fields()) + format!(" with pins on [{}]", pin_fields()) }, axis.required_index_keyword(), if aggregate_field.is_empty() { @@ -227,61 +227,185 @@ pub fn no_covering_index_message( ) } -/// Encode the equality pins into the grove path's prefix-value -/// segments: for each **leading** property of `index`, in index order, -/// the pinned value's index-key bytes -/// (`DocumentType::serialize_value_for_key` — the same encoding the -/// write path used to key that prefix's value tree). +/// Encode the resolved prefix pins into **branches** — one +/// `Vec>` of prefix path segments per branch, in index-property +/// order (the same order and encoding the write path used to key those +/// prefix value trees). A request with only `==` pins yields exactly +/// one branch; the (at most one) `IN` pin yields one branch per +/// element. /// /// This is part of the prover/verifier agreement: server executors and /// the SDK's proof helpers both come through here, so a pinned value -/// can only ever name one subtree, identically on both sides. +/// can only ever name one subtree — and a branch *set* only ever one +/// ordered subtree list — identically on both sides. Branch order is +/// canonical: ascending by encoded segment bytes, independent of the +/// caller's element order (which also makes `null`, the empty segment, +/// sort first deterministically). /// /// `index` must have been picked by [`find_ranked_index_for_axis`] /// against these same pins — every leading property is then guaranteed -/// a pin. A value the property's type cannot encode (a string against -/// an integer property, an out-of-range integer) is a caller error, -/// reported as a query-syntax rejection naming the property. -pub fn encode_equality_prefix_values( +/// a pin. A value the property's type cannot encode is a caller error +/// naming the property; two `IN` elements that encode to the same +/// segment (two spellings of one value) are one branch and are rejected +/// as a duplicate rather than walked twice. +pub fn encode_prefix_branches( document_type: DocumentTypeRef, index: &Index, - equality_pins: &[(String, Value)], + prefix_pins: &[PrefixPin], platform_version: &PlatformVersion, -) -> Result>, Error> { +) -> Result>>, Error> { let leading = &index.properties[..index.properties.len().saturating_sub(1)]; - leading + // Enforced BEFORE any encoding: the ceiling bounds every downstream + // cost (encode, sort, clone, walk, proof size), so an oversized pin + // must not buy that work first. The post-product branch count check + // below stays as a backstop. + if prefix_pins + .iter() + .any(|pin| pin.values.len() > super::MAX_PREFIX_IN_BRANCHES) + { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` prefix pin fans out into more branches than the ranked surface serves \ + — narrow the element list or issue several requests", + ), + )); + } + let per_property: Vec>> = leading .iter() .map(|property| { - let (_, value) = equality_pins + let pin = prefix_pins .iter() - .find(|(field, _)| field == &property.name) + .find(|pin| pin.field == property.name) .ok_or_else(|| { Error::Query(QuerySyntaxError::InvalidWhereClauseComponents( "internal resolution mismatch: the picked compound ranked index has \ - a leading property with no equality pin — the index picker and the \ - prefix encoder disagreed on the pins", + a leading property with no pin — the index picker and the prefix \ + encoder disagreed on the pins", )) })?; - // A null pin addresses the subtree the write walkers create - // for an **absent** value: they encode it as - // `get_raw_for_document_type(..).unwrap_or_default()` — an - // empty path segment — for user and system properties alike. - // Null must short-circuit here because the system-property - // encoders (`$updatedAt`, `$creatorId`, …) reject null before - // any encoding happens, which would make the stored - // empty-segment prefix unaddressable. - if value.is_null() { - return Ok(Vec::new()); - } - document_type - .serialize_value_for_key(&property.name, value, platform_version) - .map_err(|e| { - Error::Query(QuerySyntaxError::InvalidParameter(format!( - "the equality pin on `{}` does not encode as that property's \ - index key: {e}", - property.name - ))) + let mut encoded = pin + .values + .iter() + .map(|value| { + // A null pin addresses the subtree the write walkers + // create for an **absent** value: they encode it as + // `get_raw_for_document_type(..).unwrap_or_default()` + // — an empty path segment — for user and system + // properties alike. Null must short-circuit here + // because the system-property encoders (`$updatedAt`, + // `$creatorId`, …) reject null before any encoding + // happens, which would make the stored empty-segment + // prefix unaddressable. + if value.is_null() { + return Ok(Vec::new()); + } + document_type + .serialize_value_for_key(&property.name, value, platform_version) + .map_err(|e| { + Error::Query(QuerySyntaxError::InvalidParameter(format!( + "the pin on `{}` does not encode as that property's \ + index key: {e}", + property.name + ))) + }) }) + .collect::, Error>>()?; + if encoded.len() > 1 { + encoded.sort(); + if encoded.windows(2).any(|pair| pair[0] == pair[1]) { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` pin's elements encode to the same index key: two \ + spellings of one value are one prefix branch — deduplicate \ + the element list", + ), + )); + } + } + Ok(encoded) }) - .collect() + .collect::, Error>>()?; + + // Defense in depth at the shared choke point: the grammar enforces + // both invariants upstream, but this function is `pub` and the + // prover/verifier agreement hangs off it, so a mis-built pin set + // must fail here rather than collapse to zero branches (a + // downstream panic) or fan out into an unbounded cartesian product + // (which would also break the one-varying-position assumption + // `in_key` and the merge order rely on). + if per_property.iter().any(|candidates| candidates.is_empty()) { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "internal resolution mismatch: a prefix pin carries no values", + ), + )); + } + if per_property + .iter() + .filter(|candidates| candidates.len() > 1) + .count() + > 1 + { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "internal resolution mismatch: more than one branching pin — the grammar \ + admits at most one `IN` across the prefix properties", + ), + )); + } + + // A single `null` pin encodes as the empty path segment; the branched + // proof grammar (`PathQuery::new_branched_axis`) cannot address an + // empty segment in the shared prefix or suffix, so a null `==` pin + // combined with an `IN` would serve the unproved read and fail the + // prove — the exact proved/unproved divergence this surface forbids. + // Rejected for any non-branching pin position, conservatively: issue + // one request per `IN` element to combine null pins with multiple + // prefixes. `null` as an ELEMENT of the `IN` itself stays legal — it + // is a branch key, which the envelope addresses and authenticates + // like any other. + let has_branching_pin = per_property.iter().any(|candidates| candidates.len() > 1); + if has_branching_pin + && per_property + .iter() + .any(|candidates| candidates.len() == 1 && candidates[0].is_empty()) + { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` prefix pin cannot be combined with a `null` pin: null addresses the \ + absent-value prefix through an empty path segment, which the branched proof \ + cannot express — issue one request per `IN` element instead", + ), + )); + } + + // The grammar admits at most one multi-value pin, so this product + // is |IN| branches (or exactly one), already in canonical order + // because the only varying position was sorted above. + let mut branches: Vec>> = vec![Vec::with_capacity(leading.len())]; + for candidates in per_property { + branches = branches + .into_iter() + .flat_map(|prefix| { + candidates.iter().map(move |segment| { + let mut branch = prefix.clone(); + branch.push(segment.clone()); + branch + }) + }) + .collect(); + } + // The documented hard ceiling on branch fan-out, enforced at the + // shared choke point too: this function is `pub`, and everything + // downstream (encoding, sorting, per-branch walks, proof size) is + // linear in the branch count. + if branches.len() > super::MAX_PREFIX_IN_BRANCHES { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` prefix pin fans out into more branches than the ranked surface serves \ + — narrow the element list or issue several requests", + ), + )); + } + Ok(branches) } diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs index 68a52700540..5b54a342116 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs @@ -50,8 +50,15 @@ //! on the grouped (terminal) property itself would ask for a //! *filtered* ranking, which no secondary can express — it is sorted //! by aggregate, not by group key — and is rejected rather than -//! silently ignored, as is any non-equality prefix clause (`IN` -//! included: one walk per element is a future multi-`IN` capability). +//! silently ignored, as is any non-equality prefix clause except one +//! `IN`: exactly one leading pin may carry 2..=[`MAX_PREFIX_IN_BRANCHES`] +//! distinct elements (a single-element `IN` normalizes to the +//! equality pin), read as one walk per element and merged by +//! `(aggregate, encoded pin, group key)`, proved in a single branched +//! `PathQuery` envelope with per-element authenticated absence. A +//! `null` pin cannot combine with an `IN` (null addresses its prefix +//! through an empty path segment the branched proof cannot express), +//! and `OFFSET` is rejected together with `IN`. //! 2. **`limit` is mandatory, `offset` is depth-bounded, `start_at` is //! refused.** //! `limit` is the `k` of the walk and the ranked surface has no @@ -84,6 +91,8 @@ use dpp::platform_value::Value; #[cfg(any(feature = "server", feature = "verify"))] pub use grovedb::element::indexed::AVG_FIXED_POINT_SCALE as RANKED_AVG_SCALE; +#[cfg(any(feature = "server", feature = "verify"))] +pub(crate) mod branches; #[cfg(any(feature = "server", feature = "verify"))] pub mod index_picker; #[cfg(any(feature = "server", feature = "verify"))] @@ -117,16 +126,29 @@ mod tests; /// is rejected with /// [`crate::error::query::QuerySyntaxError::InvalidLimit`] rather than /// silently truncated. Truncation would be especially treacherous here -/// because the proof is verified against the client's own -/// reconstruction of the same axis `PathQuery` (with the client's `k`) -/// by [`grovedb::GroveDb::verify_path_query`] — a server-side clamp -/// would produce a proof the client's own query rejects. +/// because `k` is part of the traversal the client reconstructs for +/// [`grovedb::GroveDb::verify_path_query`] — a server-side clamp would +/// produce a page the client's reconstruction did not ask for. /// /// There is deliberately **no companion ceiling on `OFFSET`**; see the /// module docs and [`DriveDocumentRankedQuery::offset`]. #[cfg(any(feature = "server", feature = "verify"))] pub const MAX_RANKED_LIMIT: u16 = 100; +/// Hard ceiling on the element count of the (at most one) `IN` prefix +/// pin — the number of prefix **branches** one ranked / having-range +/// request may fan out into. +/// +/// Each element is one full secondary walk and one proof branch, each +/// carrying up to `limit` committed entries plus boundary commitments, +/// so worst-case proof size is `MAX_PREFIX_IN_BRANCHES × +/// MAX_RANKED_LIMIT` entries (≈100–150 KB at the ceiling). A hard +/// rejection rather than a clamp, for the same reason as the limit: the +/// branch set is bound into the branched proof envelope and re-checked +/// by the verifier. +#[cfg(any(feature = "server", feature = "verify"))] +pub const MAX_PREFIX_IN_BRANCHES: usize = 10; + /// The `ORDER BY` field name that means "the group's `COUNT(*)`". /// /// `COUNT(*)` has no field to name, so the ranked grammar needs some @@ -145,7 +167,8 @@ pub const RANKED_COUNT_ORDER_KEY: &str = "$count"; /// Which per-group aggregate the groups are ranked by. /// /// Maps 1:1 onto [`grovedb::element::IndexAxis`], the axis tag stored in -/// an indexed tree's TLV and echoed in the proof envelope. Kept as a +/// an indexed tree's TLV and rebuilt into the `PathQuery` a verifier +/// re-executes proofs against. Kept as a /// separate drive-side type (rather than re-exporting grovedb's) so the /// query surface's error messages and validation can talk about /// `rankedCountable` / `rankedSummable` / `rankedAverageable` — contract @@ -256,6 +279,13 @@ pub struct RankedEntry { pub key: Vec, /// The group's aggregate on the requested axis. pub value: RankedEntryValue, + /// The branch this entry came from, on an `IN`-pinned request: the + /// encoded index-key segment of the `IN` position's pinned value + /// (empty bytes for the `null` branch). `None` on single-branch + /// responses — the same group key can appear under two prefixes, so + /// only a merged page needs the discriminator. See + /// [`branches::branch_in_key`]. + pub in_key: Option>, } /// A resolved ranked query. Shared by the prover and the verifier — both @@ -279,17 +309,23 @@ pub struct DriveDocumentRankedQuery<'a> { pub document_type_name: String, /// The covering ranked index. Its **last** property is the `GROUP /// BY` property and the final path segment; any leading properties - /// are pinned by [`Self::equality_prefix_values`]. + /// are pinned by [`Self::prefix_branches`]. pub index: &'a Index, - /// Encoded index-key bytes of each leading index property's pinned - /// value, in index-property order — empty for a single-property - /// index. Together with `index` these determine the grove path - /// (each leading property contributes a name segment and a value - /// segment), so they are as much a part of the prover/verifier - /// agreement as the path builder itself. Produced by - /// [`index_picker::encode_equality_prefix_values`] from the - /// request's equality `where` pins. - pub equality_prefix_values: Vec>, + /// The prefix **branches** — one inner `Vec>` of encoded + /// index-key path segments per branch, each in index-property + /// order. Always at least one branch; a single-property index or an + /// all-`==` pinned request has exactly one (possibly empty) branch, + /// and the (at most one) `IN` pin contributes one branch per + /// element, in canonical encoded-ascending order. Together with + /// `index` these determine the grove path(s), so the branch set is + /// as much a part of the prover/verifier agreement as the path + /// builder itself. Produced by + /// [`index_picker::encode_prefix_branches`] from the request's + /// `where` pins — crate-private so the resolver is the only public + /// constructor and the encoder's invariants (nonempty, canonical + /// order, distinct keys, one varying position, the fan-out ceiling) + /// hold on every externally obtainable value. + pub(crate) prefix_branches: Vec>>, /// Which aggregate the groups are ranked by. Must be covered by /// `index`'s matching `ranked_*` flag. pub axis: RankedAxis, @@ -332,6 +368,44 @@ pub struct DriveDocumentRankedQuery<'a> { pub offset: u32, } +#[cfg(any(feature = "server", feature = "verify"))] +impl DriveDocumentRankedQuery<'_> { + /// The resolved prefix branches, in canonical order — one per `IN` + /// element (a single branch without an `IN`). Read-only: the field is + /// crate-private so the resolver's encoder invariants cannot be + /// bypassed by construction or mutation. + pub fn prefix_branches(&self) -> &[Vec>] { + &self.prefix_branches + } + + /// Reject the one cross-field combination the request grammar + /// forbids but public construction can still express: a + /// multi-branch (`IN`) query carrying a non-zero `offset`. + /// Rank-skip is attested from ONE secondary's counted commitments; + /// applied independently per branch it would page each branch + /// separately, merge the independently skipped pages, and report + /// `skipped: 0` — and the verifier, reconstructing the same + /// malformed per-branch traversal, would not reject it. Enforced at + /// every execution, proving and verification boundary, because + /// `offset` is a public field and the mode-detection grammar check + /// can be bypassed by building a mode or mutating a resolved query + /// directly. + pub(crate) fn reject_offset_with_branches(&self) -> Result<(), crate::error::Error> { + if self.prefix_branches.len() > 1 && self.offset != 0 { + return Err(crate::error::Error::Query( + crate::error::query::QuerySyntaxError::InvalidLimit( + "`OFFSET` cannot combine with an `IN` prefix pin: rank-skip is attested \ + from one secondary's counted commitments, and an `IN` merges several \ + secondaries with no counted structure over the union. Page one prefix \ + at a time (`==` pin + `OFFSET`), or drop the offset." + .to_string(), + ), + )); + } + Ok(()) + } +} + /// A page of a ranked result: the entries, plus how many ranks were /// actually skipped to reach them. /// @@ -403,7 +477,7 @@ pub struct RankedPaginationInputs { /// one executor pair (no-proof / proof) and all of its variation is in /// these values. /// -/// Not `Eq`: the equality pins carry [`Value`]s, whose float variant +/// Not `Eq`: the prefix pins carry [`Value`]s, whose float variant /// keeps the type at `PartialEq`. #[derive(Debug, Clone, PartialEq)] #[cfg(any(feature = "server", feature = "verify"))] @@ -423,12 +497,29 @@ pub struct DocumentRankedMode { /// [`RankedAxis::Count`] (`COUNT(*)`); the index's `summable` /// property for [`RankedAxis::Sum`] / [`RankedAxis::Avg`]. pub aggregate_field: String, - /// The equality `where` pins, `(property, value)` per clause — - /// exactly one per leading property of the covering compound index, - /// in whatever order the request supplied them (the resolver - /// re-orders them into index-property order when it encodes the - /// path). Empty for the single-property form. Shape-validated only: - /// the index-aware checks (does a compound index exist whose leading - /// properties these pin?) live in [`index_picker`]. - pub equality_pins: Vec<(String, Value)>, + /// The `where` prefix pins — one [`PrefixPin`] per clause, exactly + /// one per leading property of the covering compound index, in + /// whatever order the request supplied them (the resolver re-orders + /// them into index-property order when it encodes the path). A pin + /// normally carries one value (an `==` clause); at most one carries + /// several (the single permitted branching `IN`). Empty for the + /// single-property form. Shape-validated only: the index-aware + /// checks (does a compound index exist whose leading properties + /// these pin?) live in [`index_picker`]. + pub prefix_pins: Vec, +} + +/// One pinned leading property of the covering compound index. +/// +/// An `==` clause pins exactly one value; the (at most one) `IN` clause +/// pins several, each element selecting its own prefix **branch** — the +/// executors walk one secondary per branch and merge deterministically. +/// A single-element `IN` is normalized to an equality pin at grammar +/// time, so `values.len() > 1` is exactly "this is the branching pin". +#[derive(Debug, Clone, PartialEq)] +pub struct PrefixPin { + /// The pinned property's name. + pub field: String, + /// The pinned value(s); never empty. + pub values: Vec, } diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/mod.rs index 17b2d46b5f5..2c80fe16696 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/mod.rs @@ -88,4 +88,4 @@ pub fn ranked_order_key(select: &SelectProjection) -> &str { mod v0; // Re-exported so the dispatcher's callers (`drive_dispatcher`, the // test suites) keep addressing the frozen grammar by its old path. -pub use v0::{detect_ranked_mode_v0, equality_pins_from_where_clauses}; +pub use v0::{detect_ranked_mode_v0, prefix_pins_from_where_clauses}; diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs index abe883991e6..2dcae92d5ac 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs @@ -3,6 +3,7 @@ //! this file is a v0 internal: a later grammar version gets its own //! `vN/` sibling rather than editing this one. +use super::super::{PrefixPin, MAX_PREFIX_IN_BRANCHES}; use super::ranked_order_key; use super::{ DocumentRankedMode, RankedAxis, RankedPaginationInputs, MAX_RANKED_LIMIT, @@ -15,53 +16,34 @@ use crate::query::projection::{SelectFunction, SelectProjection}; use crate::query::{OrderClause, WhereClause, WhereOperator}; use dpp::platform_value::Value; -/// Translate a request's `where` clauses into equality pins — -/// `(property, value)` pairs, one per clause — for the ranked and -/// having-range surfaces. +/// Translate a request's `where` clauses into prefix pins — one +/// [`PrefixPin`] per clause, carrying the pinned property and its +/// value(s): one value from an `==` clause, several from the (at most +/// one) `IN` — for the ranked and having-range surfaces. /// /// Both surfaces read a compound index's per-prefix secondary by /// descending through one prefix value tree per **leading** index /// property, and only an equality clause names a single value tree to -/// descend into. So the grammar is: every `where` clause must be an -/// equality (`==`), each on a distinct property. `IN` is rejected -/// separately from the other operators because it *will* eventually be -/// serviceable (one branch per element, once multi-`IN` branching lands -/// on the document query surface) — the message says so — while a range -/// operator on a prefix property can never pin a single subtree. +/// descend into. So the grammar is: every `where` clause is an equality +/// (`==`) on a distinct property, except that **at most one** clause may +/// be an `IN` — each of its elements selects its own prefix *branch*, +/// and the executors walk one secondary per branch and merge (see +/// [`MAX_PREFIX_IN_BRANCHES`] for the fan-out ceiling). A +/// range operator on a prefix property can never pin a subtree and is +/// rejected outright. A single-element `IN` is normalized to an +/// equality pin, so the degenerate case is byte-identical to `==`. /// /// Shape-only, like everything in this module: whether the pinned /// properties are exactly the leading properties of a covering compound -/// index is the index picker's call. -pub fn equality_pins_from_where_clauses( +/// index is the index picker's call, and element distinctness is +/// enforced post-encoding by the prefix encoder (two spellings of one +/// value are one branch, and must be rejected as a duplicate). +pub fn prefix_pins_from_where_clauses( where_clauses: &[WhereClause], -) -> Result, Error> { - let mut pins: Vec<(String, Value)> = Vec::with_capacity(where_clauses.len()); +) -> Result, Error> { + let mut pins: Vec = Vec::with_capacity(where_clauses.len()); + let mut branching_field: Option<&str> = None; for clause in where_clauses { - match clause.operator { - WhereOperator::Equal => {} - WhereOperator::In => { - return Err(Error::Query(QuerySyntaxError::Unsupported(format!( - "`{} IN …` is not yet supported on a ranked / having-range query's \ - prefix properties: each `IN` element names a different prefix value \ - tree, so serving it means one secondary walk per element and a merged \ - result — a future capability layered on multi-`IN` branching. Pin \ - each leading index property with `==`, or issue one request per \ - value.", - clause.field - )))); - } - _ => { - return Err(Error::Query( - QuerySyntaxError::InvalidWhereClauseComponents( - "a ranked / having-range query's `where` clauses must pin the covering \ - compound index's leading properties with `==`: the per-prefix \ - secondary lives under one prefix value tree per leading property, and \ - only an equality names a single value tree to descend into — a range \ - operator cannot pin a prefix", - ), - )); - } - } if clause.field.is_empty() { return Err(Error::Query( QuerySyntaxError::InvalidWhereClauseComponents( @@ -69,15 +51,76 @@ pub fn equality_pins_from_where_clauses( ), )); } - if pins.iter().any(|(field, _)| field == &clause.field) { + if pins.iter().any(|pin| pin.field == clause.field) { return Err(Error::Query( QuerySyntaxError::InvalidWhereClauseComponents( "a ranked / having-range query pins the same property twice: each leading \ - index property takes exactly one equality pin", + index property takes exactly one pin", ), )); } - pins.push((clause.field.clone(), clause.value.clone())); + let values = match clause.operator { + WhereOperator::Equal => vec![clause.value.clone()], + WhereOperator::In => { + let Value::Array(elements) = &clause.value else { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` pin's right operand must be an array of candidate values", + ), + )); + }; + if elements.is_empty() { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "an `IN` pin's element list is empty: it can match nothing, and \ + a pin that cannot match any prefix is a caller error", + ), + )); + } + if elements.len() > MAX_PREFIX_IN_BRANCHES { + return Err(Error::Query(QuerySyntaxError::InvalidParameter(format!( + "an `IN` pin fans out into one secondary walk (and one proof \ + branch) per element; {} elements exceeds the ceiling of {}. \ + Split the request.", + elements.len(), + MAX_PREFIX_IN_BRANCHES + )))); + } + // Only a multi-element `IN` branches; a singleton is an + // equality pin and never counts against the one-`IN` + // budget — in either clause order. + if elements.len() > 1 { + if let Some(first) = branching_field { + return Err(Error::Query(QuerySyntaxError::Unsupported(format!( + "a ranked / having-range query takes at most one branching \ + `IN` across its prefix properties (`{first}` already carries \ + it): several `IN`s multiply into a cartesian product of \ + prefix branches, each a separate secondary walk inside one \ + proof — a fan-out the branch ceiling exists to prevent. Pin \ + `{}` with `==` or a single-element `IN`.", + clause.field + )))); + } + branching_field = Some(clause.field.as_str()); + } + elements.clone() + } + _ => { + return Err(Error::Query( + QuerySyntaxError::InvalidWhereClauseComponents( + "a ranked / having-range query's `where` clauses must pin the covering \ + compound index's leading properties with `==` (or one `IN`): the \ + per-prefix secondary lives under one prefix value tree per leading \ + property, and only equality names value trees to descend into — a \ + range operator cannot pin a prefix", + ), + )); + } + }; + pins.push(PrefixPin { + field: clause.field.clone(), + values, + }); } Ok(pins) } @@ -95,12 +138,19 @@ pub fn equality_pins_from_where_clauses( /// with no `HAVING`, no `START AT` / `START AFTER`, exactly one /// `GROUP BY` property, exactly one `ORDER BY` clause naming the /// selected aggregate, `1 ≤ n ≤` [`MAX_RANKED_LIMIT`], and any -/// `m ≥ 0`. `WHERE` clauses, when present, must be **equality pins** on -/// distinct properties — one per leading property of a covering -/// compound ranked index (see -/// [`equality_pins_from_where_clauses`]); the ranking then reads that -/// pinned prefix's own secondary. With no `where` the covering index is -/// single-property, exactly as before. +/// `m ≥ 0`. `WHERE` clauses, when present, pin distinct properties — +/// one per leading property of a covering compound ranked index — each +/// an **equality**, except that at most one may be a bounded **`IN`** +/// (2..=10 distinct elements; a singleton `IN` normalizes to `==` — see +/// [`prefix_pins_from_where_clauses`]). A `==`-pinned request reads +/// that pinned prefix's own secondary; an `IN` fans the read out into +/// one prefix branch per element, walked separately and merged +/// deterministically, with merged entries carrying their branch's +/// `in_key`. A **non-zero** `OFFSET` is rejected together with the +/// `IN`: the counted rank-skip is attested per-secondary and cannot +/// span the union (`OFFSET 0` stays legal as the offset-free +/// spelling). With no `where` the covering index is single-property, +/// exactly as before. /// /// `DESC` walks the axis from the largest aggregate down (the "top n" /// reading), `ASC` from the smallest up (the "bottom n" reading). @@ -141,14 +191,16 @@ pub fn detect_ranked_mode_v0( // Zero group_by would ask to rank a single global aggregate against // itself. Two or more is rejected because a compound ranked index // ranks per prefix, not across a compound grouping: its leading - // properties are pinned by equality `where` clauses, and only the - // trailing property is grouped over. + // properties are pinned by `where` clauses (`==`, at most one of + // them a bounded `IN`), and only the trailing property is grouped + // over. if group_by.len() != 1 { return Err(Error::Query(QuerySyntaxError::InvalidParameter(format!( "ranked queries require exactly one `group_by` property (the covering ranked \ index's trailing property); got {}. A compound ranked index ranks each \ - prefix's groups separately — pin every leading index property with an \ - equality `where` clause and `group_by` the trailing property.", + prefix's groups separately — pin every leading index property with a \ + `where` clause (`==`, or a bounded `IN` on at most one of them) and \ + `group_by` the trailing property.", group_by.len() )))); } @@ -257,26 +309,27 @@ pub fn detect_ranked_mode_v0( // ---- WHERE: equality pins on the compound prefix ------------------ // // Empty for the single-property form. For a compound ranked index, - // each `where` clause must pin one leading index property with `==` - // — that is what selects which prefix's secondary the walk reads - // (per-prefix semantics: there is no global cross-prefix ordering to - // serve). Anything other than a distinct-property equality is - // rejected loudly here; whether the pinned set matches a covering - // index's leading properties exactly is the index picker's call. - let equality_pins = equality_pins_from_where_clauses(where_clauses)?; + // each `where` clause pins one leading index property with `==`, + // except that at most one may use a bounded branching `IN` (a + // singleton `IN` normalizes to equality). These pins select the + // prefix secondary or secondaries the walk reads (per-prefix + // semantics: the only cross-prefix ordering is the branch merge); + // whether the pinned property set exactly matches a covering + // index's leading properties is the index picker's call. + let prefix_pins = prefix_pins_from_where_clauses(where_clauses)?; // ---- LIMIT: required, 1 ..= MAX_RANKED_LIMIT --------------------- // - // Required rather than defaulted: `k` is echoed inside the proof - // envelope and re-checked by the verifier, so a server-chosen + // Required rather than defaulted: `k` is part of the traversal the + // verifier re-executes the proof against, so a server-chosen // default would be a number the client never agreed to and could // not reproduce when rebuilding the query to verify. let limit = pagination.limit.ok_or_else(|| { Error::Query(QuerySyntaxError::InvalidLimit(format!( "ranked queries require an explicit `limit` (1 ..= {MAX_RANKED_LIMIT}): it is \ - the number of groups the walk returns, and it is echoed in the proof envelope \ - and re-checked by the verifier, so there is no server-side default a client \ - could reproduce. Write `ORDER BY {expected_order_key} DESC LIMIT 1` for the \ + the number of groups the walk returns, and the verifier re-executes the \ + proof against the traversal it rebuilds from the request, so there is no \ + server-side default a client could reproduce. Write `ORDER BY {expected_order_key} DESC LIMIT 1` for the \ single best-ranked group." ))) })?; @@ -290,8 +343,8 @@ pub fn detect_ranked_mode_v0( "`LIMIT {limit}` exceeds the ranked-query ceiling of {MAX_RANKED_LIMIT}; the \ proof commits one secondary entry per returned group, so its size grows \ linearly in the limit. Narrow the request — the ceiling is a hard limit, not \ - a clamp, because `k` is echoed in the proof envelope and re-checked by the \ - verifier. Deep results are reached with `OFFSET`, whose skip work is bounded by \ + a clamp, because `k` is part of the traversal the client rebuilds to \ + verify — a clamped page is one the client's reconstruction did not ask for. Deep results are reached with `OFFSET`, whose skip work is bounded by \ tree depth and does not grow with the offset." )))); } @@ -328,6 +381,24 @@ pub fn detect_ranked_mode_v0( ))); } + // ---- OFFSET × IN: mutually exclusive ----------------------------- + // + // Rank-skip is served from counted subtree commitments *inside one + // secondary*; there is no counted structure spanning a branch + // union, so a cross-branch offset would have to walk (and prove) + // the skipped region in every branch — silently expensive. Callers + // who need deep pages issue per-prefix requests, where offset works + // exactly as documented. + if offset > 0 && prefix_pins.iter().any(|pin| pin.values.len() > 1) { + return Err(Error::Query(QuerySyntaxError::InvalidLimit( + "`OFFSET` cannot combine with an `IN` prefix pin: rank-skip is attested from \ + one secondary's counted commitments, and an `IN` merges several secondaries \ + with no counted structure over the union. Page one prefix at a time (`==` \ + pin + `OFFSET`), or drop the offset." + .to_string(), + ))); + } + Ok(DocumentRankedMode { axis, descending, @@ -335,6 +406,6 @@ pub fn detect_ranked_mode_v0( offset, group_by_property, aggregate_field, - equality_pins, + prefix_pins, }) } diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/path.rs b/packages/rs-drive/src/query/drive_document_ranked_query/path.rs index ed3dccf0620..fe4beeeccc0 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/path.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/path.rs @@ -1,10 +1,10 @@ //! The grove path a ranked read / proof / verification is issued against. //! -//! This is the **prover/verifier-agreement boundary** for the ranked -//! surface. Unlike the count and sum surfaces there is no `PathQuery` to -//! agree on — grovedb's indexed-axis envelope carries the query shape -//! itself and re-checks `(axis, k, descending)` at verification time — so -//! the entire shared contract is these path segments. Prover and verifier +//! This is one half of the **prover/verifier-agreement boundary** for +//! the ranked surface: both sides build the same axis `PathQuery` — +//! these path segments plus the traversal (`axis, k, offset, +//! descending`) — and grovedb re-executes the proof against that +//! reconstruction at verification time. Prover and verifier //! both call [`DriveDocumentRankedQuery::indexed_property_name_tree_path`]; //! a divergence here surfaces as a failed root-hash reconstruction, not a //! wrong answer, but it is still the one place the two sides must not @@ -24,14 +24,14 @@ use dpp::data_contract::document_type::Index; /// tree. See [`DriveDocumentRankedQuery::indexed_property_name_tree_path`] /// for the segment layout. /// -/// `equality_prefix_values` carries the **encoded index-key bytes** of +/// The branch's prefix segments carry the **encoded index-key bytes** of /// each leading property's pinned value, in index-property order — one /// per property before the terminal one. Empty for a single-property /// index. The arity must match exactly: a compound index's terminal /// tree sits under one prefix value tree per leading property, and only -/// an equality `where` clause can name those values, so a missing or -/// surplus value means the caller resolved the wrong index — a typed -/// error, not a guess. +/// a `where` pin (an equality, or one element of the single permitted +/// `IN`) can name those values, so a missing or surplus value means the +/// caller resolved the wrong index — a typed error, not a guess. pub(crate) fn indexed_property_name_tree_path_for_index( contract_id: &[u8; 32], document_type_name: &str, @@ -49,8 +49,8 @@ pub(crate) fn indexed_property_name_tree_path_for_index( "ranked and having-range queries over a compound index require exactly one \ encoded equality value per leading index property: the axis secondary lives \ on the index's terminal property-name tree, which for a compound index sits \ - under one prefix value tree per leading property, and only an equality \ - `where` clause can name those values", + under one prefix value tree per leading property, and only a `where` pin (an \ + equality, or one element of the single permitted `IN`) can name those values", ))); } let mut path = Vec::with_capacity(5 + 2 * leading_properties.len()); @@ -84,7 +84,7 @@ impl DriveDocumentRankedQuery<'_> { /// For a compound index `[p1, …, pn]`, each leading property /// contributes two segments — its name and the **encoded index-key /// bytes of its pinned value** (from - /// [`Self::equality_prefix_values`]) — and the terminal property + /// [`Self::prefix_branches`]) — and the terminal property /// name closes the path: /// /// ```text @@ -101,12 +101,22 @@ impl DriveDocumentRankedQuery<'_> { /// Errors when the number of encoded prefix values does not match /// the index's leading-property count — the fail-closed backstop /// for a caller that resolved the query against the wrong index. - pub fn indexed_property_name_tree_path(&self) -> Result>, Error> { + /// + /// `branch` indexes into [`Self::prefix_branches`]; single-branch + /// queries (no `IN` pin) always pass `0`. + pub fn indexed_property_name_tree_path(&self, branch: usize) -> Result>, Error> { + let prefix_values = + self.prefix_branches + .get(branch) + .ok_or(Error::Drive(DriveError::NotSupported( + "ranked and having-range queries addressed a prefix branch outside the \ + query's resolved branch set", + )))?; indexed_property_name_tree_path_for_index( &self.contract_id, &self.document_type_name, self.index, - &self.equality_prefix_values, + prefix_values, ) } } diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs index 5298bd31cb3..fe0ce508fb1 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs @@ -256,8 +256,9 @@ fn count_star_is_ordered_by_the_dollar_count_sentinel() { } /// `LIMIT 0` selects nothing and `LIMIT > MAX_RANKED_LIMIT` is refused -/// rather than clamped — a clamp would produce a proof whose echoed `k` -/// the client's own reconstruction rejects. The boundary itself is +/// rather than clamped — a clamp would truncate the server's walk below +/// the cap the client's rebuilt `PathQuery` demands coverage for, so the +/// client's own reconstruction rejects the proof. The boundary itself is /// accepted. #[test] fn k_is_bounded_to_one_through_max_ranked_limit() { @@ -280,9 +281,9 @@ fn k_is_bounded_to_one_through_max_ranked_limit() { } /// `LIMIT` is mandatory in ranked mode. There is no server-side default -/// because `k` is echoed inside the proof envelope and re-checked by the -/// verifier: a number the client never chose is a number it cannot -/// reproduce when rebuilding the query to verify. +/// because the verifier re-executes proofs against a `PathQuery` rebuilt +/// from the request: a number the client never chose is a number it +/// cannot reproduce when rebuilding the query to verify. #[test] fn limit_is_required() { let error = detect_avg(false, None, None).expect_err( @@ -495,9 +496,9 @@ fn sum_and_avg_selects_require_a_field() { /// properties. Detection is shape-only: an equality clause becomes a /// pin (whether a compound index actually covers it is the resolver's /// call — see `pins_without_a_covering_compound_index_are_rejected`); -/// anything that is not a distinct-property equality is refused -/// loudly, `IN` with its own not-yet message because it will become -/// serviceable once multi-`IN` branching lands. +/// anything that is not a distinct-property equality — or the one +/// permitted `IN`, which resolves to a multi-value branching pin — is +/// refused loudly. #[test] fn where_clauses_resolve_to_equality_pins_and_reject_everything_else() { // Equality pin: accepted at detection, carried in the mode. @@ -516,8 +517,11 @@ fn where_clauses_resolve_to_equality_pins_and_reject_everything_else() { ) .expect("an equality pin is a well-formed prefix pin"); assert_eq!( - mode.equality_pins, - vec![("chefId".to_string(), Value::Text("alpha".to_string()))] + mode.prefix_pins, + vec![PrefixPin { + field: "chefId".to_string(), + values: vec![Value::Text("alpha".to_string())], + }] ); // A range operator can never pin a single prefix value tree. @@ -540,14 +544,17 @@ fn where_clauses_resolve_to_equality_pins_and_reject_everything_else() { Error::Query(QuerySyntaxError::InvalidWhereClauseComponents(_)) )); - // `IN` is rejected with its own message: v1 pins are equality-only, - // multi-`IN` branching is a future capability. + // `IN` resolves to a branching pin — one value per element — and a + // single-element `IN` is exactly an equality pin. let in_clause = vec![WhereClause { field: "chefId".to_string(), operator: WhereOperator::In, - value: Value::Array(vec![Value::Text("alpha".to_string())]), + value: Value::Array(vec![ + Value::Text("beta".to_string()), + Value::Text("alpha".to_string()), + ]), }]; - let error = detect_ranked_mode_v0( + let mode = detect_ranked_mode_v0( &SelectProjection::avg("grade"), &group_by(), &[], @@ -555,16 +562,19 @@ fn where_clauses_resolve_to_equality_pins_and_reject_everything_else() { &in_clause, page(Some(2), None), ) - .expect_err("IN prefixes are not yet supported"); - match error { - Error::Query(QuerySyntaxError::Unsupported(message)) => { - assert!( - message.contains("IN") && message.contains("not yet supported"), - "the IN rejection must say it is a not-yet capability, got: {message}" - ); - } - other => panic!("expected Unsupported for an IN prefix, got {other:?}"), - } + .expect("an IN pin is a well-formed branching pin"); + assert_eq!( + mode.prefix_pins, + vec![PrefixPin { + field: "chefId".to_string(), + values: vec![ + Value::Text("beta".to_string()), + Value::Text("alpha".to_string()), + ], + }], + "the pin carries the elements verbatim; canonical branch order \ + is the encoder's job, not the grammar's" + ); // The same property pinned twice is a caller error, not a silent // last-write-wins. @@ -1201,8 +1211,9 @@ fn count_axis_ranks_reads_and_proves_consistently() { assert_proof_round_trips(&drive, &contract, &bottom_one, &entries); // A missing LIMIT is refused end to end, not just in the pure - // detector: `k` is echoed in the proof envelope, so there is no - // server-side default a verifying client could reproduce. + // detector: `k` binds verification through the client's rebuilt + // `PathQuery`, so there is no server-side default a verifying + // client could reproduce. let mut no_limit = RankedCase::count(true, None); no_limit.limit = None; let error = run(&drive, &contract, &no_limit, false) @@ -1387,8 +1398,8 @@ fn offset_pages_through_the_ranking_and_the_proof_attests_the_starting_rank() { } /// A proof of one page must not verify as another page of the same -/// ranking. `offset` is echoed in the envelope and re-checked, which is -/// what stops a server from answering "the 5th best" with a proof of +/// ranking. `offset` shifts where the verifier's re-executed walk must +/// start, which is what stops a server from answering "the 5th best" with a proof of /// "the best" — the entries would look perfectly valid, and only the /// offset binding distinguishes them. #[test] @@ -1443,7 +1454,7 @@ fn a_proof_does_not_verify_under_a_different_offset() { /// has no entries", so a freshly registered contract queried with /// `prove = true` got an error until the first document landed. /// -/// `prove_indexed_axis_top_k_paginated` closes that gap — it emits a +/// The unified `PathQuery` prover closes that gap — it emits a /// guaranteed-empty range against the secondary rather than refusing — /// so the two paths now agree on empty state, and this test is the /// tripwire that says so. The attested `skipped` is `0`, which for an @@ -1612,8 +1623,9 @@ fn a_tampered_proof_never_verifies_to_the_honest_root_hash() { /// The verifier must be checking the ranking it was asked about: a proof /// generated for one `(axis, k, descending)` triple must not verify under -/// another. grovedb echoes all three in the envelope and re-checks them, -/// and this pins that drive passes each of them through faithfully — a +/// another. grovedb rebuilds the traversal from all three and re-executes +/// the proof against it, and this pins that drive passes each of them +/// through faithfully — a /// dropped argument here would let a client accept a proof of a /// different question. #[test] @@ -2122,8 +2134,9 @@ mod pinned_prefix { //! and its paginated proof. use super::super::drive_dispatcher::{DocumentRankedRequest, DocumentRankedResponse}; - use super::super::index_picker::resolve_ranked_query_for_mode; - use super::super::mode_detection::detect_ranked_mode; + use super::super::index_picker::{encode_prefix_branches, resolve_ranked_query_for_mode}; + use super::super::mode_detection::{detect_ranked_mode, detect_ranked_mode_v0}; + use super::super::PrefixPin; use super::super::{DriveDocumentRankedQuery, RankedEntry, RankedEntryValue}; use crate::drive::Drive; use crate::error::query::QuerySyntaxError; @@ -2145,6 +2158,7 @@ mod pinned_prefix { use dpp::tests::json_document::json_document_to_contract; use dpp::version::PlatformVersion; use grovedb::element::indexed::compute_avg_fixed_point; + use grovedb::TransactionArg; use std::collections::BTreeMap; const PREFIX_PROPERTY: &str = "identityId"; @@ -2245,11 +2259,11 @@ mod pinned_prefix { having: &[], order_by: &order_by, where_clauses, + resolved_time_ranges: &[], limit: Some(limit), offset: None, has_start_at: false, prove, - resolved_time_ranges: &[], }, None, platform_version(), @@ -2338,10 +2352,12 @@ mod pinned_prefix { page.entries, vec![ RankedEntry { + in_key: None, key: b"art".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(180, 2)), }, RankedEntry { + in_key: None, key: b"english".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(161, 2)), }, @@ -2481,10 +2497,12 @@ mod pinned_prefix { page.entries, vec![ RankedEntry { + in_key: None, key: b"math".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(170, 2)), }, RankedEntry { + in_key: None, key: b"science".to_vec(), value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(60, 1)), }, @@ -2530,8 +2548,8 @@ mod pinned_prefix { ) .expect("the compound index covers the null-pinned request"); assert_eq!( - query.equality_prefix_values, - vec![Vec::::new()], + query.prefix_branches, + vec![vec![Vec::::new()]], "a null pin must encode as the write path's empty segment" ); let (root_hash, verified) = query @@ -2548,6 +2566,412 @@ mod pinned_prefix { ); } + fn in_pin(identities: &[[u8; 32]]) -> Vec { + vec![WhereClause { + field: PREFIX_PROPERTY.to_string(), + operator: WhereOperator::In, + value: Value::Array( + identities + .iter() + .map(|identity| Value::Identifier(*identity)) + .collect(), + ), + }] + } + + /// `identityId IN [X, Y]` walks each identity's own secondary and + /// merges: descending aggregate order, entries tagged with their + /// branch's `in_key`, and a **cross-prefix aggregate tie** breaking + /// by encoded prefix ascending (X's 32 `1`-bytes before Y's `2`s) — + /// the comparator's middle term, observable only here. The proof is + /// one branched `PathQuery` envelope, round-tripped through the + /// shared resolver against the live root hash. + #[test] + fn in_pinned_top_k_merges_branches_and_proves() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades( + &drive, + &contract, + &[ + // X: art 90, math 80. Y: science 95, history 90 — the + // two 90s are the cross-prefix tie. + (IDENTITY_X, "art", 90), + (IDENTITY_X, "math", 80), + (IDENTITY_Y, "science", 95), + (IDENTITY_Y, "history", 90), + ], + ); + + // Request order [Y, X] deliberately reversed: canonical branch + // order is by encoded prefix, not by element order. + let pins = in_pin(&[IDENTITY_Y, IDENTITY_X]); + let page = match run(&drive, &contract, &pins, 3, false).expect("read succeeds") { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries, got a proof"), + }; + assert_eq!(page.skipped, 0); + assert_eq!( + page.entries, + vec![ + RankedEntry { + in_key: Some(IDENTITY_Y.to_vec()), + key: b"science".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(95, 1)), + }, + RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"art".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }, + RankedEntry { + in_key: Some(IDENTITY_Y.to_vec()), + key: b"history".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }, + ], + "top 3 across the union: science 95, then the 90–90 tie broken \ + by encoded prefix ascending (X's art before Y's history)" + ); + + let proof = match run(&drive, &contract, &pins, 3, true).expect("prove succeeds") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof, got entries"), + }; + let query = client_side_query(&contract, &pins, 3); + assert_eq!(query.prefix_branches.len(), 2, "two branches resolved"); + let (root_hash, verified) = query + .verify_ranked_top_k_proof(&proof, platform_version()) + .expect("the branched envelope must verify"); + assert_eq!(verified.entries, page.entries); + assert_eq!( + root_hash, + drive + .grove + .root_hash(None, &platform_version().drive.grove_version) + .unwrap() + .expect("root hash must be readable"), + ); + } + + /// Platform-level tamper cases on the branched envelope. The deep + /// matrix — reordered branch keys, duplicated or dropped branch + /// tails, echo mismatches — is pinned in grovedb's + /// `indexed_axis_branched_proof_tests`, since the envelope is one + /// grovedb proof now; here we pin what the platform layer itself + /// must not confuse: corrupted bytes, and the single-branch / + /// multi-branch envelope shapes never cross-verifying. + #[test] + fn tampered_or_mismatched_branched_proofs_do_not_verify() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades( + &drive, + &contract, + &[(IDENTITY_X, "art", 90), (IDENTITY_Y, "science", 95)], + ); + let pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + let proof = match run(&drive, &contract, &pins, 2, true).expect("prove succeeds") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof, got entries"), + }; + let query = client_side_query(&contract, &pins, 2); + + // Baseline sanity: untampered verifies. + query + .verify_ranked_top_k_proof(&proof, platform_version()) + .expect("untampered branched envelope verifies"); + + // Corrupted bytes. + let mut corrupted = proof.clone(); + let mid = corrupted.len() / 2; + corrupted[mid] ^= 0xFF; + assert!( + query + .verify_ranked_top_k_proof(&corrupted, platform_version()) + .is_err(), + "a flipped byte must not verify" + ); + + // Truncated bytes. + let truncated = &proof[..proof.len() - 8]; + assert!( + query + .verify_ranked_top_k_proof(truncated, platform_version()) + .is_err(), + "a truncated envelope must not verify" + ); + + // A single-pin proof must not verify under the branched query, + // nor a branched proof under a single-pin query — the two + // envelope shapes are distinct grovedb types. + let single = pin(IDENTITY_X); + let single_proof = match run(&drive, &contract, &single, 2, true).expect("prove") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof"), + }; + assert!( + query + .verify_ranked_top_k_proof(&single_proof, platform_version()) + .is_err(), + "a single-branch envelope must not verify under an IN query" + ); + let single_query = client_side_query(&contract, &single, 2); + assert!( + single_query + .verify_ranked_top_k_proof(&proof, platform_version()) + .is_err(), + "a branched envelope must not verify under a single-pin query" + ); + } + + /// A single-element `IN` is normalized to an equality pin at + /// grammar time: same resolved branch set, same entries, and the + /// **same proof bytes** — the degenerate case is byte-identical to + /// `==`, so no client can observe which spelling was used. + #[test] + fn single_element_in_is_byte_identical_to_an_equality_pin() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "art", 90)]); + + let equality = pin(IDENTITY_X); + let single_in = in_pin(&[IDENTITY_X]); + + let eq_query = client_side_query(&contract, &equality, 2); + let in_query = client_side_query(&contract, &single_in, 2); + assert_eq!(eq_query.prefix_branches, in_query.prefix_branches); + + let eq_proof = match run(&drive, &contract, &equality, 2, true).expect("prove") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof"), + }; + let in_proof = match run(&drive, &contract, &single_in, 2, true).expect("prove") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof"), + }; + assert_eq!(eq_proof, in_proof, "no container for a single branch"); + + let eq_page = match run(&drive, &contract, &equality, 2, false).expect("read") { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + let in_page = match run(&drive, &contract, &single_in, 2, false).expect("read") { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + assert_eq!( + eq_page.entries, in_page.entries, + "a singleton IN reads exactly what the equality pin reads" + ); + assert!( + eq_page.entries.iter().all(|e| e.in_key.is_none()), + "single-branch entries carry no in_key" + ); + } + + /// `OFFSET` is rejected together with an `IN` pin — rank-skip is + /// attested per-secondary and has no meaning across a branch union. + #[test] + fn offset_is_rejected_with_an_in_pin() { + let error = detect_ranked_mode_v0( + &SelectProjection::avg("grade"), + &[CLASS_PROPERTY.to_string()], + &[], + &[OrderClause { + field: "grade".to_string(), + ascending: false, + }], + &in_pin(&[IDENTITY_X, IDENTITY_Y]), + RankedPaginationInputs { + limit: Some(2), + offset: Some(1), + has_start_at: false, + }, + ) + .expect_err("offset cannot combine with IN"); + match error { + Error::Query(QuerySyntaxError::InvalidLimit(message)) => { + assert!( + message.contains("OFFSET") && message.contains("IN"), + "the rejection must name the offset × IN exclusion, got: {message}" + ); + } + other => panic!("expected InvalidLimit, got {other:?}"), + } + } + + /// Grammar rejections around the `IN` pin: over-cap element lists, + /// empty lists, a second `IN`, a non-array operand, and elements + /// that encode to the same segment. + #[test] + fn in_pin_shape_rejections() { + let detect = |where_clauses: &[WhereClause]| { + detect_ranked_mode_v0( + &SelectProjection::avg("grade"), + &[CLASS_PROPERTY.to_string()], + &[], + &[OrderClause { + field: "grade".to_string(), + ascending: false, + }], + where_clauses, + RankedPaginationInputs { + limit: Some(2), + offset: None, + has_start_at: false, + }, + ) + }; + + // Over the branch ceiling. + let identities: Vec<[u8; 32]> = (0..11).map(|i| [i as u8 + 1; 32]).collect(); + let error = detect(&in_pin(&identities)).expect_err("11 branches is over the cap"); + match error { + Error::Query(QuerySyntaxError::InvalidParameter(message)) => { + assert!( + message.contains("ceiling of 10"), + "the rejection must name the ceiling, got: {message}" + ); + } + other => panic!("expected InvalidParameter, got {other:?}"), + } + + // Empty element list. + let empty = vec![WhereClause { + field: PREFIX_PROPERTY.to_string(), + operator: WhereOperator::In, + value: Value::Array(vec![]), + }]; + assert!(detect(&empty).is_err(), "an empty IN list must be rejected"); + + // Two *branching* INs are rejected in either clause order. + let multi = |field: &str| WhereClause { + field: field.to_string(), + operator: WhereOperator::In, + value: Value::Array(vec![Value::U64(1), Value::U64(2)]), + }; + for two_ins in [ + vec![multi(PREFIX_PROPERTY), multi("other")], + vec![multi("other"), multi(PREFIX_PROPERTY)], + ] { + let error = detect(&two_ins).expect_err("two branching INs must be rejected"); + assert!( + matches!(error, Error::Query(QuerySyntaxError::Unsupported(_))), + "expected Unsupported for a second branching IN, got {error:?}" + ); + } + + // A singleton IN is an equality pin and never counts against + // the one-`IN` budget — in either clause order relative to the + // branching one. + let singleton = WhereClause { + field: "other".to_string(), + operator: WhereOperator::In, + value: Value::Array(vec![Value::U64(1)]), + }; + for mixed in [ + vec![multi(PREFIX_PROPERTY), singleton.clone()], + vec![singleton.clone(), multi(PREFIX_PROPERTY)], + ] { + let mode = detect(&mixed) + .expect("a singleton IN alongside a branching IN is well-formed either way"); + assert_eq!( + mode.prefix_pins + .iter() + .map(|pin| pin.values.len()) + .collect::>(), + if mixed[0].field == "other" { + vec![1, 2] + } else { + vec![2, 1] + }, + "one branching pin and one singleton pin, in clause order" + ); + } + + // Non-array operand. + let scalar = vec![WhereClause { + field: PREFIX_PROPERTY.to_string(), + operator: WhereOperator::In, + value: Value::U64(7), + }]; + assert!( + detect(&scalar).is_err(), + "a scalar IN operand must be rejected" + ); + + // Duplicate elements surface at the encoder (post-encoding + // distinctness), through the resolver. + let (_, contract) = setup_grades_compound_ranked(); + let duplicated = in_pin(&[IDENTITY_X, IDENTITY_X]); + let mode = detect(&duplicated).expect("shape-valid; duplicates are the encoder's call"); + let error = resolve_ranked_query_for_mode( + contract.id_ref().to_buffer(), + contract + .document_type_for_name(DOCUMENT_TYPE) + .expect("grade doctype exists"), + DOCUMENT_TYPE.to_string(), + contract + .document_types() + .get(DOCUMENT_TYPE) + .expect("grade doctype exists") + .indexes(), + &mode, + platform_version(), + ) + .expect_err("duplicate encoded elements must be rejected"); + assert!( + matches!( + error, + Error::Query(QuerySyntaxError::InvalidWhereClauseComponents(_)) + ), + "expected the duplicate-encoding rejection, got {error:?}" + ); + } + + /// An `IN` element whose prefix was never written contributes the + /// **empty branch** — union semantics — while the single-`==`-pin + /// contract keeps erroring on an unknown value (pinned separately + /// by `unknown_prefix_value_errors_rather_than_fabricating_an_empty_page`). + /// The proved path authenticates the absence inside the branched + /// envelope, so read, proof, and verification agree. + #[test] + fn an_absent_in_element_contributes_an_empty_branch() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "art", 90)]); + + let never_written = [9u8; 32]; + let pins = in_pin(&[IDENTITY_X, never_written]); + let page = match run(&drive, &contract, &pins, 2, false).expect("read succeeds") { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries, got a proof"), + }; + assert_eq!( + page.entries, + vec![RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"art".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }], + "only the existing branch contributes; the absent one is empty, not an error" + ); + + let proof = match run(&drive, &contract, &pins, 2, true).expect("prove succeeds") { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof, got entries"), + }; + let (root_hash, verified) = client_side_query(&contract, &pins, 2) + .verify_ranked_top_k_proof(&proof, platform_version()) + .expect("the envelope authenticates the absent branch"); + assert_eq!(verified.entries, page.entries); + assert_eq!( + root_hash, + drive + .grove + .root_hash(None, &platform_version().drive.grove_version) + .unwrap() + .expect("root hash must be readable"), + ); + } + /// An unpinned request over the compound-only contract has no /// covering index — there is no global cross-prefix ordering to /// serve, so the rejection names the missing coverage. @@ -2566,4 +2990,588 @@ mod pinned_prefix { "expected a no-covering-index rejection, got {error:?}" ); } + + const DUAL_DOCTYPE: &str = "dualGrade"; + + fn insert_dual_grades( + drive: &Drive, + contract: &DataContract, + rows: &[([u8; 32], &str, &str, i64)], + ) { + let pv = platform_version(); + let document_type = contract + .document_type_for_name(DUAL_DOCTYPE) + .expect("dualGrade doctype exists"); + for (i, (identity, tag, class, grade)) in rows.iter().enumerate() { + let mut doc: Document = document_type + .random_document(Some(8000 + i as u64), pv) + .expect("random document"); + let mut props = BTreeMap::new(); + props.insert(PREFIX_PROPERTY.to_string(), Value::Identifier(*identity)); + props.insert("tag".to_string(), Value::Text(tag.to_string())); + props.insert(CLASS_PROPERTY.to_string(), Value::Text(class.to_string())); + props.insert("grade".to_string(), Value::I64(*grade)); + doc.set_properties(props); + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&doc, None)), + owner_id: None, + }, + contract, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + pv, + None, + ) + .expect("expected to insert a dualGrade document"); + } + } + + fn run_dual( + drive: &Drive, + contract: &DataContract, + where_clauses: &[WhereClause], + limit: u32, + prove: bool, + transaction: TransactionArg, + ) -> Result { + let group_by = vec![CLASS_PROPERTY.to_string()]; + let order_by = vec![OrderClause { + field: "grade".to_string(), + ascending: false, + }]; + drive.execute_document_ranked_request( + DocumentRankedRequest { + contract, + document_type: contract + .document_type_for_name(DUAL_DOCTYPE) + .expect("dualGrade doctype exists"), + group_by: &group_by, + select: SelectProjection::avg("grade"), + having: &[], + order_by: &order_by, + where_clauses, + resolved_time_ranges: &[], + limit: Some(limit), + offset: None, + has_start_at: false, + prove, + }, + transaction, + platform_version(), + ) + } + + /// An `IN` element whose branch-key tree EXISTS (another pin value was + /// written under it) but whose deeper pinned path was never written is + /// an ABSENT branch — empty page, union semantics — not an error that + /// discards the other branches' results. grovedb's branched reader and + /// prover authenticate absence at any depth of the branch chain, so the + /// unproved executor must walk the whole chain too. + #[test] + fn an_in_element_with_an_absent_deeper_pin_contributes_an_empty_branch() { + let (drive, contract) = setup_grades_compound_ranked(); + // Y's tree exists (via tag "t2"), but Y/t1 was never written. + insert_dual_grades( + &drive, + &contract, + &[ + (IDENTITY_X, "t1", "math", 90), + (IDENTITY_Y, "t2", "math", 80), + ], + ); + + let mut pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + pins.push(WhereClause { + field: "tag".to_string(), + operator: WhereOperator::Equal, + value: Value::Text("t1".to_string()), + }); + + let page = match run_dual(&drive, &contract, &pins, 2, false, None) + .expect("a present branch key with an absent deeper pin must not error") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries, got a proof"), + }; + assert_eq!( + page.entries, + vec![RankedEntry { + in_key: Some(IDENTITY_X.to_vec()), + key: b"math".to_vec(), + value: RankedEntryValue::AvgFixedPoint(compute_avg_fixed_point(90, 1)), + }], + "X/t1 contributes; Y (present key, absent t1 suffix) is an empty branch" + ); + + // The proved path must agree byte-for-byte with the unproved one. + let proof = match run_dual(&drive, &contract, &pins, 2, true, None) + .expect("the branched envelope authenticates the absent suffix") + { + DocumentRankedResponse::Proof(proof) => proof, + DocumentRankedResponse::Entries(_) => panic!("expected a proof, got entries"), + }; + let group_by = vec![CLASS_PROPERTY.to_string()]; + let order_by = vec![OrderClause { + field: "grade".to_string(), + ascending: false, + }]; + let pv = platform_version(); + let mode = detect_ranked_mode( + &SelectProjection::avg("grade"), + &group_by, + &[], + &order_by, + &pins, + RankedPaginationInputs { + limit: Some(2), + offset: None, + has_start_at: false, + }, + pv, + ) + .expect("well-formed"); + let query = resolve_ranked_query_for_mode( + contract.id_ref().to_buffer(), + contract + .document_type_for_name(DUAL_DOCTYPE) + .expect("dualGrade doctype exists"), + DUAL_DOCTYPE.to_string(), + contract + .document_types() + .get(DUAL_DOCTYPE) + .expect("dualGrade doctype exists") + .indexes(), + &mode, + pv, + ) + .expect("covered"); + let (root_hash, verified) = query + .verify_ranked_top_k_proof(&proof, pv) + .expect("the proof verifies"); + assert_eq!(verified.entries, page.entries); + assert_eq!( + root_hash, + drive + .grove + .root_hash(None, &pv.drive.grove_version) + .unwrap() + .expect("root hash must be readable"), + ); + } + + /// A `null` pin addresses its prefix through an EMPTY path segment, + /// which the branched proof grammar cannot express — so combining it + /// with an `IN` is rejected at the grammar instead of serving the + /// unproved read and failing the prove. + #[test] + fn a_null_pin_cannot_combine_with_an_in() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_dual_grades(&drive, &contract, &[(IDENTITY_X, "t1", "math", 90)]); + + let mut pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + pins.push(WhereClause { + field: "tag".to_string(), + operator: WhereOperator::Equal, + value: Value::Null, + }); + + let error = run_dual(&drive, &contract, &pins, 2, false, None) + .expect_err("null x IN must be rejected"); + assert!( + matches!( + &error, + Error::Query(QuerySyntaxError::InvalidWhereClauseComponents(message)) + if message.contains("`null` pin") + ), + "the rejection must name the null x IN exclusion, got {error:?}" + ); + } + + /// A branched unproved read REJECTS a caller transaction, mirroring + /// the branched prover: an ordinary grovedb transaction reads the + /// latest committed state on every operation, so forwarding it could + /// tear the union across branches. Per-element reads keep the + /// transactional capability exactly — a single-pin read is one + /// grovedb operation and serves the transaction's own writes — and + /// the committed (`None`) branched read is unaffected. + #[test] + fn a_branched_unproved_read_rejects_an_ordinary_transaction() { + let (drive, contract) = setup_grades_compound_ranked(); + let pv = platform_version(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "math", 80)]); + + let transaction = drive.grove.start_transaction(); + let document_type = contract + .document_type_for_name(DOCUMENT_TYPE) + .expect("grade doctype exists"); + let mut doc: Document = document_type + .random_document(Some(9000), pv) + .expect("random document"); + let mut props = BTreeMap::new(); + props.insert(PREFIX_PROPERTY.to_string(), Value::Identifier(IDENTITY_Y)); + props.insert( + CLASS_PROPERTY.to_string(), + Value::Text("science".to_string()), + ); + props.insert("grade".to_string(), Value::I64(95)); + doc.set_properties(props); + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&doc, None)), + owner_id: None, + }, + contract: &contract, + document_type, + }, + false, + BlockInfo::default(), + true, + Some(&transaction), + pv, + None, + ) + .expect("expected to insert Y's grade inside the transaction"); + + let pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + let group_by = vec![CLASS_PROPERTY.to_string()]; + let order_by = vec![OrderClause { + field: "grade".to_string(), + ascending: false, + }]; + let request = || DocumentRankedRequest { + contract: &contract, + document_type, + group_by: &group_by, + select: SelectProjection::avg("grade"), + having: &[], + order_by: &order_by, + where_clauses: &pins, + resolved_time_ranges: &[], + limit: Some(4), + offset: None, + has_start_at: false, + prove: false, + }; + + let error = drive + .execute_document_ranked_request(request(), Some(&transaction), pv) + .expect_err("a transactional branched read must fail closed"); + assert!( + matches!( + &error, + Error::Drive(crate::error::drive::DriveError::NotSupported(message)) + if message.contains("tear the union") + ), + "expected the caller-transaction rejection, got {error:?}" + ); + + // Per-element under the SAME transaction: the single-pin read is + // one grovedb operation and serves the branch written only + // inside it. + let y_pin = pin(IDENTITY_Y); + let y_request = DocumentRankedRequest { + contract: &contract, + document_type, + group_by: &group_by, + select: SelectProjection::avg("grade"), + having: &[], + order_by: &order_by, + where_clauses: &y_pin, + resolved_time_ranges: &[], + limit: Some(4), + offset: None, + has_start_at: false, + prove: false, + }; + let with_tx = match drive + .execute_document_ranked_request(y_request, Some(&transaction), pv) + .expect("the transactional single-pin read serves") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + assert_eq!( + with_tx + .entries + .iter() + .map(|e| (e.key.clone(), e.in_key.clone())) + .collect::>(), + vec![(b"science".to_vec(), None)], + "the single-pin read serves the transaction's own branch" + ); + + let without_tx = match drive + .execute_document_ranked_request(request(), None, pv) + .expect("the committed branched read serves") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + assert_eq!( + without_tx + .entries + .iter() + .map(|e| e.in_key.clone()) + .collect::>(), + vec![Some(IDENTITY_X.to_vec())], + "without the transaction Y's uncommitted branch is authenticated absent" + ); + } + + /// grovedb's unified `prove_query` proves committed state only, so an + /// `IN`-pinned prove under a caller transaction fails closed instead of + /// silently proving a different snapshot than the unproved read serves. + #[test] + fn a_branched_prove_rejects_a_transaction() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "math", 80)]); + + let transaction = drive.grove.start_transaction(); + let pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + let group_by = vec![CLASS_PROPERTY.to_string()]; + let order_by = vec![OrderClause { + field: "grade".to_string(), + ascending: false, + }]; + let error = drive + .execute_document_ranked_request( + DocumentRankedRequest { + contract: &contract, + document_type: contract + .document_type_for_name(DOCUMENT_TYPE) + .expect("grade doctype exists"), + group_by: &group_by, + select: SelectProjection::avg("grade"), + having: &[], + order_by: &order_by, + where_clauses: &pins, + resolved_time_ranges: &[], + limit: Some(2), + offset: None, + has_start_at: false, + prove: true, + }, + Some(&transaction), + platform_version(), + ) + .expect_err("a transactional branched prove must fail closed"); + assert!( + matches!( + &error, + Error::Drive(crate::error::drive::DriveError::NotSupported(message)) + if message.contains("committed state") + ), + "expected the committed-state-only rejection, got {error:?}" + ); + + // The unproved read is unaffected by the failed prove; it serves + // committed state (a transactional branched read is likewise + // rejected — pinned by + // `a_branched_unproved_read_rejects_an_ordinary_transaction`). + let page = match run(&drive, &contract, &pins, 2, false) + .expect("the committed unproved read is unaffected") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + assert_eq!(page.entries.len(), 1); + } + + /// The offset x `IN` exclusion survives resolution: the grammar + /// rejects the combination, but `offset` is a public field and a + /// mode is publicly constructible, so every execution, proving and + /// verification boundary re-checks it — a resolved query mutated + /// into the forbidden shape is refused, not served as a page-broken + /// merge that reports `skipped: 0`. + #[test] + fn a_mutated_offset_cannot_combine_with_branches() { + let (drive, contract) = setup_grades_compound_ranked(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "math", 80)]); + + let mut query = client_side_query(&contract, &in_pin(&[IDENTITY_X, IDENTITY_Y]), 2); + query.offset = 1; + let pv = platform_version(); + + let read = query + .execute_top_k_no_proof(&drive, None, pv) + .expect_err("a branched read with a mutated offset must be refused"); + assert!( + matches!(&read, Error::Query(QuerySyntaxError::InvalidLimit(m)) if m.contains("cannot combine")), + "expected the offset x IN rejection on the read path, got {read:?}" + ); + let prove = query + .execute_top_k_with_proof(&drive, None, pv) + .expect_err("a branched prove with a mutated offset must be refused"); + assert!( + matches!(&prove, Error::Query(QuerySyntaxError::InvalidLimit(_))), + "expected the offset x IN rejection on the prove path, got {prove:?}" + ); + let verify = query + .verify_ranked_top_k_proof(&[], pv) + .expect_err("verification with a mutated offset must be refused"); + assert!( + matches!(&verify, Error::Query(QuerySyntaxError::InvalidLimit(_))), + "expected the offset x IN rejection on the verify path, got {verify:?}" + ); + } + + /// The public encoder enforces the documented branch ceiling itself: + /// its callers' grammar checks are not the only line of defense. + #[test] + fn the_prefix_encoder_enforces_the_branch_ceiling() { + let (_drive, contract) = setup_grades_compound_ranked(); + let document_type = contract + .document_type_for_name(DOCUMENT_TYPE) + .expect("grade doctype exists"); + let index = contract + .document_types() + .get(DOCUMENT_TYPE) + .expect("grade doctype exists") + .indexes() + .values() + .next() + .expect("the compound ranked index exists"); + let pins = vec![PrefixPin { + field: PREFIX_PROPERTY.to_string(), + values: (0u8..=10).map(|i| Value::Identifier([i; 32])).collect(), + }]; + let error = encode_prefix_branches(document_type, index, &pins, platform_version()) + .expect_err("11 branches must exceed the ceiling"); + assert!( + matches!( + &error, + Error::Query(QuerySyntaxError::InvalidWhereClauseComponents(message)) + if message.contains("more branches") + ), + "expected the fan-out ceiling rejection, got {error:?}" + ); + } + + /// The `IN` union is served from ONE committed state through the + /// production `None` path itself: the executor takes a snapshot + /// read transaction internally, and the test-only seam + /// (`branches::test_hooks::AFTER_BRANCHED_SNAPSHOT`) lands a commit + /// deterministically INSIDE the window — after that snapshot is + /// taken, before the branched call runs. The `None` read must + /// return the pre-commit union (the new branch still absent, the + /// changed page unchanged); a second `None` read after the window + /// returns the post-commit union. Removing the executor's internal + /// snapshot selection fails this test. + #[test] + fn a_branched_read_is_pinned_to_one_committed_state() { + use std::time::Duration; + + let (drive, contract) = setup_grades_compound_ranked(); + let pv = platform_version(); + insert_grades(&drive, &contract, &[(IDENTITY_X, "math", 80)]); + + // The mid-window "block commit": Y's branch springs into + // existence and X gains a class. Documents are built with + // distinct seeds so they cannot collide with `insert_grades`'. + let commit_post_snapshot_rows = || { + let document_type = contract + .document_type_for_name(DOCUMENT_TYPE) + .expect("grade doctype exists"); + for (seed, identity, class, grade) in [ + (9100u64, IDENTITY_Y, "science", 95i64), + (9101u64, IDENTITY_X, "art", 90i64), + ] { + let mut doc: Document = document_type + .random_document(Some(seed), pv) + .expect("random document"); + let mut props = BTreeMap::new(); + props.insert(PREFIX_PROPERTY.to_string(), Value::Identifier(identity)); + props.insert(CLASS_PROPERTY.to_string(), Value::Text(class.to_string())); + props.insert("grade".to_string(), Value::I64(grade)); + doc.set_properties(props); + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&doc, None)), + owner_id: None, + }, + contract: &contract, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + pv, + None, + ) + .expect("expected to commit the mid-window grade"); + } + }; + + // Rendezvous: when the executor's hook fires (snapshot taken, + // read not yet run), wake the writer, wait for its commit to + // land, then let the read proceed. + let (enter_window_tx, enter_window_rx) = std::sync::mpsc::channel::<()>(); + let (commit_done_tx, commit_done_rx) = std::sync::mpsc::channel::<()>(); + super::super::branches::test_hooks::AFTER_BRANCHED_SNAPSHOT.with(|hook| { + *hook.borrow_mut() = Some(Box::new(move || { + let _ = enter_window_tx.send(()); + let _ = commit_done_rx.recv_timeout(Duration::from_secs(20)); + })); + }); + + let pins = in_pin(&[IDENTITY_X, IDENTITY_Y]); + let pinned = std::thread::scope(|scope| { + scope.spawn(move || { + if enter_window_rx + .recv_timeout(Duration::from_secs(20)) + .is_ok() + { + commit_post_snapshot_rows(); + let _ = commit_done_tx.send(()); + } + }); + match run(&drive, &contract, &pins, 4, false) + .expect("the branched read serves across the mid-window commit") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + } + }); + super::super::branches::test_hooks::AFTER_BRANCHED_SNAPSHOT.with(|hook| { + *hook.borrow_mut() = None; + }); + + // The `None` read observed the pre-commit state even though the + // commit landed inside its window: Y's branch still absent + // (empty, not an error), X still math-only. + assert_eq!( + pinned + .entries + .iter() + .map(|e| e.key.clone()) + .collect::>(), + vec![b"math".to_vec()], + "the automatic snapshot pins the union to the pre-commit state" + ); + + // A fresh committed read sees the post-commit union. + let fresh = match run(&drive, &contract, &pins, 4, false) + .expect("the committed branched read serves") + { + DocumentRankedResponse::Entries(page) => page, + DocumentRankedResponse::Proof(_) => panic!("expected entries"), + }; + assert_eq!( + fresh.entries.len(), + 3, + "the committed union reflects the commit" + ); + } } diff --git a/packages/rs-drive/src/verify/document_having/mod.rs b/packages/rs-drive/src/verify/document_having/mod.rs index 06d35824f66..e21cfc913f7 100644 --- a/packages/rs-drive/src/verify/document_having/mod.rs +++ b/packages/rs-drive/src/verify/document_having/mod.rs @@ -10,9 +10,9 @@ //! //! Only one verifier exists here, for the same reason as on the ranked //! surface: every having-range request — any of the three axes, either -//! direction, any contiguous bound — resolves to one bounded-axis -//! `PathQuery` proved through grovedb's unified `prove_query`, differing -//! only in the axis, bounds, direction and limit the query carries. +//! direction, any contiguous bound — resolves to one `Bounded` axis +//! traversal proved through grovedb's unified `prove_query`, against +//! the same `PathQuery` the verifier reconstructs from the request. /// Indexed-axis range proof verification — returns the groups the proof /// commits to as falling inside the bound, in axis order. diff --git a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs index 2cf0a0ebdf2..0f58f625769 100644 --- a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs @@ -1,121 +1,158 @@ use crate::error::drive::DriveError; use crate::error::Error; -use crate::query::{DriveDocumentHavingQuery, RankedAxis, RankedEntry, RankedEntryValue}; +use crate::query::drive_document_ranked_query::branches::{ + axis_entries_to_ranked, decompose_branch_paths, merge_branch_pages, +}; +use crate::query::{DriveDocumentHavingQuery, RankedEntry}; use crate::verify::RootHash; use dpp::version::PlatformVersion; -use grovedb::operations::proof::{indexed_axis::AxisEntries, VerifiedPathQuery}; -use grovedb::{GroveDb, PathQuery}; +use grovedb::operations::proof::VerifiedPathQuery; +use grovedb::GroveDb; +use grovedb::PathQuery; +use grovedb_query::AxisQuery; impl DriveDocumentHavingQuery<'_> { /// v0 of [`Self::verify_having_range_proof`]. /// - /// Rebuilds the same [`PathQuery::new_axis_bounded`] the prover used - /// — the path via [`Self::indexed_property_name_tree_path`], the - /// bounds via - /// [`AxisRangeBounds::i128_bounds`](crate::query::drive_document_having_query::AxisRangeBounds::i128_bounds) - /// — then hands the proof to [`GroveDb::verify_path_query`] — an - /// associated function, no database handle, so this compiles and - /// runs in a verifier-only build. + /// Rebuilds the same `PathQuery` the prover built — the subtree + /// path via [`Self::indexed_property_name_tree_path`], the + /// `Bounded` traversal from the request's inclusive bounds + /// ([`AxisRangeBounds::inclusive_bounds_i128`](crate::query::drive_document_having_query::AxisRangeBounds::inclusive_bounds_i128)), + /// limit and direction — and hands the proof to + /// [`GroveDb::verify_path_query`], an associated function: no + /// database handle, so this compiles and runs in a verifier-only + /// build. /// /// Three things are checked before the entries are returned: /// - /// 1. **The proof answers this query.** The unified verifier is - /// query-as-input: nothing is echoed in the envelope; grovedb - /// lowers the query's bounds into the secondary's keyspace - /// through the same function the prover used and verifies the - /// proof against that reconstruction, so a proof generated for a - /// different bound — or a different direction, or a different - /// limit — is rejected rather than silently reinterpreted. - /// Completeness rides on the same check: a Merk range proof - /// commits its boundaries, so an in-range group the prover - /// omitted fails reconstruction. + /// 1. **The proof covers this query's own traversal.** Binding is by + /// RECONSTRUCTION, not echo comparison: the verifier re-executes + /// the proof against the bounds, direction and limit it rebuilt + /// from the request, so a proof for a different bound or + /// direction fails to cover it. The limit binds as a CAP under + /// re-execution — an exhausted-walk proof is a complete answer + /// under any admitting cap (sound, and pinned by the + /// limit-tamper test), while a proof truncated by a smaller limit + /// fails a larger cap for missing coverage of the rest of the + /// bound. Completeness rides on the Merk range proof itself: its + /// boundary commitments show no in-range group was omitted. /// 2. **The result's axis shape matches the requested axis** — the /// same belt-and-braces check the ranked verifier does. /// 3. **At most `limit` entries.** Fewer is normal — fewer groups /// may match the bound — but more would mean the proof committed /// a longer walk than the request authorized. - /// - /// No `platform_version` argument: the parent dispatcher already - /// consumed it to select this version, and verification derives - /// everything else from the proof bytes plus the query. #[inline(always)] pub(super) fn verify_having_range_proof_v0( &self, proof: &[u8], platform_version: &PlatformVersion, ) -> Result<(RootHash, Vec), Error> { - let path = self.indexed_property_name_tree_path()?; - let (lo, hi) = self.bounds.i128_bounds(); - let path_query = PathQuery::new_axis_bounded( - path, - self.bounds.axis().into(), - lo, - hi, - self.limit, - self.descending, - ); + if self.prefix_branches.len() > 1 { + // `IN`-pinned request: one grovedb branched envelope — same + // discipline as the ranked verifier (branch set from this + // query's own resolution, tails bound to keys by the + // branching-level proof, one root hash, page re-derived by + // the shared merge). + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let (prefix, keys, suffix) = decompose_branch_paths(&paths)?; + let axis = self.bounds.axis(); + let (lo, hi) = self.bounds.inclusive_bounds_i128(); + let path_query = PathQuery::new_branched_axis( + prefix, + keys.clone(), + suffix, + AxisQuery::bounded(axis.into(), lo, hi, self.limit, self.descending), + ); + let verified = GroveDb::verify_path_query( + proof, + &path_query, + &platform_version.drive.grove_version, + ) + .map_err(|e| Error::GroveDB(Box::new(e)))?; + let VerifiedPathQuery::BranchedAxisEntries { + root_hash, + branches, + } = verified + else { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a branched having range proof verified to a non-branched shape".to_string(), + ))); + }; + if branches.len() != keys.len() || branches.iter().map(|(key, _)| key).ne(keys.iter()) { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a branched having range proof verified a different branch set than the \ + request resolved" + .to_string(), + ))); + } + let per_branch = branches + .into_iter() + .map(|(_key, entries)| { + // Authenticated absence of a branch key = an empty page, + // the same reading the unproved path gives an absent + // `IN` element. + let entries = match entries { + None => Vec::new(), + Some(entries) => axis_entries_to_ranked(axis, entries)?, + }; + if entries.len() > self.limit as usize { + return Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branch of a having range proof verified to {} entries for \ + limit = {}", + entries.len(), + self.limit + )))); + } + Ok(entries) + }) + .collect::, Error>>()?; + let entries = merge_branch_pages( + per_branch, + &self.prefix_branches, + self.descending, + self.limit as usize, + )?; + return Ok((root_hash, entries)); + } + self.verify_having_range_proof_v0_branch(0, proof, platform_version) + } + /// One branch's verification — the entire pre-`IN` verifier, + /// parameterized by the prefix branch. + fn verify_having_range_proof_v0_branch( + &self, + branch: usize, + proof: &[u8], + platform_version: &PlatformVersion, + ) -> Result<(RootHash, Vec), Error> { + let path = self.indexed_property_name_tree_path(branch)?; + let axis = self.bounds.axis(); + let (lo, hi) = self.bounds.inclusive_bounds_i128(); + let path_query = + PathQuery::new_axis_bounded(path, axis.into(), lo, hi, self.limit, self.descending); let verified = GroveDb::verify_path_query(proof, &path_query, &platform_version.drive.grove_version) .map_err(|e| Error::GroveDB(Box::new(e)))?; - - // `skipped` is the paginated traversal's field and is `None` for - // bounded ones — nothing to check here. let VerifiedPathQuery::AxisEntries { root_hash, entries, skipped: _, } = verified else { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having range proof for the {:?} axis verified to a non-axis result shape", - self.bounds.axis() - )))); - }; - - let entries = match (self.bounds.axis(), entries) { - (RankedAxis::Count, AxisEntries::Count(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(count, key)| RankedEntry { - key, - value: RankedEntryValue::Count(count), - }) - .collect::>(), - (RankedAxis::Sum, AxisEntries::Sum(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(sum, key)| RankedEntry { - key, - value: RankedEntryValue::Sum(sum), - }) - .collect::>(), - (RankedAxis::Avg, AxisEntries::Avg(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(avg, key)| RankedEntry { - key, - value: RankedEntryValue::AvgFixedPoint(avg), - }) - .collect::>(), - (axis, other) => { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having range proof for the {axis:?} axis verified to {} entries of a \ - different axis shape", - other.len() - )))); - } + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a having range proof verified to a different shape".to_string(), + ))); }; - + let entries = axis_entries_to_ranked(axis, entries)?; if entries.len() > self.limit as usize { return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "having range proof for the {:?} axis verified to {} entries for limit = {}", - self.bounds.axis(), + "having range proof verified to {} entries for limit = {}", entries.len(), self.limit )))); } - Ok((root_hash, entries)) } } diff --git a/packages/rs-drive/src/verify/document_ranked/mod.rs b/packages/rs-drive/src/verify/document_ranked/mod.rs index d99a6b887f1..e9a4ebbe671 100644 --- a/packages/rs-drive/src/verify/document_ranked/mod.rs +++ b/packages/rs-drive/src/verify/document_ranked/mod.rs @@ -12,9 +12,9 @@ //! single proof shape. Where the count surface has five verifiers because //! five different grovedb primitives can answer a count, every ranked //! request — either direction, at any offset, on any of the three axes — -//! resolves to one top-k axis `PathQuery` proved through grovedb's -//! unified `prove_query`, differing only in the `(axis, k, descending, -//! offset)` tuple the query carries. +//! resolves to one `RankedPage` axis traversal proved through grovedb's +//! unified `prove_query`, against the same `PathQuery` the verifier +//! reconstructs from the request. /// Indexed-axis top-k proof verification — returns the ranked groups the /// proof commits to, in ranking order. diff --git a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs index 6e540052520..4973d1a5431 100644 --- a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs @@ -1,37 +1,43 @@ use crate::error::drive::DriveError; use crate::error::Error; -use crate::query::{ - DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue, RankedPage, +use crate::query::drive_document_ranked_query::branches::{ + axis_entries_to_ranked, decompose_branch_paths, merge_branch_pages, }; +use crate::query::{DriveDocumentRankedQuery, RankedPage}; use crate::verify::RootHash; use dpp::version::PlatformVersion; -use grovedb::operations::proof::{indexed_axis::AxisEntries, VerifiedPathQuery}; -use grovedb::{GroveDb, PathQuery}; +use grovedb::operations::proof::VerifiedPathQuery; +use grovedb::GroveDb; +use grovedb::PathQuery; +use grovedb_query::AxisQuery; impl DriveDocumentRankedQuery<'_> { /// v0 of [`Self::verify_ranked_top_k_proof`]. /// - /// Rebuilds the same [`PathQuery::new_axis_top_k`] the prover used — - /// path via [`Self::indexed_property_name_tree_path`], the axis / - /// `k` / offset / direction from this query — and hands the proof to - /// [`GroveDb::verify_path_query`], which is an associated function — - /// no database handle is involved, so this compiles and runs in a - /// verifier-only build. + /// Rebuilds the same `PathQuery` the prover built — the subtree + /// path via [`Self::indexed_property_name_tree_path`], the + /// `RankedPage` traversal from `(axis, k, offset, descending)` — + /// and hands the proof to [`GroveDb::verify_path_query`], an + /// associated function: no database handle is involved, so this + /// compiles and runs in a verifier-only build. /// /// Three things are checked before the page is returned: /// - /// 1. **The proof answers this query.** The unified verifier is - /// query-as-input: nothing is echoed in the envelope; the proof is - /// verified against the verifier's own reconstruction of the - /// `PathQuery`, so a proof generated for a different ranking — or - /// a different page of the same ranking — fails verification - /// rather than being silently reinterpreted. + /// 1. **The proof covers this query's own traversal.** Binding is by + /// RECONSTRUCTION, not echo comparison: the verifier re-executes + /// the proof against the traversal it rebuilt from the request, + /// so a proof generated for a different ranking — or a different + /// page of the same ranking — fails to cover it rather than being + /// silently reinterpreted. (Coverage semantics make one benign + /// case verifiable that the retired exact-echo check refused: an + /// exhausted-walk proof is a complete answer under any admitting + /// cap — see the having surface's limit-tamper test for the pin.) /// 2. **The result's axis shape matches the requested axis** — a /// `Count` request must not come back holding `Sum` entries. This - /// is belt-and-braces on top of (1) (the query's axis already - /// rules it out) and exists so a future decoder change that - /// decouples the query from the decoded variant surfaces as an - /// error here instead of a mis-typed number reaching the caller. + /// is belt-and-braces on top of (1) (the tag check already rules + /// it out) and exists so a future decoder change that decouples + /// the tag from the decoded variant surfaces as an error here + /// instead of a mis-typed number reaching the caller. /// 3. **At most `k` entries.** Fewer is normal — the index may hold /// fewer groups than were asked for — but more would mean the /// proof committed a longer walk than the request authorized. @@ -53,7 +59,102 @@ impl DriveDocumentRankedQuery<'_> { proof: &[u8], platform_version: &PlatformVersion, ) -> Result<(RootHash, RankedPage), Error> { - let path = self.indexed_property_name_tree_path()?; + self.reject_offset_with_branches()?; + if self.prefix_branches.len() > 1 { + // `IN`-pinned request: the proof is one grovedb branched + // envelope. The branch set (and its order) comes from + // *this* query's own resolution; grovedb binds each branch + // tail to its branch key through the branching-level + // multi-key proof, reconstructs one root hash, and + // re-executes each branch's page against the traversal + // rebuilt from THIS query — coverage, not echo comparison. + // The page is then re-derived by the shared merge — the + // client never trusts a server-side merge. + let paths = (0..self.prefix_branches.len()) + .map(|branch| self.indexed_property_name_tree_path(branch)) + .collect::, Error>>()?; + let (prefix, keys, suffix) = decompose_branch_paths(&paths)?; + let path_query = PathQuery::new_branched_axis( + prefix, + keys.clone(), + suffix, + AxisQuery::top_k( + self.axis.into(), + self.k, + self.offset as u64, + self.descending, + ), + ); + let verified = GroveDb::verify_path_query( + proof, + &path_query, + &platform_version.drive.grove_version, + ) + .map_err(|e| Error::GroveDB(Box::new(e)))?; + let VerifiedPathQuery::BranchedAxisEntries { + root_hash, + branches, + } = verified + else { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a branched ranked top-k proof verified to a non-branched shape".to_string(), + ))); + }; + if branches.len() != keys.len() || branches.iter().map(|(key, _)| key).ne(keys.iter()) { + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a branched ranked top-k proof verified a different branch set than the \ + request resolved" + .to_string(), + ))); + } + let per_branch = branches + .into_iter() + .map(|(_key, entries)| { + // A branch key whose absence the branching-level Merk + // proof authenticates contributes an empty page — the + // same reading the unproved path gives an absent `IN` + // element. + let entries = match entries { + None => Vec::new(), + Some(entries) => axis_entries_to_ranked(self.axis, entries)?, + }; + if entries.len() > self.k as usize { + return Err(Error::Drive(DriveError::CorruptedDriveState(format!( + "a branch of a ranked top-k proof verified to {} entries for \ + k = {}", + entries.len(), + self.k + )))); + } + Ok(entries) + }) + .collect::, Error>>()?; + let entries = merge_branch_pages( + per_branch, + &self.prefix_branches, + self.descending, + self.k as usize, + )?; + return Ok(( + root_hash, + RankedPage { + skipped: 0, + entries, + }, + )); + } + self.verify_ranked_top_k_proof_v0_branch(0, proof, platform_version) + } + + /// One branch's verification — the entire pre-`IN` verifier, + /// parameterized by the prefix branch. + fn verify_ranked_top_k_proof_v0_branch( + &self, + branch: usize, + proof: &[u8], + platform_version: &PlatformVersion, + ) -> Result<(RootHash, RankedPage), Error> { + let path = self.indexed_property_name_tree_path(branch)?; let path_query = PathQuery::new_axis_top_k( path, self.axis.into(), @@ -61,76 +162,33 @@ impl DriveDocumentRankedQuery<'_> { self.offset as u64, self.descending, ); - let verified = GroveDb::verify_path_query(proof, &path_query, &platform_version.drive.grove_version) .map_err(|e| Error::GroveDB(Box::new(e)))?; - let VerifiedPathQuery::AxisEntries { root_hash, entries, skipped, } = verified else { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked top-k proof for the {:?} axis verified to a non-axis result shape", - self.axis - )))); - }; - - // A paginated (top-k) traversal always attests its skip count; - // `None` is the bounded traversal's shape and cannot answer this - // query. - let skipped = skipped.ok_or_else(|| { - Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked top-k proof for the {:?} axis carried no attested skip count", - self.axis - ))) - })?; - - let entries = match (self.axis, entries) { - (RankedAxis::Count, AxisEntries::Count(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(count, key)| RankedEntry { - key, - value: RankedEntryValue::Count(count), - }) - .collect::>(), - (RankedAxis::Sum, AxisEntries::Sum(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(sum, key)| RankedEntry { - key, - value: RankedEntryValue::Sum(sum), - }) - .collect::>(), - (RankedAxis::Avg, AxisEntries::Avg(entries)) => entries - .into_iter() - .map(|entry| entry.key_pair()) - .map(|(avg, key)| RankedEntry { - key, - value: RankedEntryValue::AvgFixedPoint(avg), - }) - .collect::>(), - (axis, other) => { - return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked top-k proof for the {axis:?} axis verified to {} entries of a \ - different axis shape", - other.len() - )))); - } + return Err(Error::Drive(DriveError::CorruptedDriveState( + "a ranked top-k proof verified to a different shape".to_string(), + ))); }; - + let entries = axis_entries_to_ranked(self.axis, entries)?; if entries.len() > self.k as usize { return Err(Error::Drive(DriveError::CorruptedDriveState(format!( - "ranked top-k proof for the {:?} axis verified to {} entries for k = {}", - self.axis, + "ranked top-k proof verified to {} entries for k = {}", entries.len(), self.k )))); } - + // A paginated traversal's proof always attests its skip. + let skipped = skipped.ok_or_else(|| { + Error::Drive(DriveError::CorruptedDriveState( + "a paginated ranked proof carried no skip attestation".to_string(), + )) + })?; Ok((root_hash, RankedPage { skipped, entries })) } } diff --git a/packages/rs-drive/tests/supporting_files/contract/grades/grades-compound-ranked-contract.json b/packages/rs-drive/tests/supporting_files/contract/grades/grades-compound-ranked-contract.json index df4179096ce..b1e5959910a 100644 --- a/packages/rs-drive/tests/supporting_files/contract/grades/grades-compound-ranked-contract.json +++ b/packages/rs-drive/tests/supporting_files/contract/grades/grades-compound-ranked-contract.json @@ -95,6 +95,62 @@ "grade" ], "additionalProperties": false + }, + "dualGrade": { + "type": "object", + "documentsMutable": false, + "canBeDeleted": false, + "indices": [ + { + "name": "byIdentityTagAndClass", + "properties": [ + { + "identityId": "asc" + }, + { + "tag": "asc" + }, + { + "class": "asc" + } + ], + "averageable": "grade", + "rangeAverageable": true, + "rankedAverageable": true + } + ], + "properties": { + "identityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "position": 0, + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "tag": { + "type": "string", + "maxLength": 32, + "position": 1 + }, + "class": { + "type": "string", + "maxLength": 32, + "position": 2 + }, + "grade": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "position": 3 + } + }, + "required": [ + "identityId", + "class", + "grade" + ], + "additionalProperties": false } } -} \ No newline at end of file +} diff --git a/packages/rs-sdk/src/mock/requests.rs b/packages/rs-sdk/src/mock/requests.rs index 9015c19a583..8cd7c1c445d 100644 --- a/packages/rs-sdk/src/mock/requests.rs +++ b/packages/rs-sdk/src/mock/requests.rs @@ -732,7 +732,9 @@ impl MockResponse for drive_proof_verifier::DocumentSplitAverages { /// is already an `i128`. One numeric column keeps the tuple flat while /// the tag preserves which axis produced it, so a mock expectation /// can't quietly turn a count into a sum. -type DocumentRankedPage = (u64, Vec<(Vec, u8, i128)>); +/// One mock-serialized ranked entry: `(key, axis tag, value, in_key)`. +type MockRankedEntry = (Vec, u8, i128, Option>); +type DocumentRankedPage = (u64, Vec); const RANKED_TAG_COUNT: u8 = 0; const RANKED_TAG_SUM: u8 = 1; @@ -741,18 +743,21 @@ const RANKED_TAG_AVG: u8 = 2; impl MockResponse for drive_proof_verifier::DocumentRankedEntries { fn mock_serialize(&self, _sdk: &MockDashPlatformSdk) -> Vec { let bincode_config = standard(); - let triples: Vec<(Vec, u8, i128)> = self + let triples: Vec = self .entries .iter() .map(|e| match e.value { - drive_proof_verifier::RankedEntryValue::Count(count) => { - (e.key.clone(), RANKED_TAG_COUNT, count as i128) - } + drive_proof_verifier::RankedEntryValue::Count(count) => ( + e.key.clone(), + RANKED_TAG_COUNT, + count as i128, + e.in_key.clone(), + ), drive_proof_verifier::RankedEntryValue::Sum(sum) => { - (e.key.clone(), RANKED_TAG_SUM, sum as i128) + (e.key.clone(), RANKED_TAG_SUM, sum as i128, e.in_key.clone()) } drive_proof_verifier::RankedEntryValue::AvgFixedPoint(avg) => { - (e.key.clone(), RANKED_TAG_AVG, avg) + (e.key.clone(), RANKED_TAG_AVG, avg, e.in_key.clone()) } }) .collect(); @@ -769,7 +774,7 @@ impl MockResponse for drive_proof_verifier::DocumentRankedEntries { bincode::decode_from_slice(buf, bincode_config).expect("decode DocumentRankedEntries"); let entries: Vec = triples .into_iter() - .map(|(key, tag, value)| { + .map(|(key, tag, value, in_key)| { let value = match tag { RANKED_TAG_COUNT => drive_proof_verifier::RankedEntryValue::Count( u64::try_from(value).expect("a Count entry round-trips through i128"), @@ -780,7 +785,7 @@ impl MockResponse for drive_proof_verifier::DocumentRankedEntries { RANKED_TAG_AVG => drive_proof_verifier::RankedEntryValue::AvgFixedPoint(value), other => panic!("unknown ranked axis tag {other} in mock expectation"), }; - drive_proof_verifier::RankedEntry { key, value } + drive_proof_verifier::RankedEntry { in_key, key, value } }) .collect(); drive_proof_verifier::DocumentRankedEntries {