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

MinaProtocol / mina / 3311

08 Feb 2025 09:10PM UTC coverage: 36.017% (-24.8%) from 60.828%
3311

push

buildkite

web-flow
Merge pull request #16591 from MinaProtocol/feature/stop-unknown-stream_idx

Stop sending data on libp2p streams after the first error

7 of 14 new or added lines in 1 file covered. (50.0%)

16388 existing lines in 340 files now uncovered.

25649 of 71214 relevant lines covered (36.02%)

26723.4 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
5✔
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 =
UNCOV
21
  let open Result.Let_syntax in
×
22
  let module Context = struct
23
    include Context
24

25
    let compile_config = precomputed_values.compile_config
26

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

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

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

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