https://gitlab.com/tezos/tezos
Tip revision: e067f64fa843bfd7b83291e70a3444e8e1b8dac0 authored by Ole Krüger on 06 September 2022, 12:29:57 UTC
WIP: Check tick counter
WIP: Check tick counter
Tip revision: e067f64
block_directory.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* Copyright (c) 2019-2021 Nomadic Labs, <contact@nomadic-labs.com> *)
(* *)
(* Permission is hereby granted, free of charge, to any person obtaining a *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
(* and/or sell copies of the Software, and to permit persons to whom the *)
(* Software is furnished to do so, subject to the following conditions: *)
(* *)
(* The above copyright notice and this permission notice shall be included *)
(* in all copies or substantial portions of the Software. *)
(* *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
(* DEALINGS IN THE SOFTWARE. *)
(* *)
(*****************************************************************************)
module Proof = Tezos_context_sigs.Context.Proof_types
let read_partial_context =
let open Lwt_syntax in
let init = Proof.Dir String.Map.empty in
fun context path depth ->
if depth = 0 then Lwt.return Proof.Cut
else
(* According to the documentation of Context.fold,
"[f] is never called with an empty key for values; i.e.,
folding over a value is a no-op".
Therefore, we first need to check that whether its a value.
*)
let* o = Context_ops.find context path in
match o with
| Some v -> Lwt.return (Proof.Key v)
| None ->
(* try to read as directory *)
Context_ops.fold_value
~depth:(`Le depth)
context
path
~order:`Sorted
~init
~f:(fun k lazy_value acc ->
let open Block_services in
if List.compare_length_with k depth >= 0 then
(* only [=] case is possible because [~depth] is [(`Le depth)] *)
Lwt.return (raw_context_insert (k, Cut) acc)
else
let+ o = lazy_value () in
match o with
| None -> acc
| Some v -> raw_context_insert (k, Key v) acc)
let build_raw_header_rpc_directory (module Proto : Block_services.PROTO) =
let open Lwt_result_syntax in
let dir :
(Store.chain_store * Block_hash.t * Block_header.t) RPC_directory.t ref =
ref RPC_directory.empty
in
let register0 s f =
dir :=
RPC_directory.register !dir (RPC_service.subst0 s) (fun block p q ->
f block p q)
in
let module Block_services = Block_services.Make (Proto) (Proto) in
let module S = Block_services.S in
register0 S.hash (fun (_, hash, _) () () -> return hash) ;
(* block header *)
register0 S.header (fun (chain_store, hash, header) () () ->
let protocol_data =
Data_encoding.Binary.of_bytes_exn
Proto.block_header_data_encoding
header.protocol_data
in
return
{
Block_services.hash;
chain_id = Store.Chain.chain_id chain_store;
shell = header.shell;
protocol_data;
}) ;
register0 S.raw_header (fun (_, _, header) () () ->
return (Data_encoding.Binary.to_bytes_exn Block_header.encoding header)) ;
register0 S.Header.shell_header (fun (_, _, header) () () ->
return header.shell) ;
register0 S.Header.protocol_data (fun (_, _, header) () () ->
return
(Data_encoding.Binary.of_bytes_exn
Proto.block_header_data_encoding
header.protocol_data)) ;
register0 S.Header.raw_protocol_data (fun (_, _, header) () () ->
return header.protocol_data) ;
(* helpers *)
register0 S.Helpers.Forge.block_header (fun _block () header ->
return (Data_encoding.Binary.to_bytes_exn Block_header.encoding header)) ;
(* protocols *)
register0 S.protocols (fun (chain_store, _hash, header) () () ->
let open Lwt_result_syntax in
let*! next_proto =
Store.Chain.find_protocol
chain_store
~protocol_level:header.shell.proto_level
in
let next_protocol_hash =
WithExceptions.Option.to_exn ~none:Not_found next_proto
in
let*! o =
Store.Block.read_block_opt chain_store header.shell.predecessor
in
match o with
| None ->
return
{
Tezos_shell_services.Block_services.current_protocol =
next_protocol_hash;
next_protocol = next_protocol_hash;
}
| Some pred_block ->
let pred_header = Store.Block.header pred_block in
let*! current_protocol =
Store.Chain.find_protocol
chain_store
~protocol_level:pred_header.shell.proto_level
in
let protocol_hash =
WithExceptions.Option.to_exn ~none:Not_found current_protocol
in
return
{
Tezos_shell_services.Block_services.current_protocol =
protocol_hash;
next_protocol = next_protocol_hash;
}) ;
!dir
(* This convertor aims to merge the behavior of the force_metadata
query string into the metadata one. We must remove it as soon as
the force_metadata query string is removed. *)
let with_metadata ~force_metadata ~metadata =
match (force_metadata, metadata) with
| true, _ | _, Some `Always -> Some `Always
| _, Some `Never -> Some `Never
| _, None -> None
let build_raw_rpc_directory (module Proto : Block_services.PROTO)
(module Next_proto : Registered_protocol.T) =
let open Lwt_result_syntax in
let dir : (Store.chain_store * Store.Block.block) RPC_directory.t ref =
ref RPC_directory.empty
in
let merge d = dir := RPC_directory.merge d !dir in
let register0 s f =
dir :=
RPC_directory.register !dir (RPC_service.subst0 s) (fun block p q ->
f block p q)
in
let register1 s f =
dir :=
RPC_directory.register !dir (RPC_service.subst1 s) (fun (block, a) p q ->
f block a p q)
in
let register2 s f =
dir :=
RPC_directory.register
!dir
(RPC_service.subst2 s)
(fun ((block, a), b) p q -> f block a b p q)
in
let module Block_services = Block_services.Make (Proto) (Next_proto) in
let module S = Block_services.S in
register0 S.live_blocks (fun (chain_store, block) () () ->
let* live_blocks, _ =
Store.Chain.compute_live_blocks chain_store ~block
in
return live_blocks) ;
(* block metadata *)
let block_metadata chain_store block =
let* metadata = Store.Block.get_block_metadata chain_store block in
let protocol_data =
Data_encoding.Binary.of_bytes_exn
Proto.block_header_metadata_encoding
(Store.Block.block_metadata metadata)
in
let* test_chain_status, _ =
Store.Block.testchain_status chain_store block
in
let max_operations_ttl = Store.Block.max_operations_ttl metadata in
return
{
Block_services.protocol_data;
test_chain_status;
max_operations_ttl;
max_operation_data_length = Next_proto.max_operation_data_length;
max_block_header_length = Next_proto.max_block_length;
operation_list_quota =
List.map
(fun {Tezos_protocol_environment.max_size; max_op} ->
{Tezos_shell_services.Block_services.max_size; max_op})
Next_proto.validation_passes;
}
in
register0 S.metadata (fun (chain_store, block) () () ->
block_metadata chain_store block) ;
let fail_opt = function None -> Lwt.fail Not_found | Some v -> return v in
register0 S.metadata_hash (fun (_, block) () () ->
fail_opt (Store.Block.block_metadata_hash block)) ;
(* operations *)
let convert_with_metadata chain_id (op : Operation.t) metadata :
Block_services.operation =
let protocol_data =
Data_encoding.Binary.of_bytes_exn Proto.operation_data_encoding op.proto
in
let receipt =
match metadata with
| Block_validation.Metadata bytes ->
Block_services.Receipt
(Data_encoding.Binary.of_bytes_exn
Proto.operation_receipt_encoding
bytes)
| Too_large_metadata -> Too_large
in
{
Block_services.chain_id;
hash = Operation.hash op;
shell = op.shell;
protocol_data;
receipt;
}
in
let convert_without_metadata chain_id (op : Operation.t) =
let protocol_data =
Data_encoding.Binary.of_bytes_exn Proto.operation_data_encoding op.proto
in
{
Block_services.chain_id;
hash = Operation.hash op;
shell = op.shell;
protocol_data;
receipt = Empty;
}
in
let operations_without_metadata chain_store block =
let chain_id = Store.Chain.chain_id chain_store in
let ops = Store.Block.operations block in
return (List.map (List.map (convert_without_metadata chain_id)) ops)
in
let operations chain_store block =
let chain_id = Store.Chain.chain_id chain_store in
let ops = Store.Block.operations block in
let*! o = Store.Block.get_block_metadata_opt chain_store block in
match o with
| None ->
return (List.map (List.map (convert_without_metadata chain_id)) ops)
| Some metadata ->
Lwt.catch
(fun () ->
let ops_metadata = Store.Block.operations_metadata metadata in
List.map2_e
~when_different_lengths:()
(List.map2
~when_different_lengths:()
(convert_with_metadata chain_id))
ops
ops_metadata
|> function
| Ok v -> return v
| Error () -> raise Not_found)
(fun _ ->
return (List.map (List.map (convert_without_metadata chain_id)) ops))
in
let force_operation_metadata chain_id chain_store block =
let block_header = Store.Block.header block in
let operations = Store.Block.operations block in
let* predecessor_block =
Store.Block.read_block chain_store (Store.Block.predecessor block)
in
let predecessor_header = Store.Block.header predecessor_block in
let* context = Store.Block.context chain_store predecessor_block in
let* predecessor_context =
let*! ctxt =
Context_ops.checkout
(Context_ops.index context)
(Store.Block.context_hash predecessor_block)
in
match ctxt with Some c -> return c | None -> fail_with_exn Not_found
in
let predecessor_block_metadata_hash =
Store.Block.block_metadata_hash predecessor_block
in
let predecessor_ops_metadata_hash =
Store.Block.all_operations_metadata_hash predecessor_block
in
let* _block_metadata, ops_metadata =
Block_validation.recompute_metadata
~chain_id
~predecessor_block_header:predecessor_header
~predecessor_context
~predecessor_block_metadata_hash
~predecessor_ops_metadata_hash
~block_header
~operations
~cache:`Lazy
in
let ops_metadata =
match ops_metadata with
| Block_validation.No_metadata_hash ops_metadata -> ops_metadata
| Block_validation.Metadata_hash ops_metadata ->
List.map (List.map fst) ops_metadata
in
return ops_metadata
in
(*****************************************************************)
register0 S.Operations.operations (fun (chain_store, block) q () ->
let with_metadata =
with_metadata ~force_metadata:q#force_metadata ~metadata:q#metadata
in
match with_metadata with
| Some `Always -> (
let chain_id = Store.Chain.chain_id chain_store in
let ops = Store.Block.operations block in
let* metadata = Store.Block.get_block_metadata chain_store block in
let ops_metadata = metadata.operations_metadata in
let* ops_metadata =
(* Iter through the operations metadata to check if some are
considered as too large. *)
if
List.exists
(fun v ->
List.exists
(fun v -> v = Block_validation.Too_large_metadata)
v)
ops_metadata
then
(* The metadatas are stored but contains some too large
metadata, we need te recompute them *)
force_operation_metadata chain_id chain_store block
else return ops_metadata
in
List.map2_e
~when_different_lengths:()
(List.map2
~when_different_lengths:()
(convert_with_metadata chain_id))
ops
ops_metadata
|> function
| Ok v -> return v
| Error () -> fail_with_exn Not_found)
| Some `Never -> operations_without_metadata chain_store block
| None -> operations chain_store block) ;
register1 S.Operations.operations_in_pass (fun (chain_store, block) i q () ->
let chain_id = Store.Chain.chain_id chain_store in
Lwt.catch
(fun () ->
let with_metadata =
with_metadata ~force_metadata:q#force_metadata ~metadata:q#metadata
in
match with_metadata with
| Some `Always -> (
let*! o = Store.Block.get_block_metadata_opt chain_store block in
let ops = fst @@ Store.Block.operations_path block i in
match o with
| None ->
return (List.map (convert_without_metadata chain_id) ops)
| Some metadata -> (
let opss_metadata =
Store.Block.operations_metadata metadata
in
let ops_metadata =
List.nth opss_metadata i
|> WithExceptions.Option.to_exn ~none:Not_found
in
let* ops_metadata =
(* Iter through the operations metadata of the
requested pass to check if some are considered as
too large. *)
if
List.exists
(fun v -> v = Block_validation.Too_large_metadata)
ops_metadata
then
let* opss_metadata =
force_operation_metadata chain_id chain_store block
in
let ops_metadata =
List.nth_opt opss_metadata i
|> WithExceptions.Option.to_exn ~none:Not_found
in
return ops_metadata
else return ops_metadata
in
List.map2
~when_different_lengths:()
(convert_with_metadata chain_id)
ops
ops_metadata
|> function
| Ok x -> return x
| _ -> fail_with_exn Not_found))
| Some `Never ->
let* ops = operations_without_metadata chain_store block in
return
(List.nth ops i |> WithExceptions.Option.to_exn ~none:Not_found)
| None ->
let* ops = operations chain_store block in
return
(List.nth ops i |> WithExceptions.Option.to_exn ~none:Not_found))
(fun _ -> fail_with_exn Not_found)) ;
register2 S.Operations.operation (fun (chain_store, block) i j q () ->
let chain_id = Store.Chain.chain_id chain_store in
Lwt.catch
(fun () ->
let ops = fst @@ Store.Block.operations_path block i in
let op =
List.nth ops j |> WithExceptions.Option.to_exn ~none:Not_found
in
let with_metadata =
with_metadata ~force_metadata:q#force_metadata ~metadata:q#metadata
in
match with_metadata with
| Some `Always -> (
let*! o = Store.Block.get_block_metadata_opt chain_store block in
match o with
| None -> return (convert_without_metadata chain_id op)
| Some metadata -> (
let opss_metadata =
Store.Block.operations_metadata metadata
in
let ops_metadata =
List.nth opss_metadata i
|> WithExceptions.Option.to_exn ~none:Not_found
in
let op_metadata =
List.nth ops_metadata j
|> WithExceptions.Option.to_exn ~none:Not_found
in
match op_metadata with
| Block_validation.Too_large_metadata ->
let* opss_metadata =
force_operation_metadata chain_id chain_store block
in
let ops_metadata =
List.nth_opt opss_metadata i
|> WithExceptions.Option.to_exn ~none:Not_found
in
let op_metadata =
List.nth ops_metadata j
|> WithExceptions.Option.to_exn ~none:Not_found
in
return ((convert_with_metadata chain_id) op op_metadata)
| Metadata _ ->
return (convert_with_metadata chain_id op op_metadata)))
| Some `Never ->
let* opss = operations_without_metadata chain_store block in
let ops =
List.nth opss i |> WithExceptions.Option.to_exn ~none:Not_found
in
let op =
List.nth ops j |> WithExceptions.Option.to_exn ~none:Not_found
in
return op
| None ->
let* opss = operations chain_store block in
let ops =
List.nth opss i |> WithExceptions.Option.to_exn ~none:Not_found
in
let op =
List.nth ops j |> WithExceptions.Option.to_exn ~none:Not_found
in
return op)
(fun _ -> fail_with_exn Not_found)) ;
(* operation_hashes *)
register0 S.Operation_hashes.operation_hashes (fun (_, block) () () ->
return (Store.Block.all_operation_hashes block)) ;
register1
S.Operation_hashes.operation_hashes_in_pass
(fun (_, block) i () () ->
return (Store.Block.operations_hashes_path block i |> fst)) ;
register2 S.Operation_hashes.operation_hash (fun (_, block) i j () () ->
Lwt.catch
(fun () ->
let ops, _ = Store.Block.operations_hashes_path block i in
return (List.nth ops j |> WithExceptions.Option.to_exn ~none:Not_found))
(fun _ -> Lwt.fail Not_found)) ;
(* operation_metadata_hashes *)
register0 S.Operation_metadata_hashes.root (fun (_, block) () () ->
fail_opt (Store.Block.all_operations_metadata_hash block)) ;
register0
S.Operation_metadata_hashes.operation_metadata_hashes
(fun (_, block) () () ->
fail_opt (Store.Block.operations_metadata_hashes block)) ;
register1
S.Operation_metadata_hashes.operation_metadata_hashes_in_pass
(fun (_, block) i () () ->
let* ops_metadata_hashes =
fail_opt (Store.Block.operations_metadata_hashes block)
in
fail_opt (List.nth_opt ops_metadata_hashes i)) ;
register2
S.Operation_metadata_hashes.operation_metadata_hash
(fun (_, block) i j () () ->
Lwt.catch
(fun () ->
let* hashes =
fail_opt (Store.Block.operations_metadata_hashes_path block i)
in
return
(List.nth hashes j |> WithExceptions.Option.to_exn ~none:Not_found))
(fun _ -> Lwt.fail Not_found)) ;
(* context *)
register1 S.Context.read (fun (chain_store, block) path q () ->
let depth = Option.value ~default:max_int q#depth in
(* [depth] is defined as a [uint] not an [int] *)
assert (depth >= 0) ;
let* context = Store.Block.context chain_store block in
let*! mem = Context_ops.mem context path in
let*! dir_mem = Context_ops.mem_tree context path in
if not (mem || dir_mem) then Lwt.fail Not_found
else
let*! v = read_partial_context context path depth in
Lwt.return_ok v) ;
register1 S.Context.merkle_tree (fun (chain_store, block) path query () ->
let*! o = Store.Block.context_opt chain_store block in
match o with
| None -> return None
| Some context ->
let holey = Option.value ~default:false query#holey in
let leaf_kind =
let open Proof in
if holey then Hole else Raw_context
in
let*! v = Context_ops.merkle_tree context leaf_kind path in
return_some v) ;
(* info *)
register0 S.info (fun (chain_store, block) q () ->
let chain_id = Store.Chain.chain_id chain_store in
let hash = Store.Block.hash block in
let header = Store.Block.header block in
let shell = header.shell in
let protocol_data =
Data_encoding.Binary.of_bytes_exn
Proto.block_header_data_encoding
header.protocol_data
in
let* metadata =
let*! metadata = block_metadata chain_store block in
return (Option.of_result metadata)
in
let* operations =
let with_metadata =
with_metadata ~force_metadata:q#force_metadata ~metadata:q#metadata
in
match with_metadata with
| Some `Always -> (
let ops = Store.Block.operations block in
let* metadata = Store.Block.get_block_metadata chain_store block in
let ops_metadata = metadata.operations_metadata in
let* ops_metadata =
(* Iter through the operations metadata to check if some are
considered as too large. *)
if
List.exists
(fun v ->
List.exists
(fun v -> v = Block_validation.Too_large_metadata)
v)
ops_metadata
then
(* The metadatas are stored but contains some too large
metadata, we need te recompute them *)
force_operation_metadata chain_id chain_store block
else return ops_metadata
in
List.map2_e
~when_different_lengths:()
(List.map2
~when_different_lengths:()
(convert_with_metadata chain_id))
ops
ops_metadata
|> function
| Ok v -> return v
| Error () -> fail_with_exn Not_found)
| Some `Never -> operations_without_metadata chain_store block
| None -> operations chain_store block
in
return
{
Block_services.hash;
chain_id;
header = {shell; protocol_data};
metadata;
operations;
}) ;
(* helpers *)
register0 S.Helpers.Preapply.block (fun (chain_store, block) q p ->
let timestamp =
match q#timestamp with
| None -> Time.System.to_protocol (Time.System.now ())
| Some time -> time
in
let protocol_data =
Data_encoding.Binary.to_bytes_exn
Next_proto.block_header_data_encoding
p.protocol_data
in
let operations =
List.map
(fun operations ->
let operations =
List.map
(fun op ->
let proto =
Data_encoding.Binary.to_bytes_exn
Next_proto.operation_data_encoding
op.Next_proto.protocol_data
in
(op, {Operation.shell = op.shell; proto}))
operations
in
let operations =
if q#sort_operations then
List.sort
(fun (op, ops) (op', ops') ->
let oph, oph' = (Operation.hash ops, Operation.hash ops') in
Next_proto.compare_operations (oph, op) (oph', op'))
operations
else operations
in
List.map snd operations)
p.operations
in
let* bv =
try return (Block_validator.running_worker ())
with _ -> failwith "Block validator is not running"
in
Block_validator.preapply
bv
chain_store
~predecessor:block
~timestamp
~protocol_data
operations) ;
register0 S.Helpers.Preapply.operations (fun (chain_store, block) () ops ->
let* ctxt = Store.Block.context chain_store block in
let predecessor = Store.Block.hash block in
let header = Store.Block.shell_header block in
let predecessor_context = ctxt in
let* state =
Next_proto.begin_construction
~chain_id:(Store.Chain.chain_id chain_store)
~predecessor_context
~predecessor_timestamp:header.timestamp
~predecessor_level:header.level
~predecessor_fitness:header.fitness
~predecessor
~timestamp:(Time.System.to_protocol (Time.System.now ()))
~cache:`Lazy
()
in
let* state, acc =
List.fold_left_es
(fun (state, acc) op ->
let* state, result = Next_proto.apply_operation state op in
return (state, (op.protocol_data, result) :: acc))
(state, [])
ops
in
(* A pre application must not commit into the protocol caches.
Hence, we set [cache_nonce] to None. *)
let* _ = Next_proto.finalize_block state None in
return (List.rev acc)) ;
register1 S.Helpers.complete (fun (chain_store, block) prefix () () ->
let* ctxt = Store.Block.context chain_store block in
let*! l1 = Base58.complete prefix in
let*! l2 = Next_proto.complete_b58prefix ctxt prefix in
return (l1 @ l2)) ;
(* merge protocol rpcs... *)
merge
(RPC_directory.map
(fun (chain_store, block) ->
let hash = Store.Block.hash block in
let header = Store.Block.header block in
Lwt.return (chain_store, hash, header))
(build_raw_header_rpc_directory (module Proto))) ;
let proto_services =
match Prevalidator_filters.find Next_proto.hash with
| Some (module Filters) -> Filters.RPC.rpc_services
| None -> Next_proto.rpc_services
in
merge
(RPC_directory.map
(fun (chain_store, block) ->
let*! r =
let*! context = Store.Block.context_exn chain_store block in
let predecessor_context = context in
let chain_id = Store.Chain.chain_id chain_store in
let Block_header.
{
timestamp = predecessor_timestamp;
level = predecessor_level;
fitness = predecessor_fitness;
_;
} =
Store.Block.shell_header block
in
(*
Reactivity is important when executing RPCs and there are
no constraints to be consistent with other nodes. For this
reason, the RPC directory loads the cache lazily.
See {!Environment_context.source_of_cache}.
*)
let predecessor = Store.Block.hash block in
let timestamp = Time.System.to_protocol (Time.System.now ()) in
let* value_of_key =
Next_proto.value_of_key
~chain_id
~predecessor_context
~predecessor_timestamp
~predecessor_level
~predecessor_fitness
~predecessor
~timestamp
in
let* context =
Tezos_protocol_environment.Context.load_cache
predecessor
predecessor_context
`Lazy
value_of_key
in
return
{
Tezos_protocol_environment.block_hash = Store.Block.hash block;
block_header = Store.Block.shell_header block;
context;
}
in
match r with
| Ok result -> Lwt.return result
| Error _ -> Lwt.fail Not_found)
proto_services) ;
!dir
let get_protocol hash =
match Registered_protocol.get hash with
| None -> raise Not_found
| Some protocol -> protocol
let get_directory chain_store block =
let open Lwt_syntax in
let* o = Store.Chain.get_rpc_directory chain_store block in
match o with
| Some dir -> Lwt.return dir
| None -> (
let* next_protocol_hash =
Store.Block.protocol_hash_exn chain_store block
in
let (module Next_proto) = get_protocol next_protocol_hash in
let build_fake_rpc_directory () =
build_raw_rpc_directory
(module Block_services.Fake_protocol)
(module Next_proto)
in
if Store.Block.is_genesis chain_store (Store.Block.hash block) then
Lwt.return (build_fake_rpc_directory ())
else
let* (module Proto) =
let* o = Store.Block.read_predecessor_opt chain_store block in
match o with
| None ->
(* No predecessors (e.g. pruned caboose), return the
current protocol *)
Lwt.return (module Next_proto : Registered_protocol.T)
| Some pred ->
let* _, savepoint_level = Store.Chain.savepoint chain_store in
let* protocol_hash =
if Compare.Int32.(Store.Block.level pred < savepoint_level) then
let* predecessor_protocol =
Store.Chain.find_protocol
chain_store
~protocol_level:(Store.Block.proto_level pred)
in
let protocol_hash =
WithExceptions.Option.to_exn
~none:Not_found
predecessor_protocol
in
Lwt.return protocol_hash
else Store.Block.protocol_hash_exn chain_store pred
in
Lwt.return (get_protocol protocol_hash)
in
let* o = Store.Chain.get_rpc_directory chain_store block in
match o with
| Some dir -> Lwt.return dir
| None ->
let dir =
build_raw_rpc_directory (module Proto) (module Next_proto)
in
let* () =
Store.Chain.set_rpc_directory
chain_store
~protocol_hash:Proto.hash
~next_protocol_hash:Next_proto.hash
dir
in
Lwt.return dir)
let build_rpc_directory chain_store block =
let open Lwt_syntax in
let* o = Store.Chain.block_of_identifier_opt chain_store block in
match o with
| None -> Lwt.fail Not_found
| Some b ->
let* dir = get_directory chain_store b in
Lwt.return (RPC_directory.map (fun _ -> Lwt.return (chain_store, b)) dir)