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

MinaProtocol / mina / 2890

12 Nov 2024 12:30PM UTC coverage: 37.632% (-23.2%) from 60.813%
2890

push

buildkite

web-flow
Merge pull request #16333 from MinaProtocol/dkijania/port_new_deb_s3_dev

[DEV] Use new version of deb-s3 for validating job publishing

14488 of 38499 relevant lines covered (37.63%)

35927.1 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
2✔
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.compare Staged_ledger_diff.empty_diff
×
103
              staged_ledger_diff
104
          = 0 )
105
          ~error:`Non_empty_staged_ledger_diff_after_stop_slot
106
    | None | Some _ ->
×
107
        Result.(Ok ())
108
  in
109
  [%log' internal Context.logger] "Check_transition_not_in_process" ;
×
110
  let%bind () =
111
    Option.fold
×
112
      (Unprocessed_transition_cache.final_state unprocessed_transition_cache
×
113
         enveloped_transition )
114
      ~init:Result.(Ok ())
115
      ~f:(fun _ final_state -> Result.Error (`In_process final_state))
×
116
  in
117
  let header_with_hash = With_hash.map ~f:Mina_block.header transition in
×
118
  let%map () =
119
    validate_header_is_relevant
×
120
      ~context:(module Context)
121
      ~frontier ~slot_chain_end header_with_hash
122
  in
123
  [%log' internal Context.logger] "Register_transition_for_processing" ;
×
124
  (* we expect this to be Ok since we just checked the cache *)
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