Shell: added rpc to inspect context of a block
Added a block_service and corresponding handler in the node to perform queries of the form '/blocks/<id>/raw_context/<path>?depth=<n>' returning the sub-tree corresponding to <path> inside the context of block <id>. The parameter <depth> controls the size of the tree, default is 1.
This commit is contained in:
parent
0a10b97e33
commit
d1c1ced970
@ -561,6 +561,34 @@ module RPC = struct
|
|||||||
let dir = RPC_directory.map (fun () -> Lwt.return rpc_context) Proto.rpc_services in
|
let dir = RPC_directory.map (fun () -> Lwt.return rpc_context) Proto.rpc_services in
|
||||||
Lwt.return (Some (RPC_directory.map (fun _ -> ()) dir))
|
Lwt.return (Some (RPC_directory.map (fun _ -> ()) dir))
|
||||||
|
|
||||||
|
let context_raw_get node block ~path ~depth =
|
||||||
|
let open Block_services in
|
||||||
|
(* negative depth could be handled by a more informative error *)
|
||||||
|
if depth < 0 then Lwt.return_none else
|
||||||
|
get_rpc_context node block >>= function
|
||||||
|
| None -> Lwt.return_none
|
||||||
|
| Some rpc_context ->
|
||||||
|
let rec loop path depth = (* non tail-recursive *)
|
||||||
|
if depth = 0 then Lwt.return Cut else
|
||||||
|
(* try to read as file *)
|
||||||
|
Context.get rpc_context.context path >>= function
|
||||||
|
| Some v -> Lwt.return (Key v)
|
||||||
|
| None -> (* try to read as directory *)
|
||||||
|
Context.fold rpc_context.context path ~init:[]
|
||||||
|
~f:(fun k acc ->
|
||||||
|
match k with
|
||||||
|
| `Key k | `Dir k ->
|
||||||
|
loop k (depth-1) >>= fun v ->
|
||||||
|
let k = List.nth k ((List.length k)-1) in
|
||||||
|
Lwt.return ((k,v)::acc)) >>= fun l ->
|
||||||
|
Lwt.return (Dir (List.rev l))
|
||||||
|
in
|
||||||
|
Context.mem rpc_context.context path >>= fun mem ->
|
||||||
|
Context.dir_mem rpc_context.context path >>= fun dir_mem ->
|
||||||
|
if mem || dir_mem then
|
||||||
|
loop path depth >>= Lwt.return_some
|
||||||
|
else Lwt.return_none
|
||||||
|
|
||||||
let heads node =
|
let heads node =
|
||||||
let chain_state = Chain_validator.chain_state node.mainchain_validator in
|
let chain_state = Chain_validator.chain_state node.mainchain_validator in
|
||||||
Chain.known_heads chain_state >>= fun heads ->
|
Chain.known_heads chain_state >>= fun heads ->
|
||||||
|
@ -112,6 +112,14 @@ module RPC : sig
|
|||||||
val context_dir:
|
val context_dir:
|
||||||
t -> block -> 'a RPC_directory.t option Lwt.t
|
t -> block -> 'a RPC_directory.t option Lwt.t
|
||||||
|
|
||||||
|
(** Returns the content of the context at the given [path] descending
|
||||||
|
recursively into directories as far as [depth] allows.
|
||||||
|
Returns [None] if a path in not in the context or if [depth] is
|
||||||
|
negative. *)
|
||||||
|
val context_raw_get:
|
||||||
|
t -> block -> path:string list -> depth:int ->
|
||||||
|
Block_services.raw_context_result option Lwt.t
|
||||||
|
|
||||||
val preapply:
|
val preapply:
|
||||||
t -> block ->
|
t -> block ->
|
||||||
timestamp:Time.t -> protocol_data:MBytes.t ->
|
timestamp:Time.t -> protocol_data:MBytes.t ->
|
||||||
|
@ -436,6 +436,13 @@ let build_rpc_directory node =
|
|||||||
let dir =
|
let dir =
|
||||||
RPC_directory.register2 dir Block_services.S.complete
|
RPC_directory.register2 dir Block_services.S.complete
|
||||||
(fun block s () () -> Node.RPC.complete node ~block s >>= return) in
|
(fun block s () () -> Node.RPC.complete node ~block s >>= return) in
|
||||||
|
let dir =
|
||||||
|
RPC_directory.register2 dir Block_services.S.raw_context
|
||||||
|
(fun block path q () ->
|
||||||
|
Node.RPC.context_raw_get node block ~path ~depth:q#depth >>= function
|
||||||
|
| None -> raise Not_found
|
||||||
|
| Some v -> return v)
|
||||||
|
in
|
||||||
|
|
||||||
(* Workers : Prevalidators *)
|
(* Workers : Prevalidators *)
|
||||||
|
|
||||||
|
@ -153,6 +153,23 @@ let preapply_result_encoding =
|
|||||||
(req "operations"
|
(req "operations"
|
||||||
(list (Preapply_result.encoding RPC_error.encoding)))))
|
(list (Preapply_result.encoding RPC_error.encoding)))))
|
||||||
|
|
||||||
|
type raw_context_result =
|
||||||
|
| Key of MBytes.t
|
||||||
|
| Dir of (string * raw_context_result) list
|
||||||
|
| Cut
|
||||||
|
|
||||||
|
let raw_context_result_pp t =
|
||||||
|
let open Format in
|
||||||
|
let rec loop ppf = function
|
||||||
|
| Cut -> fprintf ppf "..."
|
||||||
|
| Key v -> let `Hex s = MBytes.to_hex v in fprintf ppf "%S" s
|
||||||
|
| Dir l ->
|
||||||
|
fprintf ppf "{@[<v 1>@,%a@]@,}"
|
||||||
|
(pp_print_list ~pp_sep:Format.pp_print_cut
|
||||||
|
(fun ppf (s,t) -> fprintf ppf "%s : %a" s loop t)) l
|
||||||
|
in
|
||||||
|
asprintf "%a" loop t
|
||||||
|
|
||||||
module S = struct
|
module S = struct
|
||||||
|
|
||||||
let blocks_arg =
|
let blocks_arg =
|
||||||
@ -239,6 +256,47 @@ module S = struct
|
|||||||
~output: (obj1 (req "context" Context_hash.encoding))
|
~output: (obj1 (req "context" Context_hash.encoding))
|
||||||
RPC_path.(block_path / "context")
|
RPC_path.(block_path / "context")
|
||||||
|
|
||||||
|
let raw_context_args : string RPC_arg.t =
|
||||||
|
let name = "context_path" in
|
||||||
|
let descr = "A path inside the context" in
|
||||||
|
let construct = fun s -> s in
|
||||||
|
let destruct = fun s -> Ok s in
|
||||||
|
RPC_arg.make ~name ~descr ~construct ~destruct ()
|
||||||
|
|
||||||
|
let raw_context_result_encoding : raw_context_result Data_encoding.t =
|
||||||
|
let open Data_encoding in
|
||||||
|
obj1 (req "content"
|
||||||
|
(mu "context_tree" (fun raw_context_result_encoding ->
|
||||||
|
union [
|
||||||
|
case (Tag 0) bytes
|
||||||
|
(function Key k -> Some k | _ -> None)
|
||||||
|
(fun k -> Key k) ;
|
||||||
|
case (Tag 1) (assoc raw_context_result_encoding)
|
||||||
|
(function Dir k -> Some k | _ -> None)
|
||||||
|
(fun k -> Dir k) ;
|
||||||
|
case (Tag 2) null
|
||||||
|
(function Cut -> Some () | _ -> None)
|
||||||
|
(fun () -> Cut) ;
|
||||||
|
])))
|
||||||
|
|
||||||
|
(* The depth query argument for the [raw_context] service,
|
||||||
|
default value is 1. *)
|
||||||
|
let depth_query : < depth: int > RPC_query.t =
|
||||||
|
let open RPC_query in
|
||||||
|
query (fun depth -> object
|
||||||
|
method depth = depth
|
||||||
|
end)
|
||||||
|
|+ field "depth" RPC_arg.int 1 (fun t -> t#depth)
|
||||||
|
|> seal
|
||||||
|
|
||||||
|
let raw_context =
|
||||||
|
RPC_service.post_service
|
||||||
|
~description:"Returns the raw context."
|
||||||
|
~query: depth_query
|
||||||
|
~input: empty
|
||||||
|
~output: raw_context_result_encoding
|
||||||
|
RPC_path.(block_path / "raw_context" /:* raw_context_args)
|
||||||
|
|
||||||
let timestamp =
|
let timestamp =
|
||||||
RPC_service.post_service
|
RPC_service.post_service
|
||||||
~description:"Returns the block's timestamp."
|
~description:"Returns the block's timestamp."
|
||||||
@ -508,3 +566,10 @@ let unmark_invalid ctxt h =
|
|||||||
|
|
||||||
let list_invalid ctxt =
|
let list_invalid ctxt =
|
||||||
make_call S.list_invalid ctxt () () ()
|
make_call S.list_invalid ctxt () () ()
|
||||||
|
|
||||||
|
let raw_context ctxt b key depth =
|
||||||
|
let depth = object
|
||||||
|
method depth = depth
|
||||||
|
end
|
||||||
|
in
|
||||||
|
make_call2 S.raw_context ctxt b key depth ()
|
||||||
|
@ -102,6 +102,21 @@ val preapply:
|
|||||||
val complete:
|
val complete:
|
||||||
#simple -> block -> string -> string list tzresult Lwt.t
|
#simple -> block -> string -> string list tzresult Lwt.t
|
||||||
|
|
||||||
|
(** Encodes a directory structure returned from a context
|
||||||
|
query as a tree plus a special case [Cut] used when
|
||||||
|
the query is limited by a [depth] value.
|
||||||
|
[Cut] is encoded as [null] in json. *)
|
||||||
|
type raw_context_result =
|
||||||
|
| Key of MBytes.t
|
||||||
|
| Dir of (string * raw_context_result) list
|
||||||
|
| Cut
|
||||||
|
|
||||||
|
(** Pretty-printer for raw_context_result *)
|
||||||
|
val raw_context_result_pp : raw_context_result -> string
|
||||||
|
|
||||||
|
val raw_context:
|
||||||
|
#simple -> block -> string list -> int -> raw_context_result tzresult Lwt.t
|
||||||
|
|
||||||
val monitor_prevalidated_operations:
|
val monitor_prevalidated_operations:
|
||||||
?contents:bool ->
|
?contents:bool ->
|
||||||
#streamed ->
|
#streamed ->
|
||||||
@ -112,7 +127,8 @@ val unmark_invalid:
|
|||||||
val list_invalid:
|
val list_invalid:
|
||||||
#simple -> (Block_hash.t * int32 * error list) list tzresult Lwt.t
|
#simple -> (Block_hash.t * int32 * error list) list tzresult Lwt.t
|
||||||
|
|
||||||
|
(** Signatures of all RPCs.
|
||||||
|
This module is shared between the Client and the Node. *)
|
||||||
module S : sig
|
module S : sig
|
||||||
|
|
||||||
val blocks_arg : block RPC_arg.arg
|
val blocks_arg : block RPC_arg.arg
|
||||||
@ -154,6 +170,19 @@ module S : sig
|
|||||||
unit * block, unit, unit,
|
unit * block, unit, unit,
|
||||||
Context_hash.t) RPC_service.t
|
Context_hash.t) RPC_service.t
|
||||||
|
|
||||||
|
(** Accepts queries of the form
|
||||||
|
/blocks/<id>/raw_context/<path>?depth=<n>
|
||||||
|
returning the sub-tree corresponding to <path> inside the context of
|
||||||
|
block <id>. The optional parameter <depth> controls the size of the
|
||||||
|
tree, default is 1.
|
||||||
|
Example:
|
||||||
|
tezos-client rpc call /blocks/head/raw_context/v1?depth=2
|
||||||
|
*)
|
||||||
|
val raw_context:
|
||||||
|
([ `POST ], unit,
|
||||||
|
(unit * block) * string list, <depth:int>, unit,
|
||||||
|
raw_context_result) RPC_service.t
|
||||||
|
|
||||||
type operations_param = {
|
type operations_param = {
|
||||||
contents: bool ;
|
contents: bool ;
|
||||||
monitor: bool ;
|
monitor: bool ;
|
||||||
|
Loading…
Reference in New Issue
Block a user