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

MinaProtocol / mina / 242

04 Jun 2025 08:45PM UTC coverage: 36.779% (-19.8%) from 56.614%
242

push

buildkite

web-flow
Merge pull request #17086 from MinaProtocol/dkijania/port_terraform_removal

port terraform removal to develop

26262 of 71405 relevant lines covered (36.78%)

34615.67 hits per line

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

3.08
/src/lib/transition_handler/validator.ml
1
open Async_kernel
8✔
2
open Core_kernel
3
open Pipe_lib.Strict_pipe
4
open Mina_base
5
open Mina_state
6
open Mina_block
7
open Network_peer
8

9
module type CONTEXT = sig
10
  val logger : Logger.t
11

12
  val precomputed_values : Precomputed_values.t
13

14
  val constraint_constants : Genesis_constants.Constraint_constants.t
15

16
  val consensus_constants : Consensus.Constants.t
17
end
18

19
let validate_header_is_relevant ~context:(module Context : CONTEXT) ~frontier
20
    ~slot_chain_end header_with_hash =
21
  let open Result.Let_syntax in
×
22
  let module Context = struct
23
    include Context
24

25
    let logger =
26
      Logger.extend logger
×
27
        [ ("selection_context", `String "Transition_handler.Validator") ]
28
  end in
29
  let transition_hash =
30
    State_hash.With_state_hashes.state_hash header_with_hash
31
  in
32
  [%log' internal Context.logger] "Validate_transition" ;
×
33
  let get_consensus_constants h =
×
34
    Mina_block.Header.protocol_state h |> Protocol_state.consensus_state
×
35
  in
36
  let header = With_hash.data header_with_hash in
37
  let block_slot =
×
38
    Consensus.Data.Consensus_state.curr_global_slot
39
    @@ Protocol_state.consensus_state
×
40
    @@ Header.protocol_state header
×
41
  in
42
  let%bind () =
43
    match slot_chain_end with
44
    | Some slot_chain_end
×
45
      when Mina_numbers.Global_slot_since_hard_fork.(
46
             block_slot >= slot_chain_end) ->
47
        [%log' internal Context.logger] "Block after slot_chain_end, rejecting" ;
×
48
        Result.fail `Block_after_after_stop_slot
×
49
    | None | Some _ ->
×
50
        Result.return ()
×
51
  in
52
  let blockchain_length = Mina_block.Header.blockchain_length header in
×
53
  let root_breadcrumb = Transition_frontier.root frontier in
×
54
  [%log' internal Context.logger] "@block_metadata"
×
55
    ~metadata:
56
      [ ("blockchain_length", Mina_numbers.Length.to_yojson blockchain_length) ] ;
×
57
  [%log' internal Context.logger] "Check_transition_not_in_frontier" ;
×
58
  let open Result.Let_syntax in
×
59
  let%bind () =
60
    Option.fold
×
61
      (Transition_frontier.find frontier transition_hash)
×
62
      ~init:Result.(Ok ())
63
      ~f:(fun _ _ -> Result.Error (`In_frontier transition_hash))
×
64
  in
65
  [%log' internal Context.logger] "Check_transition_can_be_connected" ;
×
66
  Result.ok_if_true
×
67
    (Consensus.Hooks.equal_select_status `Take
×
68
       (Consensus.Hooks.select
69
          ~context:(module Context)
70
          ~existing:
71
            (Transition_frontier.Breadcrumb.consensus_state_with_hashes
×
72
               root_breadcrumb )
73
          ~candidate:(With_hash.map ~f:get_consensus_constants header_with_hash) ) )
×
74
    ~error:`Disconnected
75

76
let validate_transition_is_relevant ~context:(module Context : CONTEXT)
77
    ~frontier ~unprocessed_transition_cache ~slot_tx_end ~slot_chain_end
78
    enveloped_transition =
79
  let open Result.Let_syntax in
×
80
  [%log' internal Context.logger] "Validate_transition" ;
×
81
  let transition =
×
82
    Envelope.Incoming.data enveloped_transition
×
83
    |> Mina_block.Validation.block_with_hash
84
  in
85
  let transition_data = With_hash.data transition in
×
86
  let block_slot =
×
87
    Consensus.Data.Consensus_state.curr_global_slot
88
    @@ Protocol_state.consensus_state @@ Header.protocol_state
×
89
    @@ Mina_block.header transition_data
×
90
  in
91
  let%bind () =
92
    match slot_tx_end with
93
    | Some slot_tx_end
×
94
      when Mina_numbers.Global_slot_since_hard_fork.(block_slot >= slot_tx_end)
95
      ->
96
        [%log' internal Context.logger]
×
97
          "Block after slot_tx_end, validating it is empty" ;
98
        let staged_ledger_diff =
×
99
          Body.staged_ledger_diff @@ body transition_data
×
100
        in
101
        Result.ok_if_true
×
102
          (Staged_ledger_diff.is_empty staged_ledger_diff)
×
103
          ~error:`Non_empty_staged_ledger_diff_after_stop_slot
104
    | None | Some _ ->
×
105
        Result.(Ok ())
106
  in
107
  [%log' internal Context.logger] "Check_transition_not_in_process" ;
×
108
  let%bind () =
109
    Option.fold
×
110
      (Unprocessed_transition_cache.final_state unprocessed_transition_cache
×
111
         enveloped_transition )
112
      ~init:Result.(Ok ())
113
      ~f:(fun _ final_state -> Result.Error (`In_process final_state))
×
114
  in
115
  let header_with_hash = With_hash.map ~f:Mina_block.header transition in
×
116
  let%map () =
117
    validate_header_is_relevant
×
118
      ~context:(module Context)
119
      ~frontier ~slot_chain_end header_with_hash
120
  in
121
  [%log' internal Context.logger] "Register_transition_for_processing" ;
×
122
  (* we expect this to be Ok since we just checked the cache *)
123
  Unprocessed_transition_cache.register_exn unprocessed_transition_cache
×
124
    enveloped_transition
125

126
let validate_transition_or_header_is_relevant
127
    ~context:(module Context : CONTEXT) ~slot_tx_end ~slot_chain_end ~frontier
128
    ~unprocessed_transition_cache b_or_h =
129
  match b_or_h with
×
130
  | `Block b ->
×
131
      Result.map ~f:(fun cached_block -> `Block cached_block)
×
132
      @@ validate_transition_is_relevant
×
133
           ~context:(module Context)
134
           ~slot_tx_end ~slot_chain_end ~frontier ~unprocessed_transition_cache
135
           b
136
  | `Header h ->
×
137
      let header_with_hash, _ = Envelope.Incoming.data h in
138
      Result.map ~f:(fun () -> `Header h)
×
139
      @@ validate_header_is_relevant
×
140
           ~context:(module Context)
141
           ~slot_chain_end ~frontier header_with_hash
142

143
let run ~context:(module Context : CONTEXT) ~trust_system ~time_controller
144
    ~frontier ~transition_reader ~valid_transition_writer
145
    ~unprocessed_transition_cache =
146
  let open Context in
6✔
147
  let module Lru = Core_extended_cache.Lru in
148
  let outdated_root_cache = Lru.create ~destruct:None 1000 in
149
  O1trace.background_thread "validate_blocks_against_frontier" (fun () ->
6✔
150
      Reader.iter transition_reader ~f:(fun (b_or_h, `Valid_cb vc) ->
6✔
151
          let header_with_hash, sender =
×
152
            match b_or_h with
153
            | `Block b ->
×
154
                let block_with_hash, _ = Envelope.Incoming.data b in
155
                ( With_hash.map ~f:Mina_block.header block_with_hash
×
156
                , Envelope.Incoming.sender b )
×
157
            | `Header h ->
×
158
                let header_with_hash, _ = Envelope.Incoming.data h in
159
                (header_with_hash, Envelope.Incoming.sender h)
×
160
          in
161
          let header = With_hash.data header_with_hash in
162
          let transition_hash =
×
163
            State_hash.With_state_hashes.state_hash header_with_hash
164
          in
165
          Internal_tracing.Context_call.with_call_id
×
166
            ~tag:"transition_handler_validator"
167
          @@ fun () ->
168
          Internal_tracing.with_state_hash transition_hash
×
169
          @@ fun () ->
170
          let slot_tx_end =
×
171
            Runtime_config.slot_tx_end
172
              precomputed_values.Precomputed_values.runtime_config
173
          in
174
          let slot_chain_end =
×
175
            Runtime_config.slot_chain_end precomputed_values.runtime_config
176
          in
177
          match
×
178
            validate_transition_or_header_is_relevant
179
              ~context:(module Context)
180
              ~frontier ~unprocessed_transition_cache ~slot_tx_end
181
              ~slot_chain_end b_or_h
182
          with
183
          | Ok b_or_h' ->
×
184
              let%map () =
185
                Trust_system.record_envelope_sender trust_system logger sender
×
186
                  ( Trust_system.Actions.Sent_useful_gossip
187
                  , Some
188
                      ( "external transition $state_hash"
189
                      , [ ("state_hash", State_hash.to_yojson transition_hash)
×
190
                        ; ("header", Mina_block.Header.to_yojson header)
×
191
                        ] ) )
192
              in
193
              let transition_time =
×
194
                Mina_block.Header.protocol_state header
×
195
                |> Protocol_state.blockchain_state |> Blockchain_state.timestamp
×
196
                |> Block_time.to_time_exn
197
              in
198
              Perf_histograms.add_span
×
199
                ~name:"accepted_transition_remote_latency"
200
                (Core_kernel.Time.diff
×
201
                   Block_time.(now time_controller |> to_time_exn)
×
202
                   transition_time ) ;
203
              [%log internal] "Validate_transition_done" ;
×
204
              Writer.write valid_transition_writer (b_or_h', `Valid_cb vc)
×
205
          | Error (`In_frontier _) | Error (`In_process _) ->
×
206
              [%log internal] "Failure"
×
207
                ~metadata:[ ("reason", `String "In_frontier or In_process") ] ;
208
              Trust_system.record_envelope_sender trust_system logger sender
×
209
                ( Trust_system.Actions.Sent_old_gossip
210
                , Some
211
                    ( "external transition with state hash $state_hash"
212
                    , [ ("state_hash", State_hash.to_yojson transition_hash)
×
213
                      ; ("header", Mina_block.Header.to_yojson header)
×
214
                      ] ) )
215
          | Error `Disconnected ->
×
216
              [%log internal] "Failure"
×
217
                ~metadata:[ ("reason", `String "Disconnected") ] ;
218
              Mina_metrics.(Counter.inc_one Rejected_blocks.worse_than_root) ;
×
219
              let protocol_state = Mina_block.Header.protocol_state header in
220
              [%log error]
×
221
                ~metadata:
222
                  [ ("state_hash", State_hash.to_yojson transition_hash)
×
223
                  ; ("reason", `String "not selected over current root")
224
                  ; ( "protocol_state"
225
                    , Mina_block.Header.protocol_state header
×
226
                      |> Protocol_state.value_to_yojson )
×
227
                  ]
228
                "Validation error: external transition with state hash \
229
                 $state_hash was rejected for reason $reason" ;
230
              let is_in_root_history =
×
231
                let open Transition_frontier.Extensions in
232
                get_extension
×
233
                  (Transition_frontier.extensions frontier)
×
234
                  Root_history
235
                |> Root_history.mem
×
236
              in
237
              let parent_hash =
238
                Protocol_state.previous_state_hash protocol_state
239
              in
240
              let action =
×
241
                if
242
                  is_in_root_history transition_hash
×
243
                  || Option.is_some
×
244
                       (Lru.find outdated_root_cache transition_hash)
×
245
                then Trust_system.Actions.Sent_old_gossip
×
246
                else if
×
247
                  is_in_root_history parent_hash
×
248
                  || Option.is_some (Lru.find outdated_root_cache parent_hash)
×
249
                then (
×
250
                  Lru.add outdated_root_cache ~key:transition_hash ~data:() ;
251
                  Sent_useless_gossip )
×
252
                else Disconnected_chain
×
253
              in
254
              Trust_system.record_envelope_sender trust_system logger sender
255
                ( action
256
                , Some
257
                    ( "received transition that was not connected to our chain \
258
                       from $sender"
259
                    , [ ("sender", Envelope.Sender.to_yojson sender)
×
260
                      ; ("header", Mina_block.Header.to_yojson header)
×
261
                      ] ) )
262
          | Error `Non_empty_staged_ledger_diff_after_stop_slot ->
×
263
              [%log error]
×
264
                ~metadata:
265
                  [ ("state_hash", State_hash.to_yojson transition_hash)
×
266
                  ; ( "reason"
267
                    , `String "not empty staged ledger diff after slot_tx_end"
268
                    )
269
                  ; ( "block_slot"
270
                    , Mina_numbers.Global_slot_since_hard_fork.to_yojson
×
271
                      @@ Consensus.Data.Consensus_state.curr_global_slot
×
272
                      @@ Protocol_state.consensus_state
×
273
                      @@ Header.protocol_state header )
×
274
                  ]
275
                "Validation error: external transition with state hash \
276
                 $state_hash was rejected for reason $reason" ;
277
              Deferred.unit
×
278
          | Error `Block_after_after_stop_slot ->
×
279
              [%log error]
×
280
                ~metadata:
281
                  [ ("state_hash", State_hash.to_yojson transition_hash)
×
282
                  ; ("reason", `String "block after slot_chain_end")
283
                  ; ( "block_slot"
284
                    , Mina_numbers.Global_slot_since_hard_fork.to_yojson
×
285
                      @@ Consensus.Data.Consensus_state.curr_global_slot
×
286
                      @@ Protocol_state.consensus_state
×
287
                      @@ Header.protocol_state header )
×
288
                  ]
289
                "Validation error: external transition with state hash \
290
                 $state_hash was rejected for reason $reason" ;
291
              Deferred.unit ) )
×
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