• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 820

17 Nov 2025 08:58AM UTC coverage: 37.322% (-1.6%) from 38.943%
820

push

buildkite

web-flow
Merge pull request #18121 from MinaProtocol/lyh/compat-into-dev-nov17-2025

Compatible into develop Nov17th 2025

82 of 261 new or added lines in 23 files covered. (31.42%)

1312 existing lines in 63 files now uncovered.

27536 of 73780 relevant lines covered (37.32%)

51720.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

42.86
/src/app/archive_hardfork_toolbox/sql.ml
1
open Async
2
open Core
3
open Caqti_request.Infix
4

5
module type CONNECTION = Mina_caqti.CONNECTION
6

7
module Protocol_version = struct
NEW
8
  type t = { transaction : int; network : int; patch : int } [@@deriving equal]
×
9

10
  let of_string : string -> t = function
NEW
11
    | version_str -> (
×
12
        try
NEW
13
          Scanf.sscanf version_str "%d.%d.%d" (fun transaction network patch ->
×
NEW
14
              { transaction; network; patch } )
×
NEW
15
        with _ ->
×
16
          failwithf
17
            "Invalid protocol version string: %s. Expected format \
18
             <network>.<transaction>.<patch>"
19
            version_str () )
20

21
  let to_string { transaction; network; patch } =
NEW
22
    sprintf "%d.%d.%d" network transaction patch
×
23

24
  let typ =
25
    let encode { transaction; network; patch } =
NEW
26
      Ok (transaction, network, patch)
×
27
    in
28
    let decode (transaction, network, patch) =
NEW
29
      Ok { transaction; network; patch }
×
30
    in
31
    Caqti_type.(custom ~encode ~decode (t3 int int int))
5✔
32
end
33

34
module Block_info = struct
35
  type t =
36
    { id : int
37
    ; height : int64
38
    ; state_hash : string
39
    ; protocol_version : Protocol_version.t
40
    }
41

42
  let typ =
43
    let encode { id; height; state_hash; protocol_version } =
NEW
44
      Ok (id, height, state_hash, protocol_version)
×
45
    in
46
    let decode (id, height, state_hash, protocol_version) =
NEW
47
      Ok { id; height; state_hash; protocol_version }
×
48
    in
49
    Caqti_type.(
50
      custom ~encode ~decode (t4 int int64 string Protocol_version.typ))
5✔
51
end
52

53
let chain_of_query_templated ~join_condition =
54
  {%string|
55
    WITH RECURSIVE chain AS (
56
        SELECT
57
            b.id AS id,
58
            b.parent_id AS parent_id,
59
            b.state_hash AS state_hash,
60
            b.height AS height,
61
            b.global_slot_since_genesis AS global_slot_since_genesis
62
        FROM blocks b
63
        WHERE b.state_hash = $1
64

65
        UNION ALL
66

67
        SELECT
68
            p.id,
69
            p.parent_id,
70
            p.state_hash,
71
            p.height,
72
            p.global_slot_since_genesis
73
        FROM blocks p
74
        JOIN chain c ON p.id = c.parent_id AND %{join_condition}
75
        WHERE p.parent_id IS NOT NULL
76
    )
77
  |}
78

79
let chain_of_query = chain_of_query_templated ~join_condition:"TRUE"
80

81
let chain_of_query_until_inclusive =
82
  chain_of_query_templated ~join_condition:"c.state_hash <> $2"
83

84
let latest_state_hash (module Conn : CONNECTION) =
85
  let query =
1✔
86
    Caqti_type.(unit ->! string)
1✔
87
      {%string|
88
        SELECT state_hash from blocks order by height desc limit 1;
89
      |}
90
  in
91
  Conn.find query ()
1✔
92

93
(* Returns the first block of a specific protocol version.
94
   NOTE: There exists some emergency HF that doesn't bump up protocol version. *)
95
let first_block_of_protocol_version (module Conn : CONNECTION)
96
    ~(v : Protocol_version.t) =
NEW
97
  let query =
×
NEW
98
    (Protocol_version.typ ->? Block_info.typ)
×
99
      {%string|
100
        SELECT blocks.id, height, state_hash, protocol_versions.network, protocol_versions.transaction, protocol_versions.patch
101
        FROM blocks INNER JOIN protocol_versions
102
          ON blocks.protocol_version_id = protocol_versions.id
103
        WHERE protocol_versions.transaction = $1::int
104
          AND protocol_versions.network = $2::int
105
          AND protocol_versions.patch = $3::int
106
          AND global_slot_since_hard_fork = 0
107
        ORDER BY id ASC
108
        LIMIT 1;
109
      |}
110
  in
NEW
111
  Conn.find_opt query v
×
112

113
let block_info_by_state_hash (module Conn : CONNECTION) ~state_hash =
NEW
114
  let query =
×
NEW
115
    Caqti_type.(string ->? Block_info.typ)
×
116
      {%string|
117
        SELECT blocks.id, height, state_hash, protocol_versions.network, protocol_versions.transaction, protocol_versions.patch
118
        FROM blocks INNER JOIN protocol_versions
119
          ON blocks.protocol_version_id = protocol_versions.id
120
        WHERE state_hash = ?
121
        LIMIT 1;
122
      |}
123
  in
NEW
124
  Conn.find_opt query state_hash
×
125

126
let mark_pending_blocks_as_canonical_or_orphaned (module Conn : CONNECTION)
127
    ~canonical_block_ids ~stop_at_slot =
NEW
128
  let mutation =
×
NEW
129
    Caqti_type.(t2 (option int) Mina_caqti.array_int_typ ->. Caqti_type.unit)
×
130
      {%string|
131
        UPDATE blocks
132
        SET chain_status = CASE
133
            WHEN id = ANY($2::int[]) THEN 'canonical'::chain_status_type
134
            ELSE 'orphaned'::chain_status_type
135
        END
136
        WHERE chain_status = 'pending'::chain_status_type
137
          AND ($1 IS NULL OR $1::int <= global_slot_since_genesis);
138
      |}
139
  in
NEW
140
  Conn.exec mutation (stop_at_slot, Array.of_list canonical_block_ids)
×
141

142
let blocks_between_both_inclusive (module Conn : CONNECTION) ~latest_block_id
143
    ~oldest_block_id : (Block_info.t list, Caqti_error.t) Deferred.Result.t =
NEW
144
  let query =
×
NEW
145
    Caqti_type.(t2 int int ->* Block_info.typ)
×
146
      {%string|
147
        %{chain_of_query_until_inclusive}
148
        SELECT chain.id, height, state_hash, protocol_versions.network, protocol_versions.transaction, protocol_versions.patch
149
        FROM chain INNER JOIN protocol_versions
150
          ON chain.protocol_version_id = protocol_versions.id
151
        ORDER BY height ASC
152
      |}
153
  in
NEW
154
  Conn.collect_list query (latest_block_id, oldest_block_id)
×
155

156
let is_in_best_chain (module Conn : CONNECTION) ~tip_hash ~check_hash
157
    ~check_height ~check_slot =
158
  let query =
1✔
159
    Caqti_type.(t4 string string int int64 ->! bool)
1✔
160
      {%string|
161
        %{chain_of_query}
162
        SELECT EXISTS (
163
          SELECT 1 FROM chain
164
          WHERE state_hash = $2
165
            AND height = $3
166
            AND global_slot_since_genesis = $4
167
        );
168
      |}
169
  in
170
  Conn.find query (tip_hash, check_hash, check_height, check_slot)
1✔
171

172
let num_of_confirmations (module Conn : CONNECTION) ~latest_state_hash
173
    ~fork_slot =
174
  let query =
1✔
175
    Caqti_type.(t2 string int ->! int)
1✔
176
      {%string|
177
        %{chain_of_query}
178
        SELECT COUNT(*) FROM chain 
179
        WHERE global_slot_since_genesis >= $2;
180
      |}
181
  in
182
  Conn.find query (latest_state_hash, fork_slot)
1✔
183

184
let number_of_commands_since_block_query block_commands_table =
185
  Caqti_type.(t2 string int ->! t4 string int int int)
3✔
186
    {%string|
187
      %{chain_of_query}
188
      SELECT 
189
          state_hash,
190
          height,
191
          global_slot_since_genesis,
192
          COUNT(bc.block_id) AS command_count
193
      FROM chain
194
      LEFT JOIN %{block_commands_table} bc 
195
          ON chain.id = bc.block_id
196
      WHERE global_slot_since_genesis >= $2
197
      GROUP BY 
198
          state_hash,
199
          height,
200
          global_slot_since_genesis;
201
    |}
202

203
let number_of_user_commands_since_block (module Conn : CONNECTION)
204
    ~fork_state_hash ~fork_slot =
205
  Conn.find
1✔
206
    (number_of_commands_since_block_query "blocks_user_commands")
1✔
207
    (fork_state_hash, fork_slot)
208

209
let number_of_internal_commands_since_block (module Conn : CONNECTION)
210
    ~fork_state_hash ~fork_slot =
211
  Conn.find
1✔
212
    (number_of_commands_since_block_query "blocks_internal_commands")
1✔
213
    (fork_state_hash, fork_slot)
214

215
let number_of_zkapps_commands_since_block (module Conn : CONNECTION)
216
    ~fork_state_hash ~fork_slot =
217
  Conn.find
1✔
218
    (number_of_commands_since_block_query "blocks_zkapp_commands")
1✔
219
    (fork_state_hash, fork_slot)
220

221
let last_fork_block (module Conn : CONNECTION) =
222
  let query =
1✔
223
    Caqti_type.(unit ->! t2 string int64)
1✔
224
      {%string|
225
        SELECT state_hash, global_slot_since_genesis FROM blocks
226
        WHERE global_slot_since_hard_fork = 0
227
        ORDER BY height DESC
228
        LIMIT 1;
229
      |}
230
  in
231
  Conn.find query ()
1✔
232

233
let fetch_latest_migration_history (module Conn : CONNECTION) =
NEW
234
  let query =
×
NEW
235
    Caqti_type.(unit ->? t3 string string string)
×
236
      {%string|
237
        SELECT
238
          status, protocol_version, migration_version
239
        FROM migration_history
240
        ORDER BY commit_start_at DESC
241
        LIMIT 1;
242
      |}
243
  in
NEW
244
  Conn.find_opt query ()
×
245

246
(* Fetches last filled block before stop transaction slot.
247

248
   Every block in mina should have internal commands since system transactions (like coinbase, fee transfer etc)
249
   are implemented as internal commands. It CAN have zero user commands and zero zkapp commands,
250
   but it should have internal commands.
251

252
   However, in context of hard fork, we want to stop including any transactions
253
   in the blocks after specified slot (called stop transaction slot). No internal, user or zkapp commands should be included in the blocks after that slot.
254
   Blocks can still be produced with no transactions, to keep chain progressing and give us confirmations but
255
   only from stop transaction slot till stop network slot, where we completely stop the chain.
256
   Knowing above we can detect last filled block by only looking at internal transactions occurrence.
257
   Therefore our fork candidate is the block with highest height that has internal transaction included in it.
258
*)
259

260
let fetch_last_filled_block (module Conn : CONNECTION) =
NEW
261
  let query =
×
NEW
262
    Caqti_type.(unit ->! t3 string int64 int)
×
263
      {%string|
264
        SELECT b.state_hash, b.global_slot_since_genesis, b.height
265
        FROM blocks b
266
        INNER JOIN blocks_internal_commands bic ON b.id = bic.block_id
267
        ORDER BY b.height DESC
268
        LIMIT 1;
269
      |}
270
  in
NEW
271
  Conn.find query ()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc