diff --git a/src/Makefile b/src/Makefile index 78b6630db..182a89f9c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,7 +12,8 @@ all: ${TZCOMPILER} ${TZNODE} ${TZCLIENT} ## Protocol environment ############################################################################ -PROTOCOL_ENV_INTFS := $(addprefix proto/environment/, \ +PROTOCOL_ENV_INTFS := \ +$(addprefix proto/environment/, \ pervasives.mli \ compare.mli \ \ @@ -30,7 +31,7 @@ PROTOCOL_ENV_INTFS := $(addprefix proto/environment/, \ uri.mli \ data_encoding.mli \ time.mli \ - base48.mli \ + ../../utils/base48.mli \ hash.mli \ ed25519.mli \ persist.mli \ @@ -42,7 +43,8 @@ PROTOCOL_ENV_INTFS := $(addprefix proto/environment/, \ ) \ utils/logging.mli \ utils/error_monad_sig.ml \ -utils/error_monad.mli +utils/error_monad.mli \ + .INTERMEDIATE: node/updater/environment_gen .SECONDARY: node/updater/proto_environment.mli @@ -75,6 +77,7 @@ EMBEDDED_PROTOCOL_LIB_CMIS := \ tmp/camlinternalFormatBasics.cmi \ utils/error_monad.cmi \ proto/environment/error_monad.mli \ + proto/environment/base48.mli \ proto/environment/logging.mli \ node/updater/proto_environment.cmi \ node/updater/register.cmi diff --git a/src/compiler/embedded_cmis.mli b/src/compiler/embedded_cmis.mli index 288657844..8b4cc4a0e 100644 --- a/src/compiler/embedded_cmis.mli +++ b/src/compiler/embedded_cmis.mli @@ -11,5 +11,6 @@ val camlinternalFormatBasics_cmi: string val error_monad_cmi: string val error_monad_mli: string val logging_mli: string +val base48_mli: string val proto_environment_cmi: string val register_cmi: string diff --git a/src/compiler/tezos_compiler.ml b/src/compiler/tezos_compiler.ml index 8211165ae..66eba9ed7 100644 --- a/src/compiler/tezos_compiler.ml +++ b/src/compiler/tezos_compiler.ml @@ -258,7 +258,8 @@ let link_shared ?(static=false) output objects = let create_register_file client file hash packname modules = let unit = List.hd (List.rev modules) in - let error_monad = packname ^ ".Local_error_monad.Error_monad" in + let error_monad = packname ^ ".Local_modules.Error_monad" in + let base48 = packname ^ ".Local_modules.Base48" in create_file file (Printf.sprintf "module Packed_protocol = struct\n\ @@ -269,6 +270,7 @@ let create_register_file client file hash packname modules = \ let error_encoding = %s.error_encoding ()\n\ \ let classify_errors = %s.classify_errors\n\ \ let pp = %s.pp\n\ + \ let complete_b48prefix = %s.complete \ end\n\ \ %s\n\ " @@ -279,6 +281,7 @@ let create_register_file client file hash packname modules = error_monad error_monad error_monad + base48 (if client then "include Register.Make(Packed_protocol)" else @@ -397,38 +400,44 @@ let main () = (* Compile the /ad-hoc/ Error_monad. *) List.iter (dump_cmi sigs_dir) tezos_protocol_env ; at_exit (fun () -> List.iter (unlink_cmi sigs_dir) tezos_protocol_env ) ; - let error_monad_unit = "local_error_monad" in - let error_monad_ml = build_dir // error_monad_unit ^ ".ml" in - create_file error_monad_ml @@ Printf.sprintf {| + let local_modules_unit = "local_modules" in + let local_modules_ml = build_dir // local_modules_unit ^ ".ml" in + create_file local_modules_ml @@ Printf.sprintf {| module Error_monad = struct type error_category = [ `Branch | `Temporary | `Permanent ] include Error_monad.Make() end module Logging = Logging.Make(struct let name = %S end) + module Base48 = struct + include Base48 + include Make(struct type context = Context.t end) + end |} logname ; - let error_monad_mli = build_dir // error_monad_unit ^ ".mli" in - create_file error_monad_mli @@ Printf.sprintf {| + let local_modules_mli = build_dir // local_modules_unit ^ ".mli" in + create_file local_modules_mli @@ Printf.sprintf {| module Error_monad : sig %s end module Logging : sig %s end + module Base48 : sig %s end |} Embedded_cmis.error_monad_mli - Embedded_cmis.logging_mli ; + Embedded_cmis.logging_mli + Embedded_cmis.base48_mli ; if not keep_object then at_exit (fun () -> - safe_unlink error_monad_mli ; - safe_unlink error_monad_ml) ; - let error_monad_object = + safe_unlink local_modules_mli ; + safe_unlink local_modules_ml) ; + let local_modules_object = compile_units ~ctxt ~for_pack:packname ~keep_object - ~build_dir ~source_dir:build_dir [error_monad_unit] + ~build_dir ~source_dir:build_dir [local_modules_unit] in Compenv.implicit_modules := !Compenv.implicit_modules @ - [ "Local_error_monad"; "Error_monad" ; "Hash" ; "Logging" ]; + [ "Local_modules"; "Error_monad" ; "Hash" ; "Logging" ]; (* Compile the protocol *) let objects = @@ -437,7 +446,7 @@ let main () = ~update_needed ~keep_object ~for_pack:packname ~build_dir ~source_dir units in pack_objects ~ctxt ~keep_object - packed_objects (error_monad_object @ objects) ; + packed_objects (local_modules_object @ objects) ; (* Compiler the 'registering module' *) List.iter (dump_cmi sigs_dir) register_env; diff --git a/src/node/db/context.ml b/src/node/db/context.ml index 6f16c2b2d..982623809 100644 --- a/src/node/db/context.ml +++ b/src/node/db/context.ml @@ -30,6 +30,7 @@ module rec S : sig val update_path: t -> IrminPath.t -> v -> unit Lwt.t val mem: v -> IrminPath.t -> bool Lwt.t + val dir_mem: v -> IrminPath.t -> bool Lwt.t val get: v -> IrminPath.t -> MBytes.t option Lwt.t val set: v -> IrminPath.t -> MBytes.t-> v Lwt.t val del: v -> IrminPath.t -> v Lwt.t @@ -187,6 +188,11 @@ let mem (module View : VIEW) key = GitStore.FunView.mem View.v (data_key key) >>= fun v -> Lwt.return v +let dir_mem (module View : VIEW) key = + let module GitStore = View.Store in + GitStore.FunView.dir_mem View.v (data_key key) >>= fun v -> + Lwt.return v + let raw_get (module View : VIEW) key = let module GitStore = View.Store in GitStore.FunView.get View.v key >>= function diff --git a/src/node/db/ir_funview.ml b/src/node/db/ir_funview.ml index acaf941b3..7b08ebee8 100644 --- a/src/node/db/ir_funview.ml +++ b/src/node/db/ir_funview.ml @@ -466,6 +466,11 @@ module Make (S: Irmin.S) = struct | None -> Lwt.return false | _ -> Lwt.return true + let dir_mem t k = + sub t k >>= function + | Some _ -> Lwt.return true + | None -> Lwt.return false + let list_aux t path = sub t path >>= function | None -> Lwt.return [] @@ -662,6 +667,7 @@ end module type S = sig include Irmin.RO + val dir_mem: t -> key -> bool Lwt.t val update: t -> key -> value -> t Lwt.t val remove: t -> key -> t Lwt.t val list: t -> key -> key list Lwt.t diff --git a/src/node/db/ir_funview.mli b/src/node/db/ir_funview.mli index bdf1671cb..b72dca5c2 100644 --- a/src/node/db/ir_funview.mli +++ b/src/node/db/ir_funview.mli @@ -9,6 +9,7 @@ module type S = sig include Irmin.RO + val dir_mem: t -> key -> bool Lwt.t val update: t -> key -> value -> t Lwt.t val remove: t -> key -> t Lwt.t val list: t -> key -> key list Lwt.t diff --git a/src/node/db/persist.ml b/src/node/db/persist.ml index b0ae55440..56cb57bcd 100644 --- a/src/node/db/persist.ml +++ b/src/node/db/persist.ml @@ -19,13 +19,13 @@ type value = MBytes.t module type STORE = sig type t val mem: t -> key -> bool Lwt.t + val dir_mem: t -> key -> bool Lwt.t val get: t -> key -> value option Lwt.t val set: t -> key -> value -> t Lwt.t val del: t -> key -> t Lwt.t val list: t -> key list -> key list Lwt.t val remove_rec: t -> key -> t Lwt.t - - val keys : t -> key list Lwt.t + val keys: t -> key list Lwt.t end module type BYTES_STORE = sig @@ -37,8 +37,7 @@ module type BYTES_STORE = sig val del: t -> key -> t Lwt.t val list: t -> key list -> key list Lwt.t val remove_rec: t -> key -> t Lwt.t - - val keys : t -> key list Lwt.t + val keys: t -> key list Lwt.t end module type TYPED_STORE = sig @@ -49,7 +48,6 @@ module type TYPED_STORE = sig val get: t -> key -> value option Lwt.t val set: t -> key -> value -> t Lwt.t val del: t -> key -> t Lwt.t - val keys: t -> key list Lwt.t end @@ -583,3 +581,37 @@ module MakeBufferedPersistentTypedMap (Map : Map.S with type key = K.t) = MakeBufferedPersistentMap(S)(K)(TypedValue(T))(Map) + +module MakeHashResolver + (Store : sig + type t + val dir_mem: t -> string list -> bool Lwt.t + val list: t -> string list list -> string list list Lwt.t + val prefix: string list + end) + (H: HASH) = struct + let plen = List.length Store.prefix + let build path = + H.of_path @@ + Utils.remove_elem_from_list plen path + let resolve t p = + let rec loop prefix = function + | [] -> + Lwt.return [build prefix] + | "" :: ds -> + Store.list t [ prefix] >>= fun prefixes -> + Lwt_list.map_p (fun prefix -> loop prefix ds) prefixes + >|= List.flatten + | [d] -> + Store.list t [prefix] >>= fun prefixes -> + Lwt_list.filter_map_p (fun prefix -> + match remove_prefix d (List.hd (List.rev prefix)) with + | None -> Lwt.return_none + | Some _ -> Lwt.return (Some (build prefix)) + ) prefixes + | d :: ds -> + Store.dir_mem t (prefix @ [d]) >>= function + | true -> loop (prefix @ [d]) ds + | false -> Lwt.return_nil in + loop Store.prefix (H.prefix_path p) +end diff --git a/src/node/db/persist.mli b/src/node/db/persist.mli index ede8de0e4..9b87058cc 100644 --- a/src/node/db/persist.mli +++ b/src/node/db/persist.mli @@ -22,13 +22,13 @@ type value = MBytes.t module type STORE = sig type t val mem: t -> key -> bool Lwt.t + val dir_mem: t -> key -> bool Lwt.t val get: t -> key -> value option Lwt.t val set: t -> key -> value -> t Lwt.t val del: t -> key -> t Lwt.t val list: t -> key list -> key list Lwt.t val remove_rec: t -> key -> t Lwt.t - - val keys : t -> key list Lwt.t + val keys: t -> key list Lwt.t end (** Projection of OCaml keys of some abstract type to concrete storage @@ -258,3 +258,14 @@ module MakeBufferedPersistentTypedMap and type key := K.t and type value := T.value and module Map := Map + +module MakeHashResolver + (Store : sig + type t + val dir_mem: t -> string list -> bool Lwt.t + val list: t -> string list list -> string list list Lwt.t + val prefix: string list + end) + (H: HASH) : sig + val resolve : Store.t -> string -> H.t list Lwt.t +end diff --git a/src/node/db/store.ml b/src/node/db/store.ml index 23744650f..92a342922 100644 --- a/src/node/db/store.ml +++ b/src/node/db/store.ml @@ -50,6 +50,10 @@ module FS = struct let file = file_of_key root key in Lwt.return (Sys.file_exists file && not (Sys.is_directory file)) + let dir_mem root key = + let file = file_of_key root key in + Lwt.return (Sys.file_exists file && Sys.is_directory file) + let exists root key = let file = file_of_key root key in Sys.file_exists file @@ -139,6 +143,7 @@ end module type IMPERATIVE_STORE = sig type t val mem: t -> key -> bool Lwt.t + val dir_mem: t -> key -> bool Lwt.t val get: t -> key -> value option Lwt.t val get_exn: t -> key -> value Lwt.t val set: t -> key -> value -> unit Lwt.t @@ -210,6 +215,7 @@ module Make (K : KEY) (V : Persist.VALUE) = struct type key = K.t type value = V.t let mem t k = FS.mem t (K.to_path k) + let dir_mem t k = FS.dir_mem t (K.to_path k) let get t k = FS.get t (K.to_path k) >|= function | None -> None @@ -226,37 +232,6 @@ module Make (K : KEY) (V : Persist.VALUE) = struct let keys _t = undefined_key_fn end -module MakeResolver (P: sig val prefix: string list end) (H: HASH) = struct - let plen = List.length P.prefix - let build path = - H.to_raw @@ H.of_path @@ - Utils.remove_elem_from_list plen path - let resolve t p = - let rec loop prefix = function - | [] -> Lwt.return [build prefix] - | "" :: ds -> - FS.list t [ prefix] >>= fun prefixes -> - Lwt_list.map_p (fun prefix -> loop prefix ds) prefixes - >|= List.flatten - | [d] -> - FS.list t [prefix] >>= fun prefixes -> - Lwt_list.filter_map_p (fun prefix -> - match remove_prefix d (List.hd (List.rev prefix)) with - | None -> Lwt.return_none - | Some _ -> Lwt.return (Some (build prefix)) - ) prefixes - | d :: ds -> - if FS.exists t prefix then - loop (prefix @ [d]) ds - else - Lwt.return_nil in - loop P.prefix (H.prefix_path p) - let register t = - match H.kind with - | None -> () - | Some kind -> Base48.register_resolver kind (resolve t) -end - module Data_store : IMPERATIVE_STORE with type t = FS.t = Make (Raw_key) (Raw_value) @@ -343,7 +318,12 @@ end module Block_errors = Make (Block_errors_key) (Errors_value) module Block_resolver = - MakeResolver(struct let prefix = ["blocks"] end)(Block_hash) + Persist.MakeHashResolver + (struct + include FS + let prefix = ["blocks"] + end) + (Block_hash) module Block = struct type t = FS.t @@ -497,7 +477,13 @@ end module Operation_errors = Make (Operation_errors_key) (Errors_value) module Operation_resolver = - MakeResolver(struct let prefix = ["operations"] end)(Operation_hash) + Persist.MakeHashResolver + (struct + include FS + let mem t k = Lwt.return (exists t k) + let prefix = ["operations"] + end) + (Operation_hash) module Operation = struct type t = FS.t @@ -756,8 +742,12 @@ let net_destroy ~root { net_genesis } = let init root = raw_init ~root:(Filename.concat root "global") () >>= fun t -> - Block_resolver.register t ; - Operation_resolver.register t ; + Base48.register_resolver + Block_hash.b48check_encoding + (fun s -> Block_resolver.resolve t s); + Base48.register_resolver + Operation_hash.b48check_encoding + (fun s -> Operation_resolver.resolve t s); Lwt.return { block = Persist.share t ; blockchain = Persist.share t ; diff --git a/src/node/db/store.mli b/src/node/db/store.mli index 6a311bf43..43f2a6e61 100644 --- a/src/node/db/store.mli +++ b/src/node/db/store.mli @@ -27,6 +27,7 @@ end module type IMPERATIVE_STORE = sig type t val mem: t -> key -> bool Lwt.t + val dir_mem: t -> key -> bool Lwt.t val get: t -> key -> value option Lwt.t val get_exn: t -> key -> value Lwt.t val set: t -> key -> value -> unit Lwt.t diff --git a/src/node/shell/node.ml b/src/node/shell/node.ml index 0ee7704bb..38b69c9f2 100644 --- a/src/node/shell/node.ml +++ b/src/node/shell/node.ml @@ -498,6 +498,20 @@ module RPC = struct Proto.fitness ctxt >>= fun fitness -> return (fitness, r) + let complete node ?block str = + match block with + | None -> + Base48.complete str + | Some block -> + get_context node block >>= function + | None -> Lwt.fail Not_found + | Some ctxt -> + Context.get_protocol ctxt >>= fun protocol_hash -> + let (module Proto) = Updater.get_exn protocol_hash in + Base48.complete str >>= fun l1 -> + Proto.complete_b48prefix ctxt str >>= fun l2 -> + Lwt.return (l1 @ l2) + let context_dir node block = get_context node block >>= function | None -> Lwt.return None diff --git a/src/node/shell/node.mli b/src/node/shell/node.mli index c771de089..997b71ca9 100644 --- a/src/node/shell/node.mli +++ b/src/node/shell/node.mli @@ -72,6 +72,12 @@ module RPC : sig val validate: t -> State.net_id -> Block_hash.t -> unit tzresult Lwt.t + val context_dir: + t -> block -> 'a RPC.directory option Lwt.t + + val complete: + t -> ?block:block -> string -> string list Lwt.t + end val shutdown: t -> unit Lwt.t diff --git a/src/node/shell/node_rpc.ml b/src/node/shell/node_rpc.ml index 87ed0191a..354187527 100644 --- a/src/node/shell/node_rpc.ml +++ b/src/node/shell/node_rpc.ml @@ -437,8 +437,11 @@ let build_rpc_directory node = let dir = RPC.register1 dir Services.complete (fun s () -> - Base48.decode_partial s >>= fun l -> - RPC.Answer.return (List.map Base48.encode l)) in + Node.RPC.complete node s >>= RPC.Answer.return) in + let dir = + RPC.register2 dir Services.Blocks.complete + (fun block s () -> + Node.RPC.complete node ~block s >>= RPC.Answer.return) in let dir = RPC.register_describe_directory_service dir Services.describe in dir diff --git a/src/node/shell/node_rpc_services.ml b/src/node/shell/node_rpc_services.ml index c85c28283..c1046755a 100644 --- a/src/node/shell/node_rpc_services.ml +++ b/src/node/shell/node_rpc_services.ml @@ -255,6 +255,19 @@ module Blocks = struct ~output: (RPC.Error.wrap preapply_result_encoding) RPC.Path.(block_path / "preapply") + let complete = + let prefix_arg = + let destruct s = Ok s + and construct s = s in + RPC.Arg.make ~name:"prefix" ~destruct ~construct () in + RPC.service + ~description: "Try to complete a prefix of a Base48Check-encoded data. \ + This RPC is actually able to complete hashes of \ + block, operations, public_keys and contracts." + ~input: empty + ~output: (list string) + RPC.Path.(block_path / "complete" /: prefix_arg ) + type list_param = { operations: bool option ; length: int option ; @@ -329,6 +342,8 @@ module Blocks = struct ~output: (obj1 (req "blocks" (list (list block_info_encoding)))) RPC.Path.(root / "blocks") + + end module Operations = struct @@ -591,7 +606,7 @@ let complete = RPC.service ~description: "Try to complete a prefix of a Base48Check-encoded data. \ This RPC is actually able to complete hashes of \ - black and hashes of operations." + block and hashes of operations." ~input: empty ~output: (list string) RPC.Path.(root / "complete" /: prefix_arg ) diff --git a/src/node/shell/node_rpc_services.mli b/src/node/shell/node_rpc_services.mli index ca0edb4e2..bc2e5e86b 100644 --- a/src/node/shell/node_rpc_services.mli +++ b/src/node/shell/node_rpc_services.mli @@ -80,6 +80,8 @@ module Blocks : sig val preapply: (unit, unit * block, preapply_param, preapply_result tzresult) RPC.service + val complete: (unit, (unit * block) * string, unit, string list) RPC.service + val proto_path: (unit, unit * block) RPC.Path.path end diff --git a/src/node/updater/environment.ml b/src/node/updater/environment.ml index a1bc9d8ab..23ca178ab 100644 --- a/src/node/updater/environment.ml +++ b/src/node/updater/environment.ml @@ -72,21 +72,25 @@ module Ed25519 = struct Base48.register_encoding ~prefix: Base48.Prefix.ed25519_public_key ~to_raw:(fun x -> Bytes.to_string (Sodium.Sign.Bytes.of_public_key x)) - ~of_raw:(fun x -> Sodium.Sign.Bytes.to_public_key (Bytes.of_string x)) + ~of_raw:(fun x -> + try Some (Sodium.Sign.Bytes.to_public_key (Bytes.of_string x)) + with _ -> None) ~wrap:(fun x -> Public_key x) let b48check_secret_key_encoding = Base48.register_encoding ~prefix: Base48.Prefix.ed25519_secret_key ~to_raw:(fun x -> Bytes.to_string (Sodium.Sign.Bytes.of_secret_key x)) - ~of_raw:(fun x -> Sodium.Sign.Bytes.to_secret_key (Bytes.of_string x)) + ~of_raw:(fun x -> + try Some (Sodium.Sign.Bytes.to_secret_key (Bytes.of_string x)) + with _ -> None) ~wrap:(fun x -> Secret_key x) let b48check_signature_encoding = Base48.register_encoding ~prefix: Base48.Prefix.ed25519_signature ~to_raw:MBytes.to_string - ~of_raw:MBytes.of_string + ~of_raw:(fun s -> Some (MBytes.of_string s)) ~wrap:(fun x -> Signature x) let public_key_encoding = @@ -164,4 +168,6 @@ module type PACKED_PROTOCOL = sig val error_encoding : error Data_encoding.t val classify_errors : error list -> [ `Branch | `Temporary | `Permanent ] val pp : Format.formatter -> error -> unit + val complete_b48prefix : + ?alphabet:string -> Context.t -> string -> string list Lwt.t end diff --git a/src/node/updater/environment_gen.ml b/src/node/updater/environment_gen.ml index d40b093cd..7f578346b 100644 --- a/src/node/updater/environment_gen.ml +++ b/src/node/updater/environment_gen.ml @@ -48,6 +48,8 @@ module type PACKED_PROTOCOL = sig val error_encoding : error Data_encoding.t val classify_errors : error list -> [ `Branch | `Temporary | `Permanent ] val pp : Format.formatter -> error -> unit + val complete_b48prefix : + ?alphabet:string -> Context.t -> string -> string list Lwt.t end val __cast: (module PACKED_PROTOCOL) -> (module Protocol.PACKED_PROTOCOL) |} diff --git a/src/node/updater/protocol.mli b/src/node/updater/protocol.mli index 88867d1f8..3d3ea57a3 100644 --- a/src/node/updater/protocol.mli +++ b/src/node/updater/protocol.mli @@ -135,4 +135,6 @@ module type PACKED_PROTOCOL = sig val error_encoding : error Data_encoding.t val classify_errors : error list -> [ `Branch | `Temporary | `Permanent ] val pp : Format.formatter -> error -> unit + val complete_b48prefix : + ?alphabet:string -> Context.t -> string -> string list Lwt.t end diff --git a/src/node/updater/updater.ml b/src/node/updater/updater.ml index e9b0af49c..3ca23c774 100644 --- a/src/node/updater/updater.ml +++ b/src/node/updater/updater.ml @@ -16,6 +16,8 @@ module type REGISTRED_PROTOCOL = sig val hash: Protocol_hash.t include Protocol.PROTOCOL with type error := error and type 'a tzresult := 'a tzresult + val complete_b48prefix : + ?alphabet:string -> Context.t -> string -> string list Lwt.t end type net_id = Store.net_id = Net of Block_hash.t diff --git a/src/node/updater/updater.mli b/src/node/updater/updater.mli index 09bd44185..aaad9c544 100644 --- a/src/node/updater/updater.mli +++ b/src/node/updater/updater.mli @@ -68,6 +68,8 @@ module type REGISTRED_PROTOCOL = sig (* exception Ecoproto_error of error list *) include Protocol.PROTOCOL with type error := error and type 'a tzresult := 'a tzresult + val complete_b48prefix : + ?alphabet:string -> Context.t -> string -> string list Lwt.t end type component = Tezos_compiler.Protocol.component = { diff --git a/src/proto/bootstrap/.merlin b/src/proto/bootstrap/.merlin index 46edbf5ec..1d807e77f 100644 --- a/src/proto/bootstrap/.merlin +++ b/src/proto/bootstrap/.merlin @@ -3,7 +3,7 @@ B _tzbuild FLG -nopervasives FLG -open Proto_environment FLG -open Hash -FLG -open Local_error_monad +FLG -open Local_modules FLG -open Error_monad FLG -open Logging FLG -w -40 diff --git a/src/proto/bootstrap/storage.ml b/src/proto/bootstrap/storage.ml index 88f7b1097..2a23756f8 100644 --- a/src/proto/bootstrap/storage.ml +++ b/src/proto/bootstrap/storage.ml @@ -88,6 +88,7 @@ module Key = struct end module Contract = struct + let store_root l = store_root ("contracts" :: l) let set = store_root ["set"] let pubkey_contract l = store_root ("pubkey" :: l) @@ -238,6 +239,7 @@ module Contract = struct let encoding = Data_encoding.int32 end) + (** FIXME REMOVE : use 'list' *) module Set = Make_data_set_storage(struct type value = Contract_repr.t @@ -518,3 +520,15 @@ let fork_test_network (c, constants) = Updater.fork_test_network c >>= fun c -> Lwt.return (c, constants) let set_test_protocol (c, constants) h = Updater.set_test_protocol c h >>= fun c -> Lwt.return (c, constants) + + +(** Resolver *) + +let () = + Storage_functors.register_resolvers + (module Contract_hash) + [ Key.Contract.generic_contract [] ] ; + Storage_functors.register_resolvers + (module Ed25519.Public_key_hash) + [ Key.Contract.pubkey_contract [] ; + Key.public_keys ] diff --git a/src/proto/bootstrap/storage_functors.ml b/src/proto/bootstrap/storage_functors.ml index 1bbf874d7..d95851b64 100644 --- a/src/proto/bootstrap/storage_functors.ml +++ b/src/proto/bootstrap/storage_functors.ml @@ -202,10 +202,9 @@ end module Make_data_set_storage (P : Single_data_description) = struct module Key = struct - include Hash.Make_SHA256(struct + include Hash.Make_minimal_SHA256(struct let name = P.name let title = ("A " ^ P.name ^ "key") - let prefix = None end) let prefix = P.key let length = path_len @@ -352,3 +351,31 @@ module Make_iterable_data_storage (H: HASH) (P: Single_data_description) = let prefix = P.key let length = path_len end)(P) + +let register_resolvers (module H : Hash.HASH) prefixes = + + let module Set = Hash_set(H) in + + let resolvers = + List.map + (fun prefix -> + let module R = Persist.MakeHashResolver(struct + include Context + let prefix = prefix + end)(H) in + R.resolve) + prefixes in + + let resolve c m = + match resolvers with + | [resolve] -> resolve c m + | resolvers -> + Lwt_list.map_p (fun resolve -> resolve c m) resolvers >|= fun hs -> + List.fold_left + (fun acc hs -> List.fold_left (fun acc h -> Set.add h acc) acc hs) + Set.empty hs |> + Set.elements in + + Base48.register_resolver H.b48check_encoding resolve + + diff --git a/src/proto/bootstrap/storage_functors.mli b/src/proto/bootstrap/storage_functors.mli index 52341b87d..53593b868 100644 --- a/src/proto/bootstrap/storage_functors.mli +++ b/src/proto/bootstrap/storage_functors.mli @@ -98,3 +98,5 @@ module Raw_make_iterable_data_storage (K: Persist.KEY) (P: Data_description) : and type value = P.value and type context := context +val register_resolvers: (module Hash.HASH) -> string list list -> unit + diff --git a/src/proto/bootstrap/storage_helpers.mli b/src/proto/bootstrap/storage_helpers.mli index c836faf2f..67e74fbdb 100644 --- a/src/proto/bootstrap/storage_helpers.mli +++ b/src/proto/bootstrap/storage_helpers.mli @@ -227,3 +227,10 @@ module Make_data_set_storage (P : Single_data_description) : module Make_iterable_data_storage (H : HASH) (P: Single_data_description) : Iterable_data_storage with type key = H.t and type value = P.value + +module Make_hash_resolver + (K: sig val prefix: string list end) + (H: Hash.HASH) : sig + val register : Store.t -> unit +end + diff --git a/src/proto/bootstrap/tezos_hash.ml b/src/proto/bootstrap/tezos_hash.ml index 1f53b4496..0cf2b3b96 100644 --- a/src/proto/bootstrap/tezos_hash.ml +++ b/src/proto/bootstrap/tezos_hash.ml @@ -8,42 +8,44 @@ (**************************************************************************) module Prefix = struct - let random_state_hash = Base48.Prefix.protocol_prefix ^ "\001" - let nonce_hash = Base48.Prefix.protocol_prefix ^ "\002" - let script_expr_hash = Base48.Prefix.protocol_prefix ^ "\003" - let proposition_hash = Base48.Prefix.protocol_prefix ^ "\004" - let contract_hash = Base48.Prefix.protocol_prefix ^ "\005" + let make x = + assert (Compare.String.(Base48.Prefix.protocol_prefix = "\015")) ; + String.make 1 (char_of_int ((x lsl 4) lor 15)) + let public_key_hash = make 0 + let contract_hash = make 1 + let nonce_hash = make 2 + let script_expr_hash = make 3 + let random_state_hash = make 15 (* never used... *) end -module State_hash = Hash.Make_SHA256(struct +module State_hash = Hash.Make_SHA256(Base48)(struct let name = "random" let title = "A random generation state" - let prefix = Some Prefix.random_state_hash + let b48check_prefix = Prefix.random_state_hash end) module State_hash_set = Hash_set(State_hash) module State_hash_map = Hash_map(State_hash) -module Nonce_hash = Hash.Make_SHA256(struct +module Nonce_hash = Hash.Make_SHA256(Base48)(struct let name = "cycle_nonce" let title = "A nonce hash" - let prefix = Some Prefix.nonce_hash + let b48check_prefix = Prefix.nonce_hash end) module Nonce_hash_set = Hash_set(Nonce_hash) module Nonce_hash_map = Hash_map(Nonce_hash) -module Script_expr_hash = Hash.Make_SHA256(struct +module Script_expr_hash = Hash.Make_SHA256(Base48)(struct let name = "script_expr" let title = "A script expression ID" - let prefix = Some Prefix.script_expr_hash + let b48check_prefix = Prefix.script_expr_hash end) module Script_expr_hash_set = Hash_set(Script_expr_hash) module Script_expr_hash_map = Hash_map(Script_expr_hash) -module Contract_hash = Hash.Make_SHA256(struct +module Contract_hash = Hash.Make_SHA256(Base48)(struct let name = "Contract_hash" let title = "A contract ID" - let prefix = Some Prefix.contract_hash + let b48check_prefix = Prefix.contract_hash end) module Contract_hash_set = Hash_set(Contract_hash) module Contract_hash_map = Hash_map(Contract_hash) - diff --git a/src/proto/demo/.merlin b/src/proto/demo/.merlin index 28fcf44c1..1d807e77f 100644 --- a/src/proto/demo/.merlin +++ b/src/proto/demo/.merlin @@ -3,6 +3,7 @@ B _tzbuild FLG -nopervasives FLG -open Proto_environment FLG -open Hash -FLG -open Local_error_monad +FLG -open Local_modules FLG -open Error_monad +FLG -open Logging FLG -w -40 diff --git a/src/proto/environment/base48.mli b/src/proto/environment/base48.mli index b2398c7a2..dd000467b 100644 --- a/src/proto/environment/base48.mli +++ b/src/proto/environment/base48.mli @@ -1,20 +1,26 @@ -type data = .. - -val decode: ?alphabet:string -> string -> data -val encode: ?alphabet:string -> data -> string - -type kind - -val register: - prefix:string -> - read:(data -> string option) -> - build:(string -> data) -> - kind - -val register_resolver: - kind -> (string -> string list Lwt.t) -> unit - module Prefix : sig val protocol_prefix: string end + +type 'a encoding = 'a Base48.encoding + +val simple_decode: ?alphabet:string -> 'a encoding -> string -> 'a option +val simple_encode: ?alphabet:string -> 'a encoding -> 'a -> string + +type data = Base48.data = .. + +val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> data) -> + 'a encoding + +val decode: ?alphabet:string -> string -> data option + +val register_resolver: + 'a encoding -> (Context.t -> string -> 'a list Lwt.t) -> unit + +val complete: + ?alphabet:string -> Context.t -> string -> string list Lwt.t diff --git a/src/proto/environment/hash.mli b/src/proto/environment/hash.mli index 785ef3165..eb3ef4b53 100644 --- a/src/proto/environment/hash.mli +++ b/src/proto/environment/hash.mli @@ -1,5 +1,5 @@ -(** Tezos - Manipulation and creation of hashes *) +(** Tezos - Manipulation and creation of hashes *) (** {2 Hash Types} ************************************************************) @@ -8,9 +8,14 @@ various kinds of hashes in the system at typing time. Each type is equipped with functions to use it as is of as keys in the database or in memory sets and maps. *) -module type HASH = sig + +module type MINIMAL_HASH = sig + type t + val name: string + val title: string + val hash_bytes: MBytes.t list -> t val hash_string: string list -> t val size: int (* in bytes *) @@ -20,9 +25,6 @@ module type HASH = sig val to_raw: t -> string val of_hex: string -> t val to_hex: t -> string - val of_b48check: string -> t - val to_b48check: t -> string - val to_short_b48check: t -> string val to_bytes: t -> MBytes.t val of_bytes: MBytes.t -> t val read: MBytes.t -> int -> t @@ -31,11 +33,22 @@ module type HASH = sig val of_path: string list -> t val prefix_path: string -> string list val path_len: int + +end + +module type HASH = sig + + include MINIMAL_HASH + + val of_b48check: string -> t + val to_b48check: t -> string + val to_short_b48check: t -> string val encoding: t Data_encoding.t val pp: Format.formatter -> t -> unit val pp_short: Format.formatter -> t -> unit type Base48.data += Hash of t - val kind: Base48.kind option + val b48check_encoding: t Base48.encoding + end (** {2 Building Hashes} *******************************************************) @@ -43,14 +56,30 @@ end (** The parameters for creating a new Hash type using {!Make_SHA256}. Both {!name} and {!title} are only informative, used in error messages and serializers. *) + module type Name = sig val name : string val title : string - val prefix : string option +end + +module type PrefixedName = sig + include Name + val b48check_prefix : string end (** Builds a new Hash type using Sha256. *) -module Make_SHA256 (Name:Name) : HASH + +module Make_minimal_SHA256 (Name : Name) : MINIMAL_HASH +module Make_SHA256 + (Register : sig + val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> Base48.data) -> + 'a Base48.encoding + end) + (Name : PrefixedName) : HASH (** Builds a Set of values of some Hash type. *) module Hash_set (Hash : HASH) : sig @@ -80,3 +109,4 @@ module Operation_hash_map : module type of Hash_map (Operation_hash) module Protocol_hash : HASH module Protocol_hash_set : Set.S with type elt = Protocol_hash.t module Protocol_hash_map : module type of Hash_map (Protocol_hash) + diff --git a/src/proto/environment/persist.mli b/src/proto/environment/persist.mli index e7fc1d792..da710368a 100644 --- a/src/proto/environment/persist.mli +++ b/src/proto/environment/persist.mli @@ -13,12 +13,12 @@ type value = MBytes.t module type STORE = sig type t val mem: t -> key -> bool Lwt.t + val dir_mem: t -> key -> bool Lwt.t val get: t -> key -> value option Lwt.t val set: t -> key -> value -> t Lwt.t val del: t -> key -> t Lwt.t val list: t -> key list -> key list Lwt.t val remove_rec: t -> key -> t Lwt.t - val keys: t -> key list Lwt.t end @@ -196,3 +196,14 @@ module MakeBufferedPersistentTypedMap and type key := K.t and type value := T.value and module Map := Map + +module MakeHashResolver + (Store : sig + type t + val dir_mem: t -> key -> bool Lwt.t + val list: t -> key list -> key list Lwt.t + val prefix: string list + end) + (H: Hash.HASH) : sig + val resolve : Store.t -> string -> H.t list Lwt.t +end diff --git a/src/utils/base48.ml b/src/utils/base48.ml index 080951c68..b5ab98861 100644 --- a/src/utils/base48.ml +++ b/src/utils/base48.ml @@ -10,6 +10,7 @@ open Utils let (>>=) = Lwt.bind +let (>|=) = Lwt.(>|=) let decode_alphabet alphabet = let str = Bytes.make 256 '\255' in @@ -102,83 +103,131 @@ let safe_decode ?alphabet s = type data = .. -type kind = - Kind : { prefix: string; - read: data -> string option ; - build: string -> data ; - mutable resolver: string -> string list Lwt.t ; - } -> kind +type 'a encoding = { + prefix: string; + to_raw: 'a -> string ; + of_raw: string -> 'a option ; + wrap: 'a -> data ; +} -let kinds = ref ([] : kind list) +let simple_decode ?alphabet { prefix ; of_raw } s = + safe_decode ?alphabet s |> + remove_prefix ~prefix |> + Utils.apply_option ~f:of_raw -exception Unknown_prefix +let simple_encode ?alphabet { prefix ; to_raw } d = + safe_encode ?alphabet (prefix ^ to_raw d) -let decode ?alphabet s = - let rec find s = function - | [] -> raise Unknown_prefix - | Kind { prefix ; build } :: kinds -> - match remove_prefix ~prefix s with - | None -> find s kinds - | Some msg -> build msg in - let s = safe_decode ?alphabet s in - find s !kinds +type registred_encoding = Encoding : 'a encoding -> registred_encoding -exception Unregistred_kind +module MakeEncodings(E: sig + val encodings: registred_encoding list + end) = struct -let encode ?alphabet s = - let rec find s = function - | [] -> raise Unregistred_kind - | Kind { prefix ; read } :: kinds -> - match read s with - | None -> find s kinds - | Some msg -> safe_encode ?alphabet (prefix ^ msg) in - try find s !kinds - with Not_found -> raise Unknown_prefix + let encodings = ref E.encodings -let default_resolver _ = Lwt.return_nil + let ambiguous_prefix prefix encodings = + List.exists (fun (Encoding { prefix = s }) -> + remove_prefix s prefix <> None || + remove_prefix prefix s <> None) + encodings -let register ~prefix ~read ~build = - match List.find (fun (Kind {prefix=s}) -> remove_prefix s prefix <> None || remove_prefix prefix s <> None) !kinds with - | exception Not_found -> - let kind = - Kind { prefix ; read ; build ; resolver = default_resolver } in - kinds := kind :: !kinds ; - kind - | Kind { prefix = s } -> - Format.kasprintf - Pervasives.failwith - "Base48.register: Conflicting prefixes: %S and %S." prefix s + let register_encoding ~prefix ~to_raw ~of_raw ~wrap = + if ambiguous_prefix prefix !encodings then + Format.ksprintf invalid_arg + "Base48.register_encoding: duplicate prefix: %S" prefix ; + let encoding = { prefix ; to_raw ; of_raw ; wrap } in + encodings := Encoding encoding :: !encodings ; + encoding -let register_resolver (Kind k) resolver = k.resolver <- resolver + let decode ?alphabet s = + let rec find s = function + | [] -> None + | Encoding { prefix ; of_raw ; wrap } :: encodings -> + match remove_prefix ~prefix s with + | None -> find s encodings + | Some msg -> of_raw msg |> Utils.map_option ~f:wrap in + let s = safe_decode ?alphabet s in + find s !encodings + +end + +type 'a resolver = + Resolver : { + encoding: 'h encoding ; + resolver: 'a -> string -> 'h list Lwt.t ; + } -> 'a resolver + +module MakeResolvers(R: sig + type context + val encodings: registred_encoding list ref + end) = struct + + let resolvers = ref [] + + let register_resolver + (type a) + (encoding : a encoding) + (resolver : R.context -> string -> a list Lwt.t) = + try + resolvers := Resolver { encoding ; resolver } :: !resolvers + with Not_found -> + invalid_arg "Base48.register_resolver: unregistred encodings" + + type context = R.context + + let complete ?alphabet context request = + (* One may extract from the prefix of a Base48-encoded value, a + prefix of the original encoded value. Given that `48 = 3 * 2^4`, + every "digits" in the Base48-prefix (i.e. a "bytes" in its ascii + representation), provides for sure 4 bits of the original data. + Hence, when we decode a prefix of a Base48-encoded value of + length `n`, the `n/2` first bytes of the decoded value are (for + sure) a prefix of the original value. *) + let n = String.length request in + let s = raw_decode request ?alphabet in + let partial = String.sub s 0 (n / 2) in + let rec find s = function + | [] -> Lwt.return_nil + | Resolver { encoding ; resolver } :: resolvers -> + match remove_prefix ~prefix:encoding.prefix s with + | None -> find s resolvers + | Some msg -> + resolver context msg >|= fun msgs -> + filter_map + (fun msg -> + let res = simple_encode encoding ?alphabet msg in + Utils.remove_prefix ~prefix:request res |> + Utils.map_option ~f:(fun _ -> res)) + msgs in + find partial !resolvers + +end + +include MakeEncodings(struct let encodings = [] end) +include MakeResolvers(struct + type context = unit + let encodings = encodings + end) + +let register_resolver enc f = register_resolver enc (fun () s -> f s) +let complete ?alphabet s = complete ?alphabet () s + +module Make(C: sig type context end) = struct + include MakeEncodings(struct let encodings = !encodings end) + include MakeResolvers(struct + type context = C.context + let encodings = encodings + end) +end module Prefix = struct let block_hash = "\000" let operation_hash = "\001" let protocol_hash = "\002" - let public_key_hash = "\003" - let public_key = "\004" - let secret_key = "\005" - let signature = "\006" + let ed25519_public_key_hash = "\003" + let ed25519_public_key = "\012" + let ed25519_secret_key = "\013" + let ed25519_signature = "\014" let protocol_prefix = "\015" end - -let decode_partial ?alphabet request = - let n = String.length request in - let s = raw_decode request ?alphabet in - let partial = String.sub s 0 (n/2) in - let rec find s = function - | [] -> Lwt.return_nil - | Kind { prefix ; build ; resolver } :: kinds -> - match remove_prefix ~prefix s with - | None -> find s kinds - | Some msg -> - resolver msg >>= fun msgs -> - let candidates = List.map build msgs in - Lwt.return @@ - List.filter - (fun data -> - match Utils.remove_prefix ~prefix:request (encode data) with - | None -> false - | Some _ -> true) - candidates in - find partial !kinds diff --git a/src/utils/base48.mli b/src/utils/base48.mli index 8877fe83b..26a1f7922 100644 --- a/src/utils/base48.mli +++ b/src/utils/base48.mli @@ -1,4 +1,3 @@ - (**************************************************************************) (* *) (* Copyright (c) 2014 - 2016. *) @@ -8,34 +7,131 @@ (* *) (**************************************************************************) +(** {1 Prefixed Base48Check encodings} *) + +(** Like Bitcoin's Base58Check, all the data encoded in Tezos are + prefixed with a constant which depends on the kind of encoded + data. + + The [Prefix] exports all the prefix used by the Tezos Shell. Each + version of the economical protocol might complete this list. + + Unlike Bitcoin's Base58Check, the prefix in the unencoded-data + is visible in the encoded data. + +*) +module Prefix : sig + + val block_hash: string + (** Prefix for block hashes: "\000". + (in Base48: "e" "f" or "g") *) + + val operation_hash: string + (** Prefix for operation hashes: "\001". + (in Base48: "E" "F" or "G") *) + + val protocol_hash: string + (** Prefix for protocol-version hashes: "\002". + (in Base48: "2" "3" or "4") *) + + val ed25519_public_key_hash: string + (** Prefix for Ed25519 public key hashes: "\003". *) + + val ed25519_public_key: string + (** Prefix for Ed25519 public key: "\012". *) + + val ed25519_secret_key: string + (** Prefix for Ed25519 secret key: "\013". *) + + val ed25519_signature: string + (** Prefix for Ed25519 signature key: "\014". *) + + val protocol_prefix: string + (** Prefix for all the encodings defined by economical protocol: + "\015". *) + +end + +(** An extensible sum-type for decoded data: one case per known + "prefix". See for instance [Hash.Block_hash.Hash] or + [Environment.Ed25519.Public_key_hash]. *) +type data = .. + +(** Abstract representation of registred encodings. The type paramater + is the type of the encoded data, for instance [Hash.Block_hash.t]. *) +type 'a encoding = private { + prefix: string; + to_raw: 'a -> string ; + of_raw: string -> 'a option ; + wrap: 'a -> data ; +} + +(** Register a new encoding. The function might raise `Invalid_arg` if + the provided [prefix] overlap with a previously registred + prefix. The [to_raw] and [of_raw] are the ad-hoc + serialisation/deserialisation for the data. The [wrap] should wrap + the deserialised value into the extensible sum-type [data] (see + the generic function [decode]). *) +val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> data) -> + 'a encoding + +(** Encoder for a given kind of data. *) +val simple_encode: ?alphabet:string -> 'a encoding -> 'a -> string + +(** Decoder for a given kind of data. It returns [None] when + the decoded data does not start with the expected prefix. *) +val simple_decode: ?alphabet:string -> 'a encoding -> string -> 'a option + +(** Generic decoder. It returns [None] when the decoded data does + not start with a registred prefix. *) +val decode: ?alphabet:string -> string -> data option + +(** {2 Completion of partial Base48Check value} *) + +(** Register a (global) resolver for a previsously + registred kind af data. *) +val register_resolver: 'a encoding -> (string -> 'a list Lwt.t) -> unit + +(** Try to complete a prefix of a Base48Check encoded data, by using + the previously registered resolver associated to this kind of + data. Note that a prefix of [n] characters of a Base48-encoded + value provides at least [n/2] bytes of a prefix of the original value. *) +val complete: ?alphabet:string -> string -> string list Lwt.t + +(** {1 Low-level: distinct registering function for economical protocol} *) + +(** See [src/proto/environment/base48.mli]} for an inlined + documentation. *) +module Make(C: sig type context end) : sig + + val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> data) -> + 'a encoding + + val decode: ?alphabet:string -> string -> data option + + val register_resolver: + 'a encoding -> (C.context -> string -> 'a list Lwt.t) -> unit + + val complete: + ?alphabet:string -> C.context -> string -> string list Lwt.t + +end + +(** {2 Low-level Base48Check encodings} *) + +(** Base48Check-encoding/decoding functions (with error detections). *) val safe_encode: ?alphabet:string -> string -> string val safe_decode: ?alphabet:string -> string -> string -type data = .. +(** Base48-encoding/decoding functions (without error detections). *) +val raw_encode: ?alphabet:string -> string -> string +val raw_decode: ?alphabet:string -> string -> string -val decode: ?alphabet:string -> string -> data -val encode: ?alphabet:string -> data -> string - -val decode_partial: ?alphabet:string -> string -> data list Lwt.t - -type kind - -val register: - prefix:string -> - read:(data -> string option) -> - build:(string -> data) -> - kind - -val register_resolver: - kind -> (string -> string list Lwt.t) -> unit - -module Prefix : sig - val block_hash: string - val operation_hash: string - val protocol_hash: string - val public_key_hash: string - val public_key: string - val secret_key: string - val signature: string - val protocol_prefix: string -end diff --git a/src/utils/hash.ml b/src/utils/hash.ml index 5126cf506..567ae16c9 100644 --- a/src/utils/hash.ml +++ b/src/utils/hash.ml @@ -15,9 +15,13 @@ open Utils (*-- Signatures -------------------------------------------------------------*) -module type HASH = sig +module type MINIMAL_HASH = sig + type t + val name: string + val title: string + val hash_bytes: MBytes.t list -> t val hash_string: string list -> t val size: int (* in bytes *) @@ -27,9 +31,6 @@ module type HASH = sig val to_raw: t -> string val of_hex: string -> t val to_hex: t -> string - val of_b48check: string -> t - val to_b48check: t -> string - val to_short_b48check: t -> string val to_bytes: t -> MBytes.t val of_bytes: MBytes.t -> t val read: MBytes.t -> int -> t @@ -38,25 +39,42 @@ module type HASH = sig val of_path: string list -> t val prefix_path: string -> string list val path_len: int + +end + +module type HASH = sig + + include MINIMAL_HASH + + val of_b48check: string -> t + val to_b48check: t -> string + val to_short_b48check: t -> string val encoding: t Data_encoding.t val pp: Format.formatter -> t -> unit val pp_short: Format.formatter -> t -> unit type Base48.data += Hash of t - val kind: Base48.kind option + val b48check_encoding: t Base48.encoding + end module type Name = sig - val name : string - val title : string - val prefix : string option + val name: string + val title: string +end + +module type PrefixedName = sig + include Name + val b48check_prefix: string end (*-- Type specific Hash builder ---------------------------------------------*) -module Make_SHA256 (K : Name) = struct +module Make_minimal_SHA256 (K : Name) = struct type t = string + include K + let size = 32 (* SHA256 *) let of_raw s = @@ -72,25 +90,6 @@ module Make_SHA256 (K : Name) = struct let of_hex s = of_raw (Hex_encode.hex_decode s) let to_hex s = Hex_encode.hex_encode s - type Base48.data += Hash of t - - let kind = - Utils.map_option - K.prefix - ~f:(fun prefix -> - Base48.register - ~prefix - ~read:(function Hash x -> Some x | _ -> None) - ~build:(fun x -> Hash x)) - - let of_b48check s = - match Base48.decode s with - | Hash x -> x - | _ -> Format.kasprintf failwith "Unexpected hash (%s)" K.name - let to_b48check s = Base48.encode (Hash s) - - let to_short_b48check s = String.sub (to_b48check s) 0 12 - let compare = String.compare let equal : t -> t -> bool = (=) @@ -143,12 +142,12 @@ module Make_SHA256 (K : Name) = struct let equal = equal end) - let path_len = 5 + let path_len = 6 let to_path key = let key = to_hex key in [ String.sub key 0 2 ; String.sub key 2 2 ; String.sub key 4 2 ; String.sub key 6 2 ; - String.sub key 8 (size * 2 - 8) ] + String.sub key 8 2 ; String.sub key 10 (size * 2 - 10) ] let of_path path = let path = String.concat "" path in of_hex path @@ -160,11 +159,41 @@ module Make_SHA256 (K : Name) = struct and p2 = if len >= 4 then String.sub p 2 2 else "" and p3 = if len >= 6 then String.sub p 4 2 else "" and p4 = if len >= 8 then String.sub p 6 2 else "" - and p5 = if len > 8 then String.sub p 8 (len - 8) else "" in - [ p1 ; p2 ; p3 ; p4 ; p5 ] + and p5 = if len >= 10 then String.sub p 8 2 else "" + and p6 = if len > 10 then String.sub p 10 (len - 10) else "" in + [ p1 ; p2 ; p3 ; p4 ; p5 ; p6 ] + +end + +module Make_SHA256 (R : sig + val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> Base48.data) -> + 'a Base48.encoding + end) (K : PrefixedName) = struct + + include Make_minimal_SHA256(K) (* Serializers *) + type Base48.data += Hash of t + + let b48check_encoding = + R.register_encoding + ~prefix: K.b48check_prefix + ~wrap: (fun x -> Hash x) + ~of_raw:(fun s -> Some s) ~to_raw + + let of_b48check s = + match Base48.simple_decode b48check_encoding s with + | Some x -> x + | None -> Format.kasprintf failwith "Unexpected hash (%s)" K.name + let to_b48check s = Base48.simple_encode b48check_encoding s + + let to_short_b48check s = String.sub (to_b48check s) 0 12 + let encoding = let open Data_encoding in splitted @@ -219,10 +248,10 @@ module Hash_table (Hash : HASH) (*-- Pre-instanciated hashes ------------------------------------------------*) module Block_hash = - Make_SHA256 (struct + Make_SHA256 (Base48) (struct let name = "Block_hash" let title = "A Tezos block ID" - let prefix = Some Base48.Prefix.block_hash + let b48check_prefix = Base48.Prefix.block_hash end) module Block_hash_set = Hash_set (Block_hash) @@ -230,10 +259,10 @@ module Block_hash_map = Hash_map (Block_hash) module Block_hash_table = Hash_table (Block_hash) module Operation_hash = - Make_SHA256 (struct + Make_SHA256 (Base48) (struct let name = "Operation_hash" let title = "A Tezos operation ID" - let prefix = Some Base48.Prefix.operation_hash + let b48check_prefix = Base48.Prefix.operation_hash end) module Operation_hash_set = Hash_set (Operation_hash) @@ -241,10 +270,10 @@ module Operation_hash_map = Hash_map (Operation_hash) module Operation_hash_table = Hash_table (Operation_hash) module Protocol_hash = - Make_SHA256 (struct + Make_SHA256 (Base48) (struct let name = "Protocol_hash" let title = "A Tezos protocol ID" - let prefix = Some Base48.Prefix.protocol_hash + let b48check_prefix = Base48.Prefix.protocol_hash end) module Protocol_hash_set = Hash_set (Protocol_hash) diff --git a/src/utils/hash.mli b/src/utils/hash.mli index 08eba0e71..220a00431 100644 --- a/src/utils/hash.mli +++ b/src/utils/hash.mli @@ -17,9 +17,14 @@ various kinds of hashes in the system at typing time. Each type is equipped with functions to use it as is of as keys in the database or in memory sets and maps. *) -module type HASH = sig + +module type MINIMAL_HASH = sig + type t + val name: string + val title: string + val hash_bytes: MBytes.t list -> t val hash_string: string list -> t val size: int (* in bytes *) @@ -29,9 +34,6 @@ module type HASH = sig val to_raw: t -> string val of_hex: string -> t val to_hex: t -> string - val of_b48check: string -> t - val to_b48check: t -> string - val to_short_b48check: t -> string val to_bytes: t -> MBytes.t val of_bytes: MBytes.t -> t val read: MBytes.t -> int -> t @@ -40,11 +42,22 @@ module type HASH = sig val of_path: string list -> t val prefix_path: string -> string list val path_len: int + +end + +module type HASH = sig + + include MINIMAL_HASH + + val of_b48check: string -> t + val to_b48check: t -> string + val to_short_b48check: t -> string val encoding: t Data_encoding.t val pp: Format.formatter -> t -> unit val pp_short: Format.formatter -> t -> unit type Base48.data += Hash of t - val kind: Base48.kind option + val b48check_encoding: t Base48.encoding + end (** {2 Building Hashes} *******************************************************) @@ -52,14 +65,29 @@ end (** The parameters for creating a new Hash type using {!Make_SHA256}. Both {!name} and {!title} are only informative, used in error messages and serializers. *) + module type Name = sig val name : string val title : string - val prefix : string option +end + +module type PrefixedName = sig + include Name + val b48check_prefix : string end (** Builds a new Hash type using Sha256. *) -module Make_SHA256 (Name:Name) : HASH +module Make_minimal_SHA256 (Name : Name) : MINIMAL_HASH +module Make_SHA256 + (Register : sig + val register_encoding: + prefix: string -> + to_raw: ('a -> string) -> + of_raw: (string -> 'a option) -> + wrap: ('a -> Base48.data) -> + 'a Base48.encoding + end) + (Name : PrefixedName) : HASH (** Builds a Set of values of some Hash type. *) module Hash_set (Hash : HASH) : sig @@ -103,3 +131,4 @@ module Protocol_hash : HASH module Protocol_hash_set : module type of Hash_set (Protocol_hash) module Protocol_hash_map : module type of Hash_map (Protocol_hash) module Protocol_hash_table : module type of Hash_table (Protocol_hash) + diff --git a/src/utils/utils.ml b/src/utils/utils.ml index d4ed38aeb..963b066fb 100644 --- a/src/utils/utils.ml +++ b/src/utils/utils.ml @@ -96,6 +96,10 @@ let map_option ~f = function | None -> None | Some x -> Some (f x) +let apply_option ~f = function + | None -> None + | Some x -> f x + let iter_option ~f = function | None -> () | Some x -> f x diff --git a/src/utils/utils.mli b/src/utils/utils.mli index 3d87afcad..0105ffaa9 100644 --- a/src/utils/utils.mli +++ b/src/utils/utils.mli @@ -27,6 +27,7 @@ val split_path: string -> string list val split: char -> ?limit: int -> string -> string list val map_option: f:('a -> 'b) -> 'a option -> 'b option +val apply_option: f:('a -> 'b option) -> 'a option -> 'b option val iter_option: f:('a -> unit) -> 'a option -> unit val unopt: 'a -> 'a option -> 'a val unopt_list: 'a option list -> 'a list diff --git a/test/lib/assert.ml b/test/lib/assert.ml index a86434461..8201fdf82 100644 --- a/test/lib/assert.ml +++ b/test/lib/assert.ml @@ -27,11 +27,9 @@ let equal_block_hash_list ?msg l1 l2 = let pr_block_hash = Block_hash.to_short_b48check in Assert.make_equal_list ?msg Block_hash.equal pr_block_hash l1 l2 -let equal_base48_list ?msg l1 l2 = +let equal_string_list ?msg l1 l2 = let msg = format_msg msg in - let pr_base48 = Base48.encode in - (* TODO do not use polymorphic equality ! *) - Assert.make_equal_list ?msg (=) pr_base48 l1 l2 + Assert.make_equal_list ?msg (=) (fun x -> x) l1 l2 let equal_string_option ?msg o1 o2 = let msg = format_msg msg in diff --git a/test/lib/assert.mli b/test/lib/assert.mli index de31ce4d7..28ee6a19f 100644 --- a/test/lib/assert.mli +++ b/test/lib/assert.mli @@ -20,8 +20,8 @@ val equal_persist_list : val equal_block_hash_list : ?msg:string -> Block_hash.t list -> Block_hash.t list -> unit -val equal_base48_list : - ?msg:string -> Base48.data list -> Base48.data list -> unit +val equal_string_list : + ?msg:string -> string list -> string list -> unit val equal_string_option : ?msg:string -> string option -> string option -> unit diff --git a/test/test_store.ml b/test/test_store.ml index 02977949d..f824fbe8c 100644 --- a/test/test_store.ml +++ b/test/test_store.ml @@ -121,12 +121,12 @@ let test_expand (s: Store.store) = Block.full_set s bh2 b2 >>= fun () -> Block.full_set s bh3 b3 >>= fun () -> Block.full_set s bh3' b3 >>= fun () -> - Base48.decode_partial (Block_hash.to_short_b48check bh1) >>= fun res -> - Assert.equal_base48_list ~msg:__LOC__ res [Block_hash.Hash bh1] ; - Base48.decode_partial (Block_hash.to_short_b48check bh2) >>= fun res -> - Assert.equal_base48_list ~msg:__LOC__ res [Block_hash.Hash bh2] ; - Base48.decode_partial (Block_hash.to_short_b48check bh3) >>= fun res -> - Assert.equal_base48_list ~msg:__LOC__ res [Block_hash.Hash bh3] ; + Base48.complete (Block_hash.to_short_b48check bh1) >>= fun res -> + Assert.equal_string_list ~msg:__LOC__ res [Block_hash.to_b48check bh1] ; + Base48.complete (Block_hash.to_short_b48check bh2) >>= fun res -> + Assert.equal_string_list ~msg:__LOC__ res [Block_hash.to_b48check bh2] ; + Base48.complete (Block_hash.to_short_b48check bh3) >>= fun res -> + Assert.equal_string_list ~msg:__LOC__ res [Block_hash.to_b48check bh3] ; Lwt.return_unit)