Skip to content

Commit 377eef1

Browse files
authored
Report nondeterministic errors as unattestable (#2946)
This change sets the Graph-Attestable header on query results to signal to the indexer-service whether it should include an attestation for the client. Unattestable responses are those that are nondeterministic, according to [GIP 20](https://hackmd.io/@CGmldHLrTRaYRnAva-HoPQ/S1W2vMjEt).
1 parent 3353426 commit 377eef1

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

graph/src/data/query/error.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,63 @@ pub enum QueryExecutionError {
8383
ResultTooBig(usize, usize),
8484
}
8585

86+
impl QueryExecutionError {
87+
pub fn is_attestable(&self) -> bool {
88+
use self::QueryExecutionError::*;
89+
match self {
90+
OperationNameRequired
91+
| OperationNotFound(_)
92+
| NotSupported(_)
93+
| NoRootSubscriptionObjectType
94+
| NamedTypeError(_)
95+
| AbstractTypeError(_)
96+
| InvalidArgumentError(_, _, _)
97+
| MissingArgumentError(_, _)
98+
| InvalidVariableTypeError(_, _)
99+
| MissingVariableError(_, _)
100+
| OrderByNotSupportedError(_, _)
101+
| OrderByNotSupportedForType(_)
102+
| FilterNotSupportedError(_, _)
103+
| UnknownField(_, _, _)
104+
| EmptyQuery
105+
| MultipleSubscriptionFields
106+
| SubgraphDeploymentIdError(_)
107+
| InvalidFilterError
108+
| EntityFieldError(_, _)
109+
| ListTypesError(_, _)
110+
| ListFilterError(_)
111+
| AttributeTypeError(_, _)
112+
| EmptySelectionSet(_)
113+
| Unimplemented(_)
114+
| CyclicalFragment(_)
115+
| UndefinedFragment(_)
116+
| FulltextQueryRequiresFilter => true,
117+
NonNullError(_, _)
118+
| ListValueError(_, _)
119+
| ResolveEntitiesError(_)
120+
| RangeArgumentsError(_, _, _)
121+
| ValueParseError(_, _)
122+
| EntityParseError(_)
123+
| StoreError(_)
124+
| Timeout
125+
| EnumCoercionError(_, _, _, _, _)
126+
| ScalarCoercionError(_, _, _, _)
127+
| AmbiguousDerivedFromResult(_, _, _, _)
128+
| TooComplex(_, _)
129+
| TooDeep(_)
130+
| IncorrectPrefetchResult { .. }
131+
| Panic(_)
132+
| EventStreamError
133+
| TooExpensive
134+
| Throttled
135+
| DeploymentReverted
136+
| SubgraphManifestResolveError(_)
137+
| InvalidSubgraphManifest
138+
| ResultTooBig(_, _) => false,
139+
}
140+
}
141+
}
142+
86143
impl Error for QueryExecutionError {
87144
fn description(&self) -> &str {
88145
"Query execution error"
@@ -217,7 +274,7 @@ impl fmt::Display for QueryExecutionError {
217274
EventStreamError => write!(f, "error in the subscription event stream"),
218275
FulltextQueryRequiresFilter => write!(f, "fulltext search queries can only use EntityFilter::Equal"),
219276
TooExpensive => write!(f, "query is too expensive"),
220-
Throttled=> write!(f, "service is overloaded and can not run the query right now. Please try again in a few minutes"),
277+
Throttled => write!(f, "service is overloaded and can not run the query right now. Please try again in a few minutes"),
221278
DeploymentReverted => write!(f, "the chain was reorganized while executing the query"),
222279
SubgraphManifestResolveError(e) => write!(f, "failed to resolve subgraph manifest: {}", e),
223280
InvalidSubgraphManifest => write!(f, "invalid subgraph manifest file"),
@@ -271,6 +328,16 @@ pub enum QueryError {
271328
IndexingError,
272329
}
273330

331+
impl QueryError {
332+
pub fn is_attestable(&self) -> bool {
333+
match self {
334+
QueryError::EncodingError(_) | QueryError::ParseError(_) => true,
335+
QueryError::ExecutionError(err) => err.is_attestable(),
336+
QueryError::IndexingError => false,
337+
}
338+
}
339+
}
340+
274341
impl From<FromUtf8Error> for QueryError {
275342
fn from(e: FromUtf8Error) -> Self {
276343
QueryError::EncodingError(e)

graph/src/data/query/result.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ impl QueryResults {
163163
.header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent")
164164
.header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST")
165165
.header(CONTENT_TYPE, "application/json")
166+
.header(
167+
"Graph-Attestable",
168+
self.results.iter().all(|r| r.is_attestable()).to_string(),
169+
)
166170
.body(T::from(json))
167171
.unwrap()
168172
}
@@ -211,6 +215,10 @@ impl QueryResult {
211215
self.data.is_some()
212216
}
213217

218+
pub fn is_attestable(&self) -> bool {
219+
self.errors.iter().all(|err| err.is_attestable())
220+
}
221+
214222
pub fn to_result(self) -> Result<Option<r::Value>, Vec<QueryError>> {
215223
if self.has_errors() {
216224
Err(self.errors)

0 commit comments

Comments
 (0)