diff --git a/src/proto_alpha/lib_protocol/contract_storage.ml b/src/proto_alpha/lib_protocol/contract_storage.ml index 4e17ed75ccb66d1196853e61327d6246276f9c05..05b6cdba17d12d749f71e8ccdfbe2b65b357064d 100644 --- a/src/proto_alpha/lib_protocol/contract_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_storage.ml @@ -441,10 +441,17 @@ let delete c contract = do not exist). An implicit contract deletion should not cost extra gas. *) Contract_delegate_storage.unlink c contract >>=? fun c -> - Storage.Contract.Spendable_balance.remove_existing c contract - >>=? fun c -> - Contract_manager_storage.remove_existing c contract >>=? fun c -> - Storage.Contract.Counter.remove_existing c contract + Storage.Contract.with_local_context + ~add_back:true + c + contract + (fun local -> + Storage.Contract.Spendable_balance.Local.remove_existing local + >>=? fun local -> + Storage.Contract.Manager.Local.remove_existing local >>=? fun local -> + Storage.Contract.Counter.Local.remove_existing local >|=? fun local -> + (local, ())) + >|=? fun (c, ()) -> c let allocated c contract = Storage.Contract.Spendable_balance.mem c contract diff --git a/src/proto_alpha/lib_protocol/global_constants_storage.ml b/src/proto_alpha/lib_protocol/global_constants_storage.ml index be7b9dc62f46ec3f8b1acd54d673b81ba26c1bf1..885ad9f7358c125b86c36d9a65d3ce22f09bc3e5 100644 --- a/src/proto_alpha/lib_protocol/global_constants_storage.ml +++ b/src/proto_alpha/lib_protocol/global_constants_storage.ml @@ -143,6 +143,13 @@ let get context hash = | None -> fail Nonexistent_global | Some value -> return (context, value) +let get_local local hash = + Storage.Global_constants.Map.Local.find local hash + >>=? fun (context, value) -> + match value with + | None -> fail Nonexistent_global + | Some value -> return (context, value) + let expr_to_address_in_context context expr = let lexpr = Script_repr.lazy_expr expr in Raw_context.consume_gas context @@ Script_repr.force_bytes_cost lexpr @@ -160,27 +167,28 @@ let node_too_large node = nodes > Constants_repr.max_micheline_node_count || string_bytes + z_bytes > Constants_repr.max_micheline_bytes_limit) -let expand_node context node = +let expand_node_local local node = (* We charge for traversing the top-level node at the beginning. Inside the loop, we charge for traversing each new constant that gets expanded. *) - Raw_context.consume_gas - context + Raw_context.Local_context.consume_gas + local (Gas_costs.expand_no_constants_branch_cost node) - >>?= fun context -> + >>?= fun local -> bottom_up_fold_cps (* We carry a Boolean representing whether we had to do any expansions or not. *) - (context, Expr_hash_map.empty, false) + (local, Expr_hash_map.empty, false) node - (fun (context, _, did_expansion) node -> - return (context, node, did_expansion)) - (fun (context, map, did_expansion) node k -> + (fun (local, _, did_expansion) node -> return (local, node, did_expansion)) + (fun (local, map, did_expansion) node k -> match node with | Prim (_, H_constant, args, annot) -> ( (* Charge for validating the b58check hash. *) - Raw_context.consume_gas context Gas_costs.expand_constants_branch_cost - >>?= fun context -> + Raw_context.Local_context.consume_gas + local + Gas_costs.expand_constants_branch_cost + >>?= fun local -> match (args, annot) with (* A constant Prim should always have a single String argument, being a properly formatted hash. *) @@ -191,30 +199,36 @@ let expand_node context node = match Expr_hash_map.find hash map with | Some node -> (* Charge traversing the newly retrieved node *) - Raw_context.consume_gas - context + Raw_context.Local_context.consume_gas + local (Gas_costs.expand_no_constants_branch_cost node) - >>?= fun context -> k (context, map, true) node + >>?= fun local -> k (local, map, true) node | None -> - get context hash >>=? fun (context, expr) -> + get_local local hash >>=? fun (local, expr) -> (* Charge traversing the newly retrieved node *) let node = root expr in - Raw_context.consume_gas - context + Raw_context.Local_context.consume_gas + local (Gas_costs.expand_no_constants_branch_cost node) - >>?= fun context -> - k (context, Expr_hash_map.add hash node map, true) node)) + >>?= fun local -> + k (local, Expr_hash_map.add hash node map, true) node)) | _ -> fail Badly_formed_constant_expression) | Int _ | String _ | Bytes _ | Prim _ | Seq _ -> - k (context, map, did_expansion) node) - >>=? fun (context, node, did_expansion) -> + k (local, map, did_expansion) node) + >>=? fun (local, node, did_expansion) -> if did_expansion then (* Gas charged during expansion is at least proportional to the size of the resulting node so the execution time of [node_too_large] is already covered. *) if node_too_large node then fail Expression_too_large - else return (context, node) - else return (context, node) + else return (local, node) + else return (local, node) + +let expand_node context node = + Storage.Global_constants.Map.with_local_context + ~add_back:false + context + (fun local -> expand_node_local local node) let expand context expr = expand_node context (root expr) >|=? fun (context, node) -> diff --git a/src/proto_alpha/lib_protocol/lazy_storage_diff.ml b/src/proto_alpha/lib_protocol/lazy_storage_diff.ml index 791b3f828448f71a918fbe489b848a6e4e559c6b..7ad9016e67eb649d55ea3ed39cdc9610dd93e9e4 100644 --- a/src/proto_alpha/lib_protocol/lazy_storage_diff.ml +++ b/src/proto_alpha/lib_protocol/lazy_storage_diff.ml @@ -118,7 +118,8 @@ module Big_map = struct Storage.Big_map.Key_type.init ctxt id key_type >>=? fun ctxt -> Storage.Big_map.Value_type.init ctxt id value_type - let apply_update ctxt ~id + (* [local_ctxt] points to the directory of the big_map *) + let apply_update local_ctxt { key = _key_is_shown_only_on_the_receipt_in_print_big_map_diff; key_hash; @@ -126,27 +127,32 @@ module Big_map = struct } = match value with | None -> - Storage.Big_map.Contents.remove (ctxt, id) key_hash - >|=? fun (ctxt, freed, existed) -> + Storage.Big_map.Contents.Local.remove local_ctxt key_hash + >|=? fun (local_ctxt, freed, existed) -> let freed = if existed then freed + bytes_size_for_big_map_key else freed in - (ctxt, Z.of_int ~-freed) + (local_ctxt, Z.of_int ~-freed) | Some v -> - Storage.Big_map.Contents.add (ctxt, id) key_hash v - >|=? fun (ctxt, size_diff, existed) -> + Storage.Big_map.Contents.Local.add local_ctxt key_hash v + >|=? fun (local_ctxt, size_diff, existed) -> let size_diff = if existed then size_diff else size_diff + bytes_size_for_big_map_key in - (ctxt, Z.of_int size_diff) + (local_ctxt, Z.of_int size_diff) let apply_updates ctxt ~id updates = - List.fold_left_es - (fun (ctxt, size) update -> - apply_update ctxt ~id update >|=? fun (ctxt, added_size) -> - (ctxt, Z.add size added_size)) - (ctxt, Z.zero) - updates + Storage.Big_map.Contents.with_local_context + ~add_back:true + (ctxt, id) + (fun local_ctxt -> + List.fold_left_es + (fun (local_ctxt, size) update -> + apply_update local_ctxt update >|=? fun (local_ctxt, added_size) -> + (local_ctxt, Z.add size added_size)) + (local_ctxt, Z.zero) + updates) + >|=? fun ((ctxt, _id), size_diff) -> (ctxt, size_diff) include Storage.Big_map end diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index a34b0145576cbea7c545515a79ace8e55dadbc1c..79caf278d4d3ffb9865eac69bd87b7894e975607 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -1021,12 +1021,23 @@ type value = bytes type tree = Context.tree +(* The type for relative context accesses instead from the root. In order for + the carbonated storage functions to consume the gas, this has gas infomation +*) +type local_context = { + tree : tree; + path : key; + remaining_operation_gas : Gas_limit_repr.Arith.fp; + unlimited_operation_gas : bool; +} + module type T = Raw_context_intf.T with type root := root and type key := key and type value := value and type tree := tree + and type local_context := local_context let mem ctxt k = Context.mem (context ctxt) k @@ -1553,3 +1564,104 @@ module Dal = struct let shards ctxt ~endorser = compute_shards ~index:0 ctxt ~endorser end + +let with_local_context ~add_back ctxt key f = + (find_tree ctxt key >|= function None -> Tree.empty ctxt | Some tree -> tree) + >>= fun tree -> + let local_ctxt = + { + tree; + path = key; + remaining_operation_gas = remaining_operation_gas ctxt; + unlimited_operation_gas = unlimited_operation_gas ctxt; + } + in + f local_ctxt >>=? fun (local_ctxt, res) -> + (if add_back then add_tree ctxt key local_ctxt.tree else Lwt.return ctxt) + >|= fun ctxt -> + update_remaining_operation_gas ctxt local_ctxt.remaining_operation_gas + |> fun ctxt -> + update_unlimited_operation_gas ctxt local_ctxt.unlimited_operation_gas + |> fun ctxt -> ok (ctxt, res) + +module Local_context : sig + include + Raw_context_intf.VIEW + with type t = local_context + and type key := key + and type value := value + and type tree := tree + + val consume_gas : + local_context -> Gas_limit_repr.cost -> local_context tzresult + + val absolute_key : local_context -> key -> key +end = struct + type t = local_context + + let consume_gas local cost = + match Gas_limit_repr.raw_consume local.remaining_operation_gas cost with + | Some gas_counter -> Ok {local with remaining_operation_gas = gas_counter} + | None -> + if local.unlimited_operation_gas then ok local + else error Operation_quota_exceeded + + let tree local = local.tree + + let update_root_tree local tree = {local with tree} + + let absolute_key local key = local.path @ key + + let find local = Tree.find (tree local) + + let find_tree local = Tree.find_tree (tree local) + + let mem local = Tree.mem (tree local) + + let mem_tree local = Tree.mem_tree (tree local) + + let get local = Tree.get (tree local) + + let get_tree local = Tree.get_tree (tree local) + + let update local key b = + Tree.update (tree local) key b >|=? update_root_tree local + + let update_tree local key b = + Tree.update_tree (tree local) key b >|=? update_root_tree local + + let init local key b = + Tree.init (tree local) key b >|=? update_root_tree local + + let init_tree local key t = + Tree.init_tree (tree local) key t >|=? update_root_tree local + + let add local i b = Tree.add (tree local) i b >|= update_root_tree local + + let add_tree local i t = + Tree.add_tree (tree local) i t >|= update_root_tree local + + let remove local i = Tree.remove (tree local) i >|= update_root_tree local + + let remove_existing local key = + Tree.remove_existing (tree local) key >|=? update_root_tree local + + let remove_existing_tree local key = + Tree.remove_existing_tree (tree local) key >|=? update_root_tree local + + let add_or_remove local key vopt = + Tree.add_or_remove (tree local) key vopt >|= update_root_tree local + + let add_or_remove_tree local key topt = + Tree.add_or_remove_tree (tree local) key topt >|= update_root_tree local + + let fold ?depth local key ~order ~init ~f = + Tree.fold ?depth (tree local) key ~order ~init ~f + + let list local ?offset ?length key = + Tree.list (tree local) ?offset ?length key + + let config local = Tree.config (tree local) + + let length local i = Tree.length (tree local) i +end diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index eef60d8c3117c1c550ed074203bf4586e57f8273..8c44aaf2752e18f92738879796c786718b9b8426 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -181,12 +181,15 @@ type value = bytes type tree +type local_context + module type T = Raw_context_intf.T with type root := root and type key := key and type value := value and type tree := tree + and type local_context := local_context include T with type t := t diff --git a/src/proto_alpha/lib_protocol/raw_context_intf.ml b/src/proto_alpha/lib_protocol/raw_context_intf.ml index 39c8b058d78aa7a682d24b1d8e9095c4cb5a2645..6c131a7717d32af905379ff315c3bfd77f768860 100644 --- a/src/proto_alpha/lib_protocol/raw_context_intf.ml +++ b/src/proto_alpha/lib_protocol/raw_context_intf.ml @@ -559,4 +559,32 @@ module type T = sig val check_enough_gas : t -> Gas_limit_repr.cost -> unit tzresult val description : t Storage_description.t + + (** The type for relative context accesses instead from the root. In order for + the carbonated storage functions to consume the gas, this has gas infomation + *) + type local_context + + val with_local_context : + add_back:bool -> + t -> + key -> + (local_context -> (local_context * 'a) tzresult Lwt.t) -> + (t * 'a) tzresult Lwt.t + + module Local_context : sig + include + VIEW + with type t = local_context + and type tree := tree + and type key := key + and type value := value + + type t = local_context + + val consume_gas : + local_context -> Gas_limit_repr.cost -> local_context tzresult + + val absolute_key : local_context -> key -> key + end end diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 637404e7263f23e9e5c08844b89d3e15dfae2693..201296561bc37023b6dfe9e3ec119c7440726226 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -188,6 +188,10 @@ module Contract = struct let list = Indexed_context.keys + type local_context = Indexed_context.local_context + + let with_local_context = Indexed_context.with_local_context + module Spendable_balance = Indexed_context.Make_map (struct @@ -387,7 +391,7 @@ end module Global_constants = struct module Map : - Non_iterable_indexed_carbonated_data_storage + Non_iterable_indexed_carbonated_data_storage_with_local_context with type t := Raw_context.t and type key = Script_expr_hash.t and type value = Script_repr.expr = @@ -539,6 +543,33 @@ module Big_map = struct consume_deserialize_gas ctxt value >|? fun ctxt -> (ctxt, value_opt) let keys_unaccounted = I.keys_unaccounted + + let with_local_context = I.with_local_context + + module Local = struct + include I.Local + + let consume_deserialize_gas local_ctxt value = + Raw_context.Local_context.consume_gas + local_ctxt + (Script_repr.deserialized_cost value) + + let get local_ctxt contract = + I.Local.get local_ctxt contract >>=? fun (local_ctxt, value) -> + Lwt.return + ( consume_deserialize_gas local_ctxt value >|? fun local_ctxt -> + (local_ctxt, value) ) + + let find local_ctxt contract = + I.Local.find local_ctxt contract >>=? fun (local_ctxt, value_opt) -> + Lwt.return + @@ + match value_opt with + | None -> ok (local_ctxt, None) + | Some value -> + consume_deserialize_gas local_ctxt value >|? fun local_ctxt -> + (local_ctxt, value_opt) + end end end diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index c4c00f28d58f8399c3603045adb723dea0d9a467..2020083f1adcf64e6cdfb1812980049fd4e00ab9 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -69,15 +69,25 @@ module Contract : sig val list : Raw_context.t -> Contract_repr.t list Lwt.t + type local_context + + val with_local_context : + add_back:bool -> + Raw_context.t -> + Contract_repr.t -> + (local_context -> (local_context * 'a) tzresult Lwt.t) -> + (Raw_context.t * 'a) tzresult Lwt.t + (** The tez possessed by a contract and that can be used. A contract may also possess tez in frozen deposits. Empty balances (of zero tez) are only allowed for originated contracts, not for implicit ones. *) module Spendable_balance : - Indexed_data_storage + Indexed_data_storage_with_local_context with type key = Contract_repr.t and type value = Tez_repr.t and type t := Raw_context.t + and type local_context := local_context (** If the value is not set, the delegate didn't miss any endorsing opportunity. If it is set, this value is a record of type @@ -99,10 +109,11 @@ module Contract : sig (** The manager of a contract *) module Manager : - Indexed_data_storage + Indexed_data_storage_with_local_context with type key = Contract_repr.t and type value = Manager_repr.t and type t := Raw_context.t + and type local_context := local_context (** The delegate of a contract, if any. *) module Delegate : @@ -148,10 +159,11 @@ module Contract : sig and type t := Raw_context.t module Counter : - Indexed_data_storage + Indexed_data_storage_with_local_context with type key = Contract_repr.t and type value = Z.t and type t := Raw_context.t + and type local_context := local_context module Code : Non_iterable_indexed_carbonated_data_storage @@ -168,14 +180,14 @@ module Contract : sig (** Current storage space in bytes. Includes code, global storage and big map elements. *) module Used_storage_space : - Indexed_data_storage + Indexed_data_storage_with_local_context with type key = Contract_repr.t and type value = Z.t and type t := Raw_context.t (** Maximal space available without needing to burn new fees. *) module Paid_storage_space : - Indexed_data_storage + Indexed_data_storage_with_local_context with type key = Contract_repr.t and type value = Z.t and type t := Raw_context.t @@ -232,7 +244,7 @@ module Big_map : sig module Contents : sig include - Non_iterable_indexed_carbonated_data_storage + Non_iterable_indexed_carbonated_data_storage_with_local_context with type key = Script_expr_hash.t and type value = Script_repr.expr and type t := key @@ -573,7 +585,7 @@ end [Michelson_v1_primitives.H_constant]. *) module Global_constants : sig module Map : - Non_iterable_indexed_carbonated_data_storage + Non_iterable_indexed_carbonated_data_storage_with_local_context with type t := Raw_context.t and type key = Script_expr_hash.t and type value = Script_repr.expr diff --git a/src/proto_alpha/lib_protocol/storage_functors.ml b/src/proto_alpha/lib_protocol/storage_functors.ml index b07dcbe31d9ed40f66ca4e3306a54f8d9c3777e7..912e2e8a41565bec95d846a67a2a40fc1930dd0c 100644 --- a/src/proto_alpha/lib_protocol/storage_functors.ml +++ b/src/proto_alpha/lib_protocol/storage_functors.ml @@ -142,6 +142,11 @@ module Make_subcontext (R : REGISTER) (C : Raw_context.T) (N : NAME) : Storage_description.register_named_subcontext description N.name let length = C.length + + let with_local_context ~add_back ctxt k f = + C.with_local_context ~add_back ctxt (to_key k) f + + module Local_context = C.Local_context end module Make_single_data_storage @@ -463,6 +468,125 @@ module Make_indexed_carbonated_data_storage_INTERNAL let add_or_remove s i v = match v with None -> remove s i | Some v -> add s i v + let with_local_context ~add_back s f = + (* The gas cost for using C.with_local_context are: + - find_tree : To access the directory + - add_tree : `write_access` does not charge the path length. Then no need + to reduce the gas *) + (* Remark: To the consumption costs remain the same, the additional gas cost + when add_back is true is set to zero. + *) + let consume_find_tree_gas c = + let path_length = List.length @@ C.absolute_key c [] in + C.consume_gas c (Storage_costs.read_access ~path_length ~read_bytes:0) + in + let consume_add_tree_gas c = + C.consume_gas c (Storage_costs.write_access ~written_bytes:0) + in + (if not add_back then consume_find_tree_gas s else ok s) >>?= fun s -> + C.with_local_context ~add_back s [] f >>=? fun (s, x) -> + (if not add_back then consume_add_tree_gas s else ok s) >>?= fun s -> + return (s, x) + + module Local = struct + type context = Raw_context.local_context + + let consume_mem_gas local key = + C.Local_context.consume_gas + local + (Storage_costs.read_access ~path_length:(List.length key) ~read_bytes:0) + + let existing_size local i = + C.Local_context.find local (len_key i) >|= function + | None -> ok (0, false) + | Some len -> decode_len_value (len_key i) len >|? fun len -> (len, true) + + let consume_read_gas get local i = + let len_key = len_key i in + get local len_key >>=? fun len -> + Lwt.return + ( decode_len_value len_key len >>? fun read_bytes -> + let cost = + Storage_costs.read_access + ~path_length:(List.length len_key) + ~read_bytes + in + C.Local_context.consume_gas local cost ) + + let consume_serialize_write_gas set local i v = + let bytes = to_bytes v in + let len = Bytes.length bytes in + C.Local_context.consume_gas local (Gas_limit_repr.alloc_mbytes_cost len) + >>?= fun local -> + let cost = Storage_costs.write_access ~written_bytes:len in + C.Local_context.consume_gas local cost >>?= fun local -> + set local (len_key i) (encode_len_value bytes) >|=? fun local -> + (local, bytes) + + let consume_remove_gas del local i = + C.Local_context.consume_gas + local + (Storage_costs.write_access ~written_bytes:0) + >>?= fun local -> del local (len_key i) + + let mem local i = + let key = data_key i in + consume_mem_gas local key >>?= fun local -> + C.Local_context.mem local key >|= fun exists -> ok (local, exists) + + let get local i = + consume_read_gas C.Local_context.get local i >>=? fun local -> + C.Local_context.get local (data_key i) >>=? fun b -> + let key () = C.Local_context.absolute_key local (data_key i) in + Lwt.return (of_bytes ~key b >|? fun v -> (local, v)) + + let find local i = + let key = data_key i in + consume_mem_gas local key >>?= fun local -> + C.Local_context.mem local key >>= fun exists -> + if exists then get local i >|=? fun (local, v) -> (local, Some v) + else return (local, None) + + let update local i v = + existing_size local i >>=? fun (prev_size, _) -> + consume_serialize_write_gas C.Local_context.update local i v + >>=? fun (local, bytes) -> + C.Local_context.update local (data_key i) bytes >|=? fun local -> + let size_diff = Bytes.length bytes - prev_size in + (local, size_diff) + + let init local i v = + consume_serialize_write_gas C.Local_context.init local i v + >>=? fun (local, bytes) -> + C.Local_context.init local (data_key i) bytes >|=? fun local -> + let size = Bytes.length bytes in + (local, size) + + let add local i v = + let add local i v = C.Local_context.add local i v >|= ok in + existing_size local i >>=? fun (prev_size, existed) -> + consume_serialize_write_gas add local i v >>=? fun (local, bytes) -> + add local (data_key i) bytes >|=? fun local -> + let size_diff = Bytes.length bytes - prev_size in + (local, size_diff, existed) + + let remove local i = + let remove local i = C.Local_context.remove local i >|= ok in + existing_size local i >>=? fun (prev_size, existed) -> + consume_remove_gas remove local i >>=? fun local -> + remove local (data_key i) >|=? fun local -> (local, prev_size, existed) + + let remove_existing local i = + existing_size local i >>=? fun (prev_size, _) -> + consume_remove_gas C.Local_context.remove_existing local i + >>=? fun local -> + C.Local_context.remove_existing local (data_key i) >|=? fun local -> + (local, prev_size) + + let add_or_remove local i v = + match v with None -> remove local i | Some v -> add local i v + end + (** Because big map values are not stored under some common key, we have no choice but to fold over all nodes with a path of length [I.path_length] to retrieve actual keys and then paginate. @@ -545,7 +669,7 @@ module Make_indexed_carbonated_data_storage : functor (I : INDEX) (V : VALUE) -> - Non_iterable_indexed_carbonated_data_storage_with_values + Non_iterable_indexed_carbonated_data_storage_with_local_context with type t = C.t and type key = I.t and type value = V.t = @@ -663,6 +787,8 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : type 'a ipath = 'a I.ipath + type local_context = Raw_context.local_context + let clear t = C.remove t [] >|= fun t -> C.project t let fold_keys t ~order ~init ~f = @@ -824,8 +950,20 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : let length c = let t, _i = unpack c in C.length t + + let with_local_context ~add_back c k f = + let t, i = unpack c in + C.with_local_context ~add_back t (to_key i k) f >|=? fun (t, res) -> + (pack t i, res) + + module Local_context = C.Local_context end + let with_local_context ~add_back s i f = + Raw_context.with_local_context ~add_back (pack s i) [] f >|=? fun (c, x) -> + let s, _ = unpack c in + (s, x) + module Make_set (R : REGISTER) (N : NAME) : Data_set_storage with type t = t and type elt = key = struct type t = C.t @@ -878,8 +1016,11 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : end module Make_map (N : NAME) (V : VALUE) : - Indexed_data_storage with type t = t and type key = key and type value = V.t = - struct + Indexed_data_storage_with_local_context + with type t = t + and type key = key + and type value = V.t + and type local_context = local_context = struct type t = C.t type context = t @@ -888,6 +1029,8 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : type value = V.t + type nonrec local_context = local_context + include Make_encoder (V) let mem s i = Raw_context.mem (pack s i) N.name @@ -967,6 +1110,43 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : find c k) (register_named_subcontext Raw_context.description N.name) V.encoding + + module Local = struct + type context = Raw_context.Local_context.t + + let mem local = Raw_context.Local_context.mem local N.name + + let get local = + Raw_context.Local_context.get local N.name >|= fun r -> + let key () = Raw_context.Local_context.absolute_key local N.name in + r >>? of_bytes ~key + + let find local = + Raw_context.Local_context.find local N.name >|= function + | None -> Result.return_none + | Some b -> + let key () = Raw_context.Local_context.absolute_key local N.name in + of_bytes ~key b >|? fun v -> Some v + + let init local v = + Raw_context.Local_context.init local N.name (to_bytes v) + + let update local v = + Raw_context.Local_context.update local N.name (to_bytes v) + + let add local v = Raw_context.Local_context.add local N.name (to_bytes v) + + let add_or_remove local vo = + Raw_context.Local_context.add_or_remove + local + N.name + (Option.map to_bytes vo) + + let remove_existing local = + Raw_context.Local_context.remove_existing local N.name + + let remove local = Raw_context.Local_context.remove local N.name + end end module Make_carbonated_map (N : NAME) (V : VALUE) : diff --git a/src/proto_alpha/lib_protocol/storage_functors.mli b/src/proto_alpha/lib_protocol/storage_functors.mli index e83a1f9da791173b079b1700c6c97939dfbd9ac6..0b1dcbb8edf5e8cdfff5ff588e837c17e8d9a8a1 100644 --- a/src/proto_alpha/lib_protocol/storage_functors.mli +++ b/src/proto_alpha/lib_protocol/storage_functors.mli @@ -84,7 +84,7 @@ module Make_indexed_carbonated_data_storage (C : Raw_context.T) (I : INDEX) (V : VALUE) : - Non_iterable_indexed_carbonated_data_storage_with_values + Non_iterable_indexed_carbonated_data_storage_with_local_context with type t = C.t and type key = I.t and type value = V.t diff --git a/src/proto_alpha/lib_protocol/storage_sigs.ml b/src/proto_alpha/lib_protocol/storage_sigs.ml index d6c0a2886083bc6ce72bf2782e1d894fbbd7c882..b3bdcba53ac6558f4d4fbc20a0a889dd5c5179a8 100644 --- a/src/proto_alpha/lib_protocol/storage_sigs.ml +++ b/src/proto_alpha/lib_protocol/storage_sigs.ml @@ -226,9 +226,43 @@ module type Non_iterable_indexed_carbonated_data_storage_with_values = sig (Raw_context.t * (key * value) list) tzresult Lwt.t end -module type Non_iterable_indexed_carbonated_data_storage_INTERNAL = sig +module type Non_iterable_indexed_carbonated_data_storage_with_local_context = sig include Non_iterable_indexed_carbonated_data_storage_with_values + val with_local_context : + add_back:bool -> + context -> + (Raw_context.local_context -> + (Raw_context.local_context * 'a) tzresult Lwt.t) -> + (context * 'a) tzresult Lwt.t + + module Local : sig + type context = Raw_context.local_context + + val mem : context -> key -> (context * bool) tzresult Lwt.t + + val get : context -> key -> (context * value) tzresult Lwt.t + + val find : context -> key -> (context * value option) tzresult Lwt.t + + val update : context -> key -> value -> (context * int) tzresult Lwt.t + + val init : context -> key -> value -> (context * int) tzresult Lwt.t + + val add : context -> key -> value -> (context * int * bool) tzresult Lwt.t + + val add_or_remove : + context -> key -> value option -> (context * int * bool) tzresult Lwt.t + + val remove_existing : context -> key -> (context * int) tzresult Lwt.t + + val remove : context -> key -> (context * int * bool) tzresult Lwt.t + end +end + +module type Non_iterable_indexed_carbonated_data_storage_INTERNAL = sig + include Non_iterable_indexed_carbonated_data_storage_with_local_context + val fold_keys_unaccounted : context -> order:[`Sorted | `Undefined] -> @@ -269,6 +303,55 @@ module type Indexed_data_storage = sig 'a Lwt.t end +module type Indexed_data_storage_with_local_context = sig + include Indexed_data_storage + + type local_context + + module Local : sig + type context = local_context + + (** Tells if the data is already defined *) + val mem : context -> bool Lwt.t + + (** Retrieve the value from the storage bucket ; returns a + {!Storage_error} if the key is not set or if the deserialisation + fails *) + val get : context -> value tzresult Lwt.t + + (** Retrieves the value from the storage bucket ; returns [None] if + the data is not initialized, or {!Storage_helpers.Storage_error} + if the deserialisation fails *) + val find : context -> value option tzresult Lwt.t + + (** Allocates the storage bucket and initializes it ; returns a + {!Storage_error Existing_key} if the bucket exists *) + val init : context -> value -> context tzresult Lwt.t + + (** Updates the content of the bucket ; returns a {!Storage_Error + Missing_key} if the value does not exists *) + val update : context -> value -> context tzresult Lwt.t + + (** Allocates the data and initializes it with a value ; just + updates it if the bucket exists *) + val add : context -> value -> context Lwt.t + + (** When the value is [Some v], allocates the data and initializes + it with [v] ; just updates it if the bucket exists. When the + value is [None], delete the storage bucket when the value ; does + nothing if the bucket does not exists. *) + val add_or_remove : context -> value option -> context Lwt.t + + (** Delete the storage bucket ; returns a {!Storage_error + Missing_key} if the bucket does not exists *) + val remove_existing : context -> context tzresult Lwt.t + + (** Removes the storage bucket and its contents ; does nothing if + the bucket does not exists *) + val remove : context -> context Lwt.t + end +end + module type Indexed_data_snapshotable_storage = sig type snapshot @@ -393,6 +476,8 @@ module type Indexed_raw_context = sig type 'a ipath + type local_context + val clear : context -> Raw_context.t Lwt.t val fold_keys : @@ -408,11 +493,22 @@ module type Indexed_raw_context = sig val copy : context -> from:key -> to_:key -> context tzresult Lwt.t + val with_local_context : + add_back:bool -> + context -> + key -> + (local_context -> (local_context * 'a) tzresult Lwt.t) -> + (context * 'a) tzresult Lwt.t + module Make_set (_ : REGISTER) (_ : NAME) : Data_set_storage with type t = t and type elt = key module Make_map (_ : NAME) (V : VALUE) : - Indexed_data_storage with type t = t and type key = key and type value = V.t + Indexed_data_storage_with_local_context + with type t = t + and type key = key + and type value = V.t + and type local_context = local_context module Make_carbonated_map (_ : NAME) (V : VALUE) : Non_iterable_indexed_carbonated_data_storage