package octez-smart-rollup-node-lib

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file snapshot_utils.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
(*****************************************************************************)
(*                                                                           *)
(* SPDX-License-Identifier: MIT                                              *)
(* Copyright (c) 2023 Functori <contact@functori.com>                        *)
(*                                                                           *)
(*****************************************************************************)

module type READER = sig
  type in_channel

  val open_in : string -> in_channel

  val really_input : in_channel -> bytes -> int -> int -> unit

  val input : in_channel -> bytes -> int -> int -> int

  val close_in : in_channel -> unit
end

module type WRITER = sig
  type out_channel

  val open_out : string -> out_channel

  val output : out_channel -> bytes -> int -> int -> unit

  val flush_continue : out_channel -> unit

  val close_out : out_channel -> unit
end

module type READER_INPUT = sig
  include READER

  val in_chan : in_channel
end

module type WRITER_OUTPUT = sig
  include WRITER

  val out_chan : out_channel
end

module Stdlib_reader : READER with type in_channel = Stdlib.in_channel = Stdlib

module Stdlib_writer : WRITER with type out_channel = Stdlib.out_channel =
struct
  include Stdlib

  let flush_continue = flush
end

module Gzip_reader : READER with type in_channel = Gzip.in_channel = Gzip

module Gzip_writer : WRITER with type out_channel = Gzip.out_channel = struct
  include Gzip

  let open_out f = open_out f
end

type reader = (module READER)

type writer = (module WRITER)

let stdlib_reader : reader = (module Stdlib_reader)

let stdlib_writer : writer = (module Stdlib_writer)

let gzip_reader : reader = (module Gzip_reader)

let gzip_writer : writer = (module Gzip_writer)

type snapshot_version = V0

type snapshot_metadata = {
  history_mode : Configuration.history_mode;
  address : Address.t;
  head_level : int32;
  last_commitment : Commitment.Hash.t;
}

let snapshot_version_encoding =
  let open Data_encoding in
  conv_with_guard
    (function V0 -> 0)
    (function
      | 0 -> Ok V0 | x -> Error ("Invalid snapshot version " ^ string_of_int x))
    int8

let snaphsot_metadata_encoding =
  let open Data_encoding in
  conv
    (fun {history_mode; address; head_level; last_commitment} ->
      (history_mode, address, head_level, last_commitment))
    (fun (history_mode, address, head_level, last_commitment) ->
      {history_mode; address; head_level; last_commitment})
  @@ obj4
       (req "history_mode" Configuration.history_mode_encoding)
       (req "address" Address.encoding)
       (req "head_level" int32)
       (req "last_commitment" Commitment.Hash.encoding)

let snapshot_metadata_size =
  Data_encoding.Binary.fixed_length snaphsot_metadata_encoding
  |> WithExceptions.Option.get ~loc:__LOC__

let version = V0

let write_snapshot_metadata (module Writer : WRITER_OUTPUT) metadata =
  let version_bytes =
    Data_encoding.Binary.to_bytes_exn snapshot_version_encoding version
  in
  let metadata_bytes =
    Data_encoding.Binary.to_bytes_exn snaphsot_metadata_encoding metadata
  in
  Writer.output Writer.out_chan version_bytes 0 (Bytes.length version_bytes) ;
  Writer.output Writer.out_chan metadata_bytes 0 (Bytes.length metadata_bytes)

let read_snapshot_metadata (module Reader : READER_INPUT) =
  let version_bytes = Bytes.create 1 in
  let metadata_bytes = Bytes.create snapshot_metadata_size in
  Reader.really_input Reader.in_chan version_bytes 0 1 ;
  Reader.really_input Reader.in_chan metadata_bytes 0 snapshot_metadata_size ;
  let snapshot_version =
    Data_encoding.Binary.of_bytes_exn snapshot_version_encoding version_bytes
  in
  assert (snapshot_version = version) ;
  Data_encoding.Binary.of_bytes_exn snaphsot_metadata_encoding metadata_bytes

let list_files dir ~include_file f =
  let rec list_files_in_dir stream
      ((dir, relative_dir, dir_handle) as current_dir_info) =
    match Unix.readdir dir_handle with
    | "." | ".." -> list_files_in_dir stream current_dir_info
    | basename ->
        let full_path = Filename.concat dir basename in
        let relative_path = Filename.concat relative_dir basename in
        let stream =
          if Sys.is_directory full_path then
            let sub_dir_handle = Unix.opendir full_path in
            list_files_in_dir stream (full_path, relative_path, sub_dir_handle)
          else if include_file ~relative_path then
            Stream.icons (f ~full_path ~relative_path) stream
          else stream
        in
        list_files_in_dir stream current_dir_info
    | exception End_of_file ->
        Unix.closedir dir_handle ;
        stream
  in
  let dir_handle = Unix.opendir dir in
  list_files_in_dir Stream.sempty (dir, "", dir_handle)

let total_bytes_to_export dir ~include_file =
  let file_stream =
    list_files dir ~include_file @@ fun ~full_path ~relative_path:_ ->
    let {Unix.st_size; _} = Unix.lstat full_path in
    st_size
  in
  let total = ref 0 in
  Stream.iter (fun size -> total := !total + size) file_stream ;
  !total

let create (module Reader : READER) (module Writer : WRITER) metadata ~dir
    ~include_file ~dest =
  let module Archive_writer = Tar.Make (struct
    include Reader
    include Writer
  end) in
  let total = total_bytes_to_export dir ~include_file in
  let progress_bar =
    Progress_bar.progress_bar
      ~counter:`Bytes
      ~message:"Exporting snapshot  "
      ~color:(Terminal.Color.rgb 3 132 252)
      total
  in
  Progress_bar.with_reporter progress_bar @@ fun count_progress ->
  let write_file file (out_chan : Writer.out_channel) =
    let in_chan = Reader.open_in file in
    try
      let buffer_size = 64 * 1024 in
      let buf = Bytes.create buffer_size in
      let rec copy () =
        let read_bytes = Reader.input in_chan buf 0 buffer_size in
        Writer.output out_chan buf 0 read_bytes ;
        count_progress read_bytes ;
        if read_bytes > 0 then copy ()
      in
      copy () ;
      Writer.flush_continue out_chan ;
      Reader.close_in in_chan
    with e ->
      Reader.close_in in_chan ;
      raise e
  in
  let file_stream =
    list_files dir ~include_file @@ fun ~full_path ~relative_path ->
    let {Unix.st_perm; st_size; st_mtime; _} = Unix.lstat full_path in
    let header =
      Tar.Header.make
        ~file_mode:st_perm
        ~mod_time:(Int64.of_float st_mtime)
        relative_path
        (Int64.of_int st_size)
    in
    let writer = write_file full_path in
    (header, writer)
  in
  let out_chan = Writer.open_out dest in
  try
    write_snapshot_metadata
      (module struct
        include Writer

        let out_chan = out_chan
      end)
      metadata ;
    Archive_writer.Archive.create_gen file_stream out_chan ;
    Writer.close_out out_chan
  with e ->
    Writer.close_out out_chan ;
    raise e

let rec create_dir ?(perm = 0o755) dir =
  let stat =
    try Some (Unix.stat dir) with Unix.Unix_error (ENOENT, _, _) -> None
  in
  match stat with
  | Some {st_kind = S_DIR; _} -> ()
  | Some _ -> Stdlib.failwith "Not a directory"
  | None -> (
      create_dir ~perm (Filename.dirname dir) ;
      try Unix.mkdir dir perm
      with Unix.Unix_error (EEXIST, _, _) ->
        (* This is the case where the directory has been created at the same
           time. *)
        ())

let extract (module Reader : READER) (module Writer : WRITER) metadata_check
    ~snapshot_file ~dest =
  let module Archive_reader = Tar.Make (struct
    include Reader
    include Writer
  end) in
  let out_channel_of_header (header : Tar.Header.t) =
    let path = Filename.concat dest header.file_name in
    create_dir (Filename.dirname path) ;
    Writer.open_out path
  in
  let in_chan = Reader.open_in snapshot_file in
  try
    let metadata =
      read_snapshot_metadata
        (module struct
          include Reader

          let in_chan = in_chan
        end)
    in
    metadata_check metadata ;
    Archive_reader.Archive.extract_gen out_channel_of_header in_chan ;
    Reader.close_in in_chan
  with e ->
    Reader.close_in in_chan ;
    raise e

let compress ~snapshot_file =
  let Unix.{st_size = total; _} = Unix.stat snapshot_file in
  let progress_bar =
    Progress_bar.progress_bar
      ~counter:`Bytes
      ~message:"Compressing snapshot"
      ~color:(Terminal.Color.rgb 3 198 252)
      total
  in
  Progress_bar.with_reporter progress_bar @@ fun count_progress ->
  let snapshot_file_gz = Filename.chop_suffix snapshot_file ".uncompressed" in
  let in_chan = open_in snapshot_file in
  let out_chan = Gzip.open_out snapshot_file_gz in
  try
    let buffer_size = 64 * 1024 in
    let buf = Bytes.create buffer_size in
    let rec copy () =
      let read_bytes = input in_chan buf 0 buffer_size in
      Gzip.output out_chan buf 0 read_bytes ;
      count_progress read_bytes ;
      if read_bytes > 0 then copy ()
    in
    copy () ;
    Gzip.close_out out_chan ;
    close_in in_chan ;
    Unix.unlink snapshot_file ;
    snapshot_file_gz
  with e ->
    Gzip.close_out out_chan ;
    close_in in_chan ;
    raise e
OCaml

Innovation. Community. Security.