@o1-labs/mina-archive-node-graphql 0.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +92 -268
  3. package/build/src/blockchain/utils.js.map +1 -1
  4. package/build/src/config.js +86 -0
  5. package/build/src/config.js.map +1 -0
  6. package/build/src/db/archive-node-adapter/archive-node-adapter.js +29 -2
  7. package/build/src/db/archive-node-adapter/archive-node-adapter.js.map +1 -1
  8. package/build/src/db/archive-node-adapter/postgres-options.js +35 -0
  9. package/build/src/db/archive-node-adapter/postgres-options.js.map +1 -0
  10. package/build/src/db/sql/best-chain.js +5 -0
  11. package/build/src/db/sql/best-chain.js.map +1 -0
  12. package/build/src/db/sql/events-actions/queries.js +102 -21
  13. package/build/src/db/sql/events-actions/queries.js.map +1 -1
  14. package/build/src/db/sql/verification-key-updates/queries.js +100 -0
  15. package/build/src/db/sql/verification-key-updates/queries.js.map +1 -0
  16. package/build/src/db/sql/zkapp-commands/queries.js +363 -0
  17. package/build/src/db/sql/zkapp-commands/queries.js.map +1 -0
  18. package/build/src/db/sql/zkapp-commands/types.js +2 -0
  19. package/build/src/db/sql/zkapp-commands/types.js.map +1 -0
  20. package/build/src/errors/error.js +4 -1
  21. package/build/src/errors/error.js.map +1 -1
  22. package/build/src/index.js +44 -7
  23. package/build/src/index.js.map +1 -1
  24. package/build/src/resolvers.js +42 -23
  25. package/build/src/resolvers.js.map +1 -1
  26. package/build/src/schema-version.js +2 -0
  27. package/build/src/schema-version.js.map +1 -0
  28. package/build/src/server/cors.js +41 -0
  29. package/build/src/server/cors.js.map +1 -0
  30. package/build/src/server/graceful-shutdown.js +42 -0
  31. package/build/src/server/graceful-shutdown.js.map +1 -0
  32. package/build/src/server/graphql-armor.js +39 -0
  33. package/build/src/server/graphql-armor.js.map +1 -0
  34. package/build/src/server/logger.js +45 -0
  35. package/build/src/server/logger.js.map +1 -0
  36. package/build/src/server/metrics.js +72 -0
  37. package/build/src/server/metrics.js.map +1 -0
  38. package/build/src/server/plugins.js +31 -15
  39. package/build/src/server/plugins.js.map +1 -1
  40. package/build/src/server/rate-limit.js +143 -0
  41. package/build/src/server/rate-limit.js.map +1 -0
  42. package/build/src/server/readiness.js +45 -0
  43. package/build/src/server/readiness.js.map +1 -0
  44. package/build/src/server/request-logging.js +58 -0
  45. package/build/src/server/request-logging.js.map +1 -0
  46. package/build/src/server/server.js +38 -13
  47. package/build/src/server/server.js.map +1 -1
  48. package/build/src/services/actions-service/actions-service.js +26 -15
  49. package/build/src/services/actions-service/actions-service.js.map +1 -1
  50. package/build/src/services/blocks-service/blocks-service.js +10 -9
  51. package/build/src/services/blocks-service/blocks-service.js.map +1 -1
  52. package/build/src/services/network-service/network-service.js +17 -9
  53. package/build/src/services/network-service/network-service.js.map +1 -1
  54. package/build/src/services/verification-key-updates-service/verification-key-updates-service.interface.js +2 -0
  55. package/build/src/services/verification-key-updates-service/verification-key-updates-service.interface.js.map +1 -0
  56. package/build/src/services/verification-key-updates-service/verification-key-updates-service.js +41 -0
  57. package/build/src/services/verification-key-updates-service/verification-key-updates-service.js.map +1 -0
  58. package/build/src/services/zkapp-commands-service/zkapp-commands-service.interface.js +2 -0
  59. package/build/src/services/zkapp-commands-service/zkapp-commands-service.interface.js.map +1 -0
  60. package/build/src/services/zkapp-commands-service/zkapp-commands-service.js +115 -0
  61. package/build/src/services/zkapp-commands-service/zkapp-commands-service.js.map +1 -0
  62. package/build/src/tracing/jaeger-tracing.js +2 -1
  63. package/build/src/tracing/jaeger-tracing.js.map +1 -1
  64. package/build/tests/devnet-dump/action-state-invariants.js +165 -0
  65. package/build/tests/devnet-dump/action-state-invariants.js.map +1 -0
  66. package/build/tests/devnet-dump/devnet-dump.test.js +24 -10
  67. package/build/tests/devnet-dump/devnet-dump.test.js.map +1 -1
  68. package/build/tests/devnet-dump/setup.js +63 -17
  69. package/build/tests/devnet-dump/setup.js.map +1 -1
  70. package/build/tests/integration/action-state-ordering.test.js +139 -0
  71. package/build/tests/integration/action-state-ordering.test.js.map +1 -0
  72. package/build/tests/integration/action-state-setup.js +48 -0
  73. package/build/tests/integration/action-state-setup.js.map +1 -0
  74. package/build/tests/integration/consensus-vs-chain.test.js +81 -0
  75. package/build/tests/integration/consensus-vs-chain.test.js.map +1 -0
  76. package/build/tests/integration/integration.test.js +166 -2
  77. package/build/tests/integration/integration.test.js.map +1 -1
  78. package/build/tests/integration/postgres-timeout.test.js +92 -0
  79. package/build/tests/integration/postgres-timeout.test.js.map +1 -0
  80. package/build/tests/integration/setup.js +58 -1
  81. package/build/tests/integration/setup.js.map +1 -1
  82. package/build/tests/integration/verification-key-setup.js +49 -0
  83. package/build/tests/integration/verification-key-setup.js.map +1 -0
  84. package/build/tests/integration/verification-key-updates.test.js +159 -0
  85. package/build/tests/integration/verification-key-updates.test.js.map +1 -0
  86. package/build/tests/live-api/events.test.js +11 -7
  87. package/build/tests/live-api/events.test.js.map +1 -1
  88. package/build/tests/live-api/networkState.test.js +1 -0
  89. package/build/tests/live-api/networkState.test.js.map +1 -1
  90. package/build/tests/live-network/live-network.test.js +1 -0
  91. package/build/tests/live-network/live-network.test.js.map +1 -1
  92. package/build/tests/resolvers.test.js +65 -37
  93. package/build/tests/resolvers.test.js.map +1 -1
  94. package/build/tests/services/verification-key-updates-service/verification-key-updates-service.test.js +35 -0
  95. package/build/tests/services/verification-key-updates-service/verification-key-updates-service.test.js.map +1 -0
  96. package/build/tests/services/zkapp-commands-service/zkapp-commands-service.test.js +159 -0
  97. package/build/tests/services/zkapp-commands-service/zkapp-commands-service.test.js.map +1 -0
  98. package/build/tests/test-helpers.js +15 -0
  99. package/build/tests/test-helpers.js.map +1 -1
  100. package/build/tests/unit/best-chain-anchor.test.js +70 -0
  101. package/build/tests/unit/best-chain-anchor.test.js.map +1 -0
  102. package/build/tests/unit/config.test.js +106 -0
  103. package/build/tests/unit/config.test.js.map +1 -0
  104. package/build/tests/unit/cors.test.js +95 -0
  105. package/build/tests/unit/cors.test.js.map +1 -0
  106. package/build/tests/unit/error-masking.test.js +119 -0
  107. package/build/tests/unit/error-masking.test.js.map +1 -0
  108. package/build/tests/unit/graceful-shutdown.test.js +99 -0
  109. package/build/tests/unit/graceful-shutdown.test.js.map +1 -0
  110. package/build/tests/unit/graphql-armor.test.js +121 -0
  111. package/build/tests/unit/graphql-armor.test.js.map +1 -0
  112. package/build/tests/unit/logging.test.js +120 -0
  113. package/build/tests/unit/logging.test.js.map +1 -0
  114. package/build/tests/unit/metrics.test.js +114 -0
  115. package/build/tests/unit/metrics.test.js.map +1 -0
  116. package/build/tests/unit/network-service.test.js +66 -0
  117. package/build/tests/unit/network-service.test.js.map +1 -0
  118. package/build/tests/unit/postgres-options.test.js +56 -0
  119. package/build/tests/unit/postgres-options.test.js.map +1 -0
  120. package/build/tests/unit/rate-limit.test.js +271 -0
  121. package/build/tests/unit/rate-limit.test.js.map +1 -0
  122. package/build/tests/unit/readiness.test.js +75 -0
  123. package/build/tests/unit/readiness.test.js.map +1 -0
  124. package/build/tests/unit/schema-nullability.test.js +40 -0
  125. package/build/tests/unit/schema-nullability.test.js.map +1 -0
  126. package/build/tests/unit/schema-version.test.js +48 -0
  127. package/build/tests/unit/schema-version.test.js.map +1 -0
  128. package/build/tests/unit/yoga-http-contract.test.js +30 -0
  129. package/build/tests/unit/yoga-http-contract.test.js.map +1 -0
  130. package/build/tests/unit/zkapp-commands-gating.test.js +38 -0
  131. package/build/tests/unit/zkapp-commands-gating.test.js.map +1 -0
  132. package/build/zkapp/utils.js +2 -0
  133. package/build/zkapp/utils.js.map +1 -1
  134. package/package.json +33 -14
  135. package/schema.graphql +126 -8
  136. package/src/blockchain/types.ts +65 -1
  137. package/src/blockchain/utils.ts +26 -2
  138. package/src/config.ts +133 -0
  139. package/src/db/archive-node-adapter/archive-node-adapter.interface.ts +26 -3
  140. package/src/db/archive-node-adapter/archive-node-adapter.ts +65 -2
  141. package/src/db/archive-node-adapter/postgres-options.ts +90 -0
  142. package/src/db/sql/best-chain.ts +34 -0
  143. package/src/db/sql/events-actions/queries.ts +168 -24
  144. package/src/db/sql/verification-key-updates/queries.ts +132 -0
  145. package/src/db/sql/zkapp-commands/queries.ts +406 -0
  146. package/src/db/sql/zkapp-commands/types.ts +43 -0
  147. package/src/envionment.d.ts +20 -0
  148. package/src/errors/error.ts +17 -1
  149. package/src/index.ts +64 -7
  150. package/src/resolvers-types.ts +311 -24
  151. package/src/resolvers.ts +70 -34
  152. package/src/schema-version.ts +24 -0
  153. package/src/server/cors.ts +97 -0
  154. package/src/server/graceful-shutdown.ts +76 -0
  155. package/src/server/graphql-armor.ts +78 -0
  156. package/src/server/logger.ts +72 -0
  157. package/src/server/metrics.ts +104 -0
  158. package/src/server/plugins.ts +53 -19
  159. package/src/server/rate-limit.ts +274 -0
  160. package/src/server/readiness.ts +78 -0
  161. package/src/server/request-logging.ts +83 -0
  162. package/src/server/server.ts +70 -15
  163. package/src/services/actions-service/actions-service.ts +87 -24
  164. package/src/services/blocks-service/blocks-service.ts +16 -14
  165. package/src/services/network-service/network-service.ts +30 -12
  166. package/src/services/verification-key-updates-service/verification-key-updates-service.interface.ts +9 -0
  167. package/src/services/verification-key-updates-service/verification-key-updates-service.ts +72 -0
  168. package/src/services/zkapp-commands-service/zkapp-commands-service.interface.ts +9 -0
  169. package/src/services/zkapp-commands-service/zkapp-commands-service.ts +181 -0
  170. package/src/tracing/jaeger-tracing.ts +2 -1
  171. package/build/tests/consensus/run-compare.js +0 -40
  172. package/build/tests/consensus/run-compare.js.map +0 -1
  173. package/build/tests/consensus/select-consensus-precomputed.js +0 -80
  174. package/build/tests/consensus/select-consensus-precomputed.js.map +0 -1
  175. package/build/tests/consensus/types.js +0 -15
  176. package/build/tests/consensus/types.js.map +0 -1
@@ -0,0 +1,406 @@
1
+ import type postgres from 'postgres';
2
+ import { BlockStatusFilter } from '../../../blockchain/types.js';
3
+ import type { ZkappCommandDatabaseRow } from './types.js';
4
+
5
+ function blockRangeCte(
6
+ dbClient: postgres.Sql,
7
+ status: BlockStatusFilter,
8
+ to: number,
9
+ from: number
10
+ ) {
11
+ if (status === BlockStatusFilter.canonical) {
12
+ return dbClient`
13
+ max_height AS (
14
+ SELECT MAX(height) AS value FROM blocks
15
+ ),
16
+ block_range AS (
17
+ SELECT
18
+ b.id,
19
+ b.state_hash,
20
+ b.parent_hash,
21
+ b.height,
22
+ b.global_slot_since_genesis,
23
+ b.global_slot_since_hard_fork,
24
+ b.timestamp,
25
+ b.chain_status,
26
+ b.ledger_hash,
27
+ b.last_vrf_output
28
+ FROM blocks b
29
+ WHERE b.chain_status = 'canonical'
30
+ AND b.height >= ${from}
31
+ AND b.height < ${to}
32
+ )
33
+ `;
34
+ }
35
+
36
+ return dbClient`
37
+ RECURSIVE max_height AS (
38
+ SELECT MAX(height) AS value FROM blocks
39
+ ),
40
+ pending_chain AS (
41
+ (
42
+ SELECT
43
+ id,
44
+ state_hash,
45
+ parent_hash,
46
+ parent_id,
47
+ height,
48
+ global_slot_since_genesis,
49
+ global_slot_since_hard_fork,
50
+ timestamp,
51
+ chain_status,
52
+ ledger_hash,
53
+ last_vrf_output
54
+ FROM blocks
55
+ WHERE height = (SELECT value FROM max_height)
56
+ )
57
+ UNION ALL
58
+ SELECT
59
+ b.id,
60
+ b.state_hash,
61
+ b.parent_hash,
62
+ b.parent_id,
63
+ b.height,
64
+ b.global_slot_since_genesis,
65
+ b.global_slot_since_hard_fork,
66
+ b.timestamp,
67
+ b.chain_status,
68
+ b.ledger_hash,
69
+ b.last_vrf_output
70
+ FROM blocks b
71
+ INNER JOIN pending_chain
72
+ ON b.id = pending_chain.parent_id
73
+ AND pending_chain.id <> pending_chain.parent_id
74
+ AND pending_chain.chain_status <> 'canonical'
75
+ ),
76
+ full_chain AS (
77
+ SELECT DISTINCT
78
+ id,
79
+ state_hash,
80
+ parent_id,
81
+ parent_hash,
82
+ height,
83
+ global_slot_since_genesis,
84
+ global_slot_since_hard_fork,
85
+ timestamp,
86
+ chain_status,
87
+ ledger_hash,
88
+ last_vrf_output
89
+ FROM (
90
+ SELECT
91
+ id,
92
+ state_hash,
93
+ parent_id,
94
+ parent_hash,
95
+ height,
96
+ global_slot_since_genesis,
97
+ global_slot_since_hard_fork,
98
+ timestamp,
99
+ chain_status,
100
+ ledger_hash,
101
+ last_vrf_output
102
+ FROM pending_chain
103
+ WHERE height >= ${from}
104
+ AND height < ${to}
105
+ UNION ALL
106
+ SELECT
107
+ id,
108
+ state_hash,
109
+ parent_id,
110
+ parent_hash,
111
+ height,
112
+ global_slot_since_genesis,
113
+ global_slot_since_hard_fork,
114
+ timestamp,
115
+ chain_status,
116
+ ledger_hash,
117
+ last_vrf_output
118
+ FROM blocks b
119
+ WHERE chain_status = 'canonical'
120
+ AND b.height >= ${from}
121
+ AND b.height < ${to}
122
+ ) AS resolved_chain
123
+ ),
124
+ block_range AS (
125
+ SELECT
126
+ b.id,
127
+ b.state_hash,
128
+ b.parent_hash,
129
+ b.height,
130
+ b.global_slot_since_genesis,
131
+ b.global_slot_since_hard_fork,
132
+ b.timestamp,
133
+ b.chain_status,
134
+ b.ledger_hash,
135
+ b.last_vrf_output
136
+ FROM full_chain b
137
+ WHERE 1 = 1
138
+ ${
139
+ status === BlockStatusFilter.all
140
+ ? dbClient``
141
+ : dbClient`AND b.chain_status = ${status.toLowerCase()}`
142
+ }
143
+ )
144
+ `;
145
+ }
146
+
147
+ function accountUpdateFilterCtes(
148
+ dbClient: postgres.Sql,
149
+ accountPublicKey?: string,
150
+ tokenId?: string
151
+ ) {
152
+ if (!accountPublicKey && !tokenId) {
153
+ return dbClient``;
154
+ }
155
+
156
+ return dbClient`
157
+ ,
158
+ matching_account_identifiers AS (
159
+ SELECT ai.id
160
+ FROM account_identifiers ai
161
+ JOIN public_keys pk ON ai.public_key_id = pk.id
162
+ JOIN tokens t ON ai.token_id = t.id
163
+ WHERE 1 = 1
164
+ ${
165
+ accountPublicKey
166
+ ? dbClient`AND pk.value = ${accountPublicKey}`
167
+ : dbClient``
168
+ }
169
+ ${tokenId ? dbClient`AND t.value = ${tokenId}` : dbClient``}
170
+ ),
171
+ target_account_updates AS (
172
+ SELECT zkau.id AS account_update_id
173
+ FROM matching_account_identifiers mai
174
+ JOIN zkapp_account_update_body zkub
175
+ ON zkub.account_identifier_id = mai.id
176
+ JOIN zkapp_account_update zkau ON zkau.body_id = zkub.id
177
+ )
178
+ `;
179
+ }
180
+
181
+ function accountUpdateRowsJoin(
182
+ dbClient: postgres.Sql,
183
+ accountPublicKey?: string,
184
+ tokenId?: string
185
+ ) {
186
+ if (!accountPublicKey && !tokenId) {
187
+ return dbClient`
188
+ JOIN LATERAL unnest(zkc.zkapp_account_updates_ids)
189
+ WITH ORDINALITY AS account_update_ids(account_update_id, account_update_order) ON true
190
+ JOIN zkapp_account_update zkau ON zkau.id = account_update_ids.account_update_id
191
+ `;
192
+ }
193
+
194
+ return dbClient`
195
+ JOIN target_account_updates tau
196
+ ON tau.account_update_id = ANY(zkc.zkapp_account_updates_ids)
197
+ JOIN LATERAL (
198
+ SELECT account_update_ids.account_update_id, account_update_ids.account_update_order
199
+ FROM unnest(zkc.zkapp_account_updates_ids)
200
+ WITH ORDINALITY AS account_update_ids(account_update_id, account_update_order)
201
+ WHERE account_update_ids.account_update_id = tau.account_update_id
202
+ ) account_update_ids ON true
203
+ JOIN zkapp_account_update zkau ON zkau.id = tau.account_update_id
204
+ `;
205
+ }
206
+
207
+ export function getZkappCommandsQuery(
208
+ dbClient: postgres.Sql,
209
+ status: BlockStatusFilter,
210
+ to: number,
211
+ from: number,
212
+ accountPublicKey?: string,
213
+ tokenId?: string
214
+ ) {
215
+ return dbClient<ZkappCommandDatabaseRow[]>`
216
+ WITH ${blockRangeCte(dbClient, status, to, from)}
217
+ ${accountUpdateFilterCtes(dbClient, accountPublicKey, tokenId)}
218
+ SELECT
219
+ b.id AS block_id,
220
+ b.state_hash,
221
+ b.parent_hash,
222
+ b.height,
223
+ b.global_slot_since_genesis,
224
+ b.global_slot_since_hard_fork,
225
+ b.timestamp,
226
+ b.chain_status,
227
+ b.ledger_hash,
228
+ (SELECT value FROM max_height) - b.height AS distance_from_max_block_height,
229
+ b.last_vrf_output,
230
+ zkc.hash,
231
+ zkc.memo,
232
+ bzkc.sequence_no AS sequence_number,
233
+ fee_payer_pk.value AS fee_payer,
234
+ fpb.fee,
235
+ zkau.id AS account_update_id,
236
+ account_update_ids.account_update_order,
237
+ account_update_pk.value AS public_key,
238
+ t.value AS token_id,
239
+ zkub.authorization_kind,
240
+ zkub.balance_change,
241
+ zkub.increment_nonce,
242
+ zkub.call_depth,
243
+ COALESCE(actions.fields, '[]'::jsonb) AS actions,
244
+ COALESCE(events.fields, '[]'::jsonb) AS events,
245
+ app_state.fields AS app_state,
246
+ account_precondition_state.fields AS account_precondition_state,
247
+ account_precondition_action_state.fields AS account_precondition_action_state,
248
+ account_precondition.proved_state AS account_precondition_proved_state,
249
+ account_precondition.is_new AS account_precondition_is_new,
250
+ network_global_slot_bounds.global_slot_lower_bound AS network_precondition_global_slot_lower_bound,
251
+ network_global_slot_bounds.global_slot_upper_bound AS network_precondition_global_slot_upper_bound
252
+ FROM block_range b
253
+ JOIN blocks_zkapp_commands bzkc
254
+ ON b.id = bzkc.block_id
255
+ AND bzkc.status <> 'failed'
256
+ JOIN zkapp_commands zkc ON bzkc.zkapp_command_id = zkc.id
257
+ JOIN zkapp_fee_payer_body fpb ON zkc.zkapp_fee_payer_body_id = fpb.id
258
+ JOIN public_keys fee_payer_pk ON fpb.public_key_id = fee_payer_pk.id
259
+ ${accountUpdateRowsJoin(dbClient, accountPublicKey, tokenId)}
260
+ JOIN zkapp_account_update_body zkub ON zkau.body_id = zkub.id
261
+ JOIN account_identifiers ai ON zkub.account_identifier_id = ai.id
262
+ JOIN public_keys account_update_pk
263
+ ON ai.public_key_id = account_update_pk.id
264
+ ${
265
+ accountPublicKey
266
+ ? dbClient`AND account_update_pk.value = ${accountPublicKey}`
267
+ : dbClient``
268
+ }
269
+ JOIN tokens t
270
+ ON ai.token_id = t.id
271
+ ${tokenId ? dbClient`AND t.value = ${tokenId}` : dbClient``}
272
+ LEFT JOIN zkapp_updates update_body ON update_body.id = zkub.update_id
273
+ LEFT JOIN zkapp_states_nullable app_state_rows ON app_state_rows.id = update_body.app_state_id
274
+ LEFT JOIN zkapp_account_precondition account_precondition
275
+ ON account_precondition.id = zkub.zkapp_account_precondition_id
276
+ LEFT JOIN zkapp_states_nullable account_precondition_state_rows
277
+ ON account_precondition_state_rows.id = account_precondition.state_id
278
+ LEFT JOIN zkapp_action_states account_precondition_action_state_rows
279
+ ON account_precondition_action_state_rows.id = account_precondition.action_state_id
280
+ LEFT JOIN zkapp_network_precondition network_precondition
281
+ ON network_precondition.id = zkub.zkapp_network_precondition_id
282
+ LEFT JOIN zkapp_global_slot_bounds network_global_slot_bounds
283
+ ON network_global_slot_bounds.id = network_precondition.global_slot_since_genesis
284
+ LEFT JOIN LATERAL (
285
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('fields', field_array.fields) ORDER BY event_ids.event_order), '[]'::jsonb) AS fields
286
+ FROM zkapp_events action_events
287
+ JOIN LATERAL unnest(action_events.element_ids)
288
+ WITH ORDINALITY AS event_ids(field_array_id, event_order) ON true
289
+ JOIN zkapp_field_array event_field_array ON event_field_array.id = event_ids.field_array_id
290
+ JOIN LATERAL (
291
+ SELECT COALESCE(jsonb_agg(zkf.field ORDER BY field_ids.field_order), '[]'::jsonb) AS fields
292
+ FROM unnest(event_field_array.element_ids)
293
+ WITH ORDINALITY AS field_ids(field_id, field_order)
294
+ JOIN zkapp_field zkf ON zkf.id = field_ids.field_id
295
+ ) field_array ON true
296
+ WHERE action_events.id = zkub.actions_id
297
+ ) actions ON true
298
+ LEFT JOIN LATERAL (
299
+ SELECT COALESCE(jsonb_agg(jsonb_build_object('fields', field_array.fields) ORDER BY event_ids.event_order), '[]'::jsonb) AS fields
300
+ FROM zkapp_events account_update_events
301
+ JOIN LATERAL unnest(account_update_events.element_ids)
302
+ WITH ORDINALITY AS event_ids(field_array_id, event_order) ON true
303
+ JOIN zkapp_field_array event_field_array ON event_field_array.id = event_ids.field_array_id
304
+ JOIN LATERAL (
305
+ SELECT COALESCE(jsonb_agg(zkf.field ORDER BY field_ids.field_order), '[]'::jsonb) AS fields
306
+ FROM unnest(event_field_array.element_ids)
307
+ WITH ORDINALITY AS field_ids(field_id, field_order)
308
+ JOIN zkapp_field zkf ON zkf.id = field_ids.field_id
309
+ ) field_array ON true
310
+ WHERE account_update_events.id = zkub.events_id
311
+ ) events ON true
312
+ LEFT JOIN LATERAL (
313
+ SELECT jsonb_build_object('fields', jsonb_agg(zkf.field ORDER BY state_fields.field_order)) AS fields
314
+ FROM (VALUES
315
+ (0, app_state_rows.element0), (1, app_state_rows.element1),
316
+ (2, app_state_rows.element2), (3, app_state_rows.element3),
317
+ (4, app_state_rows.element4), (5, app_state_rows.element5),
318
+ (6, app_state_rows.element6), (7, app_state_rows.element7),
319
+ (8, app_state_rows.element8), (9, app_state_rows.element9),
320
+ (10, app_state_rows.element10), (11, app_state_rows.element11),
321
+ (12, app_state_rows.element12), (13, app_state_rows.element13),
322
+ (14, app_state_rows.element14), (15, app_state_rows.element15),
323
+ (16, app_state_rows.element16), (17, app_state_rows.element17),
324
+ (18, app_state_rows.element18), (19, app_state_rows.element19),
325
+ (20, app_state_rows.element20), (21, app_state_rows.element21),
326
+ (22, app_state_rows.element22), (23, app_state_rows.element23),
327
+ (24, app_state_rows.element24), (25, app_state_rows.element25),
328
+ (26, app_state_rows.element26), (27, app_state_rows.element27),
329
+ (28, app_state_rows.element28), (29, app_state_rows.element29),
330
+ (30, app_state_rows.element30), (31, app_state_rows.element31)
331
+ ) AS state_fields(field_order, field_id)
332
+ LEFT JOIN zkapp_field zkf ON zkf.id = state_fields.field_id
333
+ WHERE app_state_rows.id IS NOT NULL
334
+ ) app_state ON true
335
+ LEFT JOIN LATERAL (
336
+ SELECT jsonb_build_object('fields', jsonb_agg(zkf.field ORDER BY state_fields.field_order)) AS fields
337
+ FROM (VALUES
338
+ (0, account_precondition_state_rows.element0), (1, account_precondition_state_rows.element1),
339
+ (2, account_precondition_state_rows.element2), (3, account_precondition_state_rows.element3),
340
+ (4, account_precondition_state_rows.element4), (5, account_precondition_state_rows.element5),
341
+ (6, account_precondition_state_rows.element6), (7, account_precondition_state_rows.element7),
342
+ (8, account_precondition_state_rows.element8), (9, account_precondition_state_rows.element9),
343
+ (10, account_precondition_state_rows.element10), (11, account_precondition_state_rows.element11),
344
+ (12, account_precondition_state_rows.element12), (13, account_precondition_state_rows.element13),
345
+ (14, account_precondition_state_rows.element14), (15, account_precondition_state_rows.element15),
346
+ (16, account_precondition_state_rows.element16), (17, account_precondition_state_rows.element17),
347
+ (18, account_precondition_state_rows.element18), (19, account_precondition_state_rows.element19),
348
+ (20, account_precondition_state_rows.element20), (21, account_precondition_state_rows.element21),
349
+ (22, account_precondition_state_rows.element22), (23, account_precondition_state_rows.element23),
350
+ (24, account_precondition_state_rows.element24), (25, account_precondition_state_rows.element25),
351
+ (26, account_precondition_state_rows.element26), (27, account_precondition_state_rows.element27),
352
+ (28, account_precondition_state_rows.element28), (29, account_precondition_state_rows.element29),
353
+ (30, account_precondition_state_rows.element30), (31, account_precondition_state_rows.element31)
354
+ ) AS state_fields(field_order, field_id)
355
+ LEFT JOIN zkapp_field zkf ON zkf.id = state_fields.field_id
356
+ WHERE account_precondition_state_rows.id IS NOT NULL
357
+ ) account_precondition_state ON true
358
+ LEFT JOIN LATERAL (
359
+ SELECT jsonb_build_object('fields', jsonb_agg(zkf.field ORDER BY action_state_fields.field_order)) AS fields
360
+ FROM (VALUES
361
+ (0, account_precondition_action_state_rows.element0),
362
+ (1, account_precondition_action_state_rows.element1),
363
+ (2, account_precondition_action_state_rows.element2),
364
+ (3, account_precondition_action_state_rows.element3),
365
+ (4, account_precondition_action_state_rows.element4)
366
+ ) AS action_state_fields(field_order, field_id)
367
+ LEFT JOIN zkapp_field zkf ON zkf.id = action_state_fields.field_id
368
+ WHERE account_precondition_action_state_rows.id IS NOT NULL
369
+ ) account_precondition_action_state ON true
370
+ ORDER BY b.height, bzkc.sequence_no, account_update_ids.account_update_order;
371
+ `;
372
+ }
373
+
374
+ export function getZkappCommandAccountUpdateCountQuery(
375
+ dbClient: postgres.Sql,
376
+ status: BlockStatusFilter,
377
+ to: number,
378
+ from: number,
379
+ accountPublicKey?: string,
380
+ tokenId?: string
381
+ ) {
382
+ if (!accountPublicKey && !tokenId) {
383
+ return dbClient<{ count: string }[]>`
384
+ WITH ${blockRangeCte(dbClient, status, to, from)}
385
+ SELECT COALESCE(SUM(cardinality(zkc.zkapp_account_updates_ids)), 0)::text AS count
386
+ FROM block_range b
387
+ JOIN blocks_zkapp_commands bzkc
388
+ ON b.id = bzkc.block_id
389
+ AND bzkc.status <> 'failed'
390
+ JOIN zkapp_commands zkc ON bzkc.zkapp_command_id = zkc.id;
391
+ `;
392
+ }
393
+
394
+ return dbClient<{ count: string }[]>`
395
+ WITH ${blockRangeCte(dbClient, status, to, from)}
396
+ ${accountUpdateFilterCtes(dbClient, accountPublicKey, tokenId)}
397
+ SELECT COUNT(*) AS count
398
+ FROM block_range b
399
+ JOIN blocks_zkapp_commands bzkc
400
+ ON b.id = bzkc.block_id
401
+ AND bzkc.status <> 'failed'
402
+ JOIN zkapp_commands zkc ON bzkc.zkapp_command_id = zkc.id
403
+ JOIN target_account_updates tau
404
+ ON tau.account_update_id = ANY(zkc.zkapp_account_updates_ids);
405
+ `;
406
+ }
@@ -0,0 +1,43 @@
1
+ export type RawFieldArray = {
2
+ fields: (string | null)[];
3
+ };
4
+
5
+ export type RawNestedFieldArray = {
6
+ fields: string[];
7
+ }[];
8
+
9
+ export type ZkappCommandDatabaseRow = {
10
+ block_id: number;
11
+ state_hash: string;
12
+ parent_hash: string;
13
+ height: string;
14
+ global_slot_since_genesis: string;
15
+ global_slot_since_hard_fork: string;
16
+ timestamp: string;
17
+ chain_status: string;
18
+ ledger_hash: string;
19
+ distance_from_max_block_height: string;
20
+ last_vrf_output: string;
21
+ hash: string;
22
+ memo: string;
23
+ sequence_number: number;
24
+ fee_payer: string;
25
+ fee: string;
26
+ account_update_id: number;
27
+ account_update_order: string;
28
+ public_key: string;
29
+ token_id: string;
30
+ authorization_kind: string;
31
+ balance_change: string;
32
+ increment_nonce: boolean;
33
+ call_depth: number;
34
+ actions: RawNestedFieldArray;
35
+ events: RawNestedFieldArray;
36
+ app_state: RawFieldArray | null;
37
+ account_precondition_state: RawFieldArray | null;
38
+ account_precondition_action_state: RawFieldArray | null;
39
+ account_precondition_proved_state: boolean | null;
40
+ account_precondition_is_new: boolean | null;
41
+ network_precondition_global_slot_lower_bound: number | null;
42
+ network_precondition_global_slot_upper_bound: number | null;
43
+ };
@@ -4,11 +4,31 @@ declare global {
4
4
  LOG_LEVEL: string;
5
5
  PORT?: string;
6
6
  PG_CONN: string;
7
+ SHUTDOWN_TIMEOUT_MS?: string;
8
+ PG_MAX_CONNECTIONS?: string;
9
+ PG_IDLE_TIMEOUT?: string;
10
+ PG_CONNECT_TIMEOUT?: string;
11
+ PG_STATEMENT_TIMEOUT?: string;
7
12
  CORS_ORIGIN?: string;
13
+ READINESS_PING_TIMEOUT_MS?: string;
14
+ RATE_LIMIT_MAX?: string;
15
+ RATE_LIMIT_WINDOW_MS?: string;
16
+ TRUST_PROXY?: string;
17
+ GRAPHQL_MAX_DEPTH?: string;
18
+ GRAPHQL_MAX_ALIASES?: string;
19
+ GRAPHQL_MAX_TOKENS?: string;
20
+ GRAPHQL_MAX_COST?: string;
21
+ BLOCK_RANGE_SIZE?: string;
22
+ ZKAPP_COMMAND_RANGE_SIZE?: string;
23
+ ZKAPP_COMMAND_ACCOUNT_UPDATE_LIMIT?: string;
8
24
  ENABLE_LOGGING?: bool;
25
+ ENABLE_METRICS?: bool;
9
26
  ENABLE_INTROSPECTION?: bool;
10
27
  ENABLE_GRAPHIQL?: bool;
11
28
  ENABLE_JAEGER?: bool;
29
+ ENABLE_BLOCK_TRANSACTION_DETAILS?: bool;
30
+ ENABLE_ZKAPP_COMMANDS_QUERY?: bool;
31
+ ENABLED_QUERIES?: string;
12
32
  JAEGER_ENDPOINT?: string;
13
33
  JAEGER_SERVICE_NAME?: string;
14
34
  }
@@ -1,6 +1,11 @@
1
1
  import { GraphQLError } from 'graphql';
2
2
 
3
- export { throwGraphQLError, throwActionStateError, throwBlockRangeError };
3
+ export {
4
+ throwGraphQLError,
5
+ throwActionStateError,
6
+ throwActionStateOutOfRangeError,
7
+ throwBlockRangeError,
8
+ };
4
9
 
5
10
  function throwGraphQLError(message: string, code?: string, status?: number) {
6
11
  throw new GraphQLError(message, {
@@ -15,6 +20,17 @@ function throwActionStateError(message: string) {
15
20
  throwGraphQLError(message, 'ACTION_STATE_NOT_FOUND', 400);
16
21
  }
17
22
 
23
+ /**
24
+ * The action state is a real action state of the requested account, but it sits
25
+ * below the block range this query covers, and the account emitted actions in
26
+ * between. Any list we could return would be missing those actions. Returning it
27
+ * silently is what makes a client fold a wrong action state, so this is an error
28
+ * and not a truncated answer.
29
+ */
30
+ function throwActionStateOutOfRangeError(message: string) {
31
+ throwGraphQLError(message, 'ACTION_STATE_OUT_OF_RANGE', 400);
32
+ }
33
+
18
34
  function throwBlockRangeError(message: string) {
19
35
  throwGraphQLError(message, 'BLOCK_RANGE_ERROR', 400);
20
36
  }
package/src/index.ts CHANGED
@@ -3,29 +3,86 @@
3
3
  import { buildContext } from './context.js';
4
4
  import { buildServer } from './server/server.js';
5
5
  import { buildPlugins } from './server/plugins.js';
6
+ import { createGracefulShutdown } from './server/graceful-shutdown.js';
7
+ import { assertValidConfig } from './config.js';
8
+ import { logger } from './server/logger.js';
6
9
 
7
10
  const PORT = process.env.PORT || 8080;
11
+ const SHUTDOWN_TIMEOUT_MS = Number(process.env.SHUTDOWN_TIMEOUT_MS) || 20000;
12
+
13
+ function withTimeout(
14
+ label: string,
15
+ ms: number,
16
+ run: () => Promise<unknown>
17
+ ): Promise<void> {
18
+ let timer: ReturnType<typeof setTimeout> | undefined;
19
+ return Promise.race([
20
+ run(),
21
+ new Promise<void>((_, reject) => {
22
+ timer = setTimeout(
23
+ () => reject(new Error(`${label} timed out after ${ms}ms`)),
24
+ ms
25
+ );
26
+ }),
27
+ ])
28
+ .then(() => undefined)
29
+ .finally(() => {
30
+ if (timer !== undefined) clearTimeout(timer);
31
+ });
32
+ }
8
33
 
9
34
  (async function main() {
10
35
  try {
36
+ assertValidConfig();
11
37
  const context = await buildContext(process.env.PG_CONN);
12
- const plugins = await buildPlugins();
38
+ const { plugins, provider } = await buildPlugins();
13
39
  const server = buildServer(context, plugins);
14
40
 
15
41
  server.listen(PORT, () => {
16
- console.info(`Server is running on port: ${PORT}`);
42
+ logger.info({ port: PORT }, 'server started');
43
+ });
44
+
45
+ const shutdown = createGracefulShutdown({
46
+ timeoutMs: SHUTDOWN_TIMEOUT_MS,
47
+ // Stop accepting connections and wait for in-flight requests to drain.
48
+ closeServer: () =>
49
+ new Promise<void>((resolve, reject) => {
50
+ server.close((error) => (error ? reject(error) : resolve()));
51
+ // `close` also waits on idle keep-alive sockets, which browser
52
+ // clients hold open for keepAliveTimeout; dropping them keeps the
53
+ // drain prompt so the closers below still run inside the timeout.
54
+ server.closeIdleConnections();
55
+ }),
56
+ closers: [
57
+ // Flush buffered spans without letting an unreachable collector consume
58
+ // the whole shutdown budget.
59
+ () =>
60
+ withTimeout('trace flush', 3000, async () => {
61
+ if (provider) await provider.shutdown();
62
+ }),
63
+ // Close the Postgres pool. postgres.js waits forever by default for
64
+ // open connections, so bound the wait and let the force timer handle any
65
+ // still-running queries.
66
+ () => withTimeout('pg pool close', 5000, () => context.db_client.close()),
67
+ ],
17
68
  });
18
69
 
19
70
  ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => {
20
- process.on(signal, () => server.close());
71
+ process.on(signal, () => void shutdown(signal));
21
72
  });
22
73
 
23
- server.on('close', async () => {
24
- await context.db_client.close();
25
- process.exit(0); // normal termination
74
+ // Crashes exit non-zero: an exit 0 reads as a clean stop to Kubernetes and
75
+ // systemd, suppressing restarts and non-zero-exit alerting.
76
+ process.on('uncaughtException', (error) => {
77
+ logger.error({ err: error }, 'uncaught exception');
78
+ void shutdown('uncaughtException', 1);
79
+ });
80
+ process.on('unhandledRejection', (reason) => {
81
+ logger.error({ err: reason }, 'unhandled rejection');
82
+ void shutdown('unhandledRejection', 1);
26
83
  });
27
84
  } catch (error) {
28
- console.error('An error occurred:', error);
85
+ logger.error({ err: error }, 'fatal error during startup');
29
86
  process.exit(1); // exit with an error code
30
87
  }
31
88
  })();