From 8f95c9f1fed58e69f2083cfadad4436ec659571b Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 16 Jan 2023 13:36:32 +0100 Subject: [PATCH 1/4] WASM: implement generic tick model --- src/lib_webassembly/exec/tick_model.ml | 16 +++++++++++++++ src/lib_webassembly/exec/tick_model.mli | 26 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/lib_webassembly/exec/tick_model.ml create mode 100644 src/lib_webassembly/exec/tick_model.mli diff --git a/src/lib_webassembly/exec/tick_model.ml b/src/lib_webassembly/exec/tick_model.ml new file mode 100644 index 000000000000..7ca7d1bea924 --- /dev/null +++ b/src/lib_webassembly/exec/tick_model.ml @@ -0,0 +1,16 @@ +type tick = Z.t + +let ( + ) = Z.add + +let ( * ) = Z.mul + +let nop = Z.zero + +(* This value has been benchmarked from MemoryCopy. *) +let ticks_per_byte_copied = Z.of_int 48 + +(* This value has been benchmarked from MemoryFill and MemoryInit. *) +let ticks_per_byte_read = Z.of_int 42 + +(* This value has been benchmarked from MemoryFill and MemoryInit. *) +let ticks_per_byte_written = Z.of_int 42 diff --git a/src/lib_webassembly/exec/tick_model.mli b/src/lib_webassembly/exec/tick_model.mli new file mode 100644 index 000000000000..5bff6f489943 --- /dev/null +++ b/src/lib_webassembly/exec/tick_model.mli @@ -0,0 +1,26 @@ +(** This module implements a generic tick model. These values were benchmarked + from `MemoryCopy`, `MemoryFill` and `MemoryInit`. *) + +type tick = Z.t + +(** [t1 + t2] returns the sum of [t1] and [t2] *) +val ( + ) : tick -> tick -> tick + +(** [t1 * t2] returns the product of [t1] and [t2] *) +val ( * ) : tick -> tick -> tick + +(** [nop] consumes zero tick. This is used specifically for + `write_debug`. *) +val nop : tick + +(** [ticks_per_byte_copied] is the consumption for a byte read in the memory and + written in the memory in the context of reading a chunk of bytes. *) +val ticks_per_byte_copied : tick + +(** [ticks_per_byte_read] is the consumption for a byte read from the memory in + the context of reading a chunk of bytes. *) +val ticks_per_byte_read : tick + +(** [ticks_per_byte_written] is the consumption for a byte written in the + memory in the context of writing a chunk of bytes. *) +val ticks_per_byte_written : tick -- GitLab From 523ff35eb0d8398d8eab97c429ff7cfd6dd520e0 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Thu, 5 Jan 2023 17:49:51 +0100 Subject: [PATCH 2/4] WASM/PVM: implement tick model for the host functions --- src/lib_scoru_wasm/host_funcs.ml | 135 +++++++++++++++++------------- src/lib_scoru_wasm/host_funcs.mli | 40 +++++++++ src/lib_webassembly/exec/eval.ml | 7 +- 3 files changed, 121 insertions(+), 61 deletions(-) diff --git a/src/lib_scoru_wasm/host_funcs.ml b/src/lib_scoru_wasm/host_funcs.ml index 3ec2cecf355b..cecadefd9030 100644 --- a/src/lib_scoru_wasm/host_funcs.ml +++ b/src/lib_scoru_wasm/host_funcs.ml @@ -595,7 +595,30 @@ module Aux = struct include Make (Memory_access_interpreter) end -let default_ticks = Z.zero +module Tick_model = struct + include Tezos_webassembly_interpreter.Tick_model + + let read_key_in_memory key_length = + Z.of_int32 key_length * ticks_per_byte_read + + let value_written_in_memory value_size = + Z.of_int32 value_size * ticks_per_byte_written + + let value_read_from_memory value_size = + Z.of_int32 value_size * ticks_per_byte_read + + let tree_access = Z.one + + let tree_move = Z.one + + let tree_copy = Z.one + + let tree_deletion = Z.one + + let tree_write = Z.one + + let tree_read = Z.one +end let value i = Values.(Num (I32 i)) @@ -608,10 +631,7 @@ let read_input_type = let read_input_name = "tezos_read_input" -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let read_input_ticks _size = default_ticks +let read_input_ticks input_size = Tick_model.value_written_in_memory input_size let read_input = Host_funcs.Host_func @@ -624,10 +644,11 @@ let read_input = Values.(Num (I32 max_bytes)); ] -> let* memory = retrieve_memory memories in - let* read_bytes = + let* written_bytes = Aux.read_input ~input_buffer ~memory ~info_addr ~dst ~max_bytes in - Lwt.return (durable, [value read_bytes], read_input_ticks read_bytes) + Lwt.return + (durable, [value written_bytes], read_input_ticks written_bytes) | _ -> raise Bad_input) let write_output_name = "tezos_write_output" @@ -639,10 +660,8 @@ let write_output_type = let output_types = Types.[NumType I32Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let write_output_ticks _size = default_ticks +let write_output_ticks output_size = + Tick_model.value_read_from_memory output_size let write_output = Host_funcs.Host_func @@ -682,7 +701,7 @@ let write_debug ~implem = let+ () = run ~memory ~src ~num_bytes in (* Write_debug is considered a no-op, it shouldn't take more than the default ticks. *) - (durable, [], default_ticks) + (durable, [], Tick_model.nop) | _ -> raise Bad_input) let store_has_name = "tezos_store_has" @@ -694,10 +713,8 @@ let store_has_type = let output_types = Types.[NumType I32Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_has_ticks _size = default_ticks +let store_has_ticks key_size = + Tick_model.(read_key_in_memory key_size + tree_access) let store_has = Host_funcs.Host_func @@ -713,7 +730,7 @@ let store_has = ~key_offset ~key_length in - (durable, [value r], store_has_ticks 0l) + (durable, [value r], store_has_ticks key_length) | _ -> raise Bad_input) let store_delete_name = "tezos_store_delete" @@ -725,10 +742,8 @@ let store_delete_type = let output_types = Vector.of_list [Types.NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_delete_ticks _size = default_ticks +let store_delete_ticks key_size = + Tick_model.(read_key_in_memory key_size + tree_deletion) let store_delete = Host_funcs.Host_func @@ -744,7 +759,9 @@ let store_delete = ~key_offset ~key_length in - (Durable.to_storage durable, [value code], store_delete_ticks 0l) + ( Durable.to_storage durable, + [value code], + store_delete_ticks key_length ) | _ -> raise Bad_input) let store_value_size_name = "tezos_store_value_size" @@ -757,10 +774,8 @@ let store_value_size_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_value_size_ticks _size = default_ticks +let store_value_size_ticks key_size = + Tick_model.(read_key_in_memory key_size + tree_access) let store_value_size = let open Lwt_syntax in @@ -777,7 +792,7 @@ let store_value_size = ~key_offset ~key_length in - (durable, [value res], store_value_size_ticks 0l) + (durable, [value res], store_value_size_ticks key_length) | _ -> raise Bad_input) let store_list_size_name = "tezos_store_list_size" @@ -790,10 +805,8 @@ let store_list_size_type = let output_types = Types.[NumType I64Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_list_size_ticks _size = default_ticks +let store_list_size_ticks key_size = + Tick_model.(read_key_in_memory key_size + tree_access) let store_list_size = Host_funcs.Host_func @@ -811,7 +824,7 @@ let store_list_size = in ( Durable.to_storage durable, [Values.(Num (I64 result))], - store_list_size_ticks 0l ) + store_list_size_ticks key_length ) | _ -> raise Bad_input) let store_get_nth_key_name = "tezos_store_get_nth_key_list" @@ -831,10 +844,8 @@ let store_get_nth_key_type = let output_types = Types.[NumType I32Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_get_nth_key_ticks _size = default_ticks +let store_get_nth_key_ticks key_size = + Tick_model.(read_key_in_memory key_size + tree_access) let store_get_nth_key = Host_funcs.Host_func @@ -860,7 +871,7 @@ let store_get_nth_key = ~dst ~max_size in - (durable, [value result], store_get_nth_key_ticks 0l) + (durable, [value result], store_get_nth_key_ticks key_length) | _ -> raise Bad_input) let store_copy_name = "tezos_store_copy" @@ -874,10 +885,11 @@ let store_copy_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_copy_ticks _size = default_ticks +let store_copy_ticks from_key_size to_key_size = + Tick_model.( + read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_copy) let store_copy = Host_funcs.Host_func @@ -901,7 +913,9 @@ let store_copy = ~to_key_offset ~to_key_length in - (Durable.to_storage durable, [value code], store_copy_ticks 0l) + ( Durable.to_storage durable, + [value code], + store_copy_ticks from_key_length to_key_length ) | _ -> raise Bad_input) let store_move_name = "tezos_store_move" @@ -915,10 +929,11 @@ let store_move_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_move_ticks _size = default_ticks +let store_move_ticks from_key_size to_key_size = + Tick_model.( + read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_move) let store_move = Host_funcs.Host_func @@ -942,7 +957,9 @@ let store_move = ~to_key_offset ~to_key_length in - (Durable.to_storage durable, [value code], store_move_ticks 0l) + ( Durable.to_storage durable, + [value code], + store_move_ticks to_key_length from_key_length ) | _ -> raise Bad_input) let store_read_name = "tezos_store_read" @@ -962,10 +979,11 @@ let store_read_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_read_ticks _size = default_ticks +let store_read_ticks key_size value_size = + Tick_model.( + read_key_in_memory key_size + + tree_access + + value_written_in_memory value_size) let store_read = Host_funcs.Host_func @@ -990,7 +1008,7 @@ let store_read = ~dest ~max_bytes in - (durable, [value len], store_read_ticks 0l) + (durable, [value len], store_read_ticks key_length len) | _ -> raise Bad_input) let reveal_preimage_name = "tezos_reveal_preimage" @@ -1059,10 +1077,11 @@ let store_write_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. -*) -let store_write_ticks _size = default_ticks +let store_write_ticks key_size value_size = + Tick_model.( + read_key_in_memory key_size + + tree_access + + value_read_from_memory value_size) let store_write = Host_funcs.Host_func @@ -1088,7 +1107,9 @@ let store_write = ~src ~num_bytes in - (Durable.to_storage durable, [value code], store_write_ticks 0l) + ( Durable.to_storage durable, + [value code], + store_write_ticks key_length num_bytes ) | _ -> raise Bad_input) let lookup_opt name = diff --git a/src/lib_scoru_wasm/host_funcs.mli b/src/lib_scoru_wasm/host_funcs.mli index 5cf9600fd021..cdfadd320fab 100644 --- a/src/lib_scoru_wasm/host_funcs.mli +++ b/src/lib_scoru_wasm/host_funcs.mli @@ -260,6 +260,46 @@ module Aux : sig S with type memory = Tezos_webassembly_interpreter.Instance.memory_inst end +(** Defines the tick consumption of memory access for the host functions. *) +module Tick_model : sig + include module type of Tezos_webassembly_interpreter.Tick_model + + (* [read_key_in_memory length] returns the tick consumption of reading a + key of [length] bytes from the memory. *) + val read_key_in_memory : int32 -> tick + + (* [value_written_in_memory length] returns the tick consumption of writing a + value of [length] bytes to the memory. *) + val value_written_in_memory : int32 -> tick + + (* [value_copied_in_memory length] returns the tick consumption of reading a + value of [length] bytes from the memory. *) + val value_read_from_memory : int32 -> tick + + (* [tree_access] is tick consumption of accessing a tree in the durable + storage. *) + val tree_access : tick + + (* [tree_move] is tick consumption of moving a tree in the durable storage. *) + val tree_move : tick + + (* [tree_copy] is tick consumption of copying a tree in the durable + storage. *) + val tree_copy : tick + + (* [tree_deletion] is tick consumption of deleting a tree in the durable + storage. *) + val tree_deletion : tick + + (* [tree_write] is tick consumption of writing a tree in the durable + storage. *) + val tree_write : tick + + (* [tree_read] is tick consumption of reading a tree in the durable + storage. *) + val tree_read : tick +end + module Internal_for_tests : sig (** The number of bytes used to store the metadata of a rollup in memory *) val metadata_size : int diff --git a/src/lib_webassembly/exec/eval.ml b/src/lib_webassembly/exec/eval.ml index d87ce7305716..0e9a984460b4 100644 --- a/src/lib_webassembly/exec/eval.ml +++ b/src/lib_webassembly/exec/eval.ml @@ -1607,10 +1607,9 @@ let reveal_step reveal module_reg payload = in let vs, es = inv.code in let vs = Vector.cons (Num (I32 bytes_count)) vs in - (* TODO: https://gitlab.com/tezos/tezos/-/issues/4531 - Define a tick consumption model. - *) - let ticks_to_consume = Z.zero in + let ticks_to_consume = + Tick_model.(Z.of_int32 bytes_count * ticks_per_byte_written) + in { config with step_kont = -- GitLab From 06b3a06ebe6b8fce7d2c139c9a9eb6fba960c068 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 9 Jan 2023 16:39:32 +0100 Subject: [PATCH 3/4] WASM/PVM: handle error codes in host functions' tick model --- src/lib_scoru_wasm/host_funcs.ml | 81 ++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/lib_scoru_wasm/host_funcs.ml b/src/lib_scoru_wasm/host_funcs.ml index cecadefd9030..867d5e41a19e 100644 --- a/src/lib_scoru_wasm/host_funcs.ml +++ b/src/lib_scoru_wasm/host_funcs.ml @@ -618,6 +618,8 @@ module Tick_model = struct let tree_write = Z.one let tree_read = Z.one + + let with_error result ticks = if result < 0l then nop else ticks end let value i = Values.(Num (I32 i)) @@ -631,7 +633,8 @@ let read_input_type = let read_input_name = "tezos_read_input" -let read_input_ticks input_size = Tick_model.value_written_in_memory input_size +let read_input_ticks input_size = + Tick_model.(with_error input_size (value_written_in_memory input_size)) let read_input = Host_funcs.Host_func @@ -661,7 +664,7 @@ let write_output_type = Types.FuncType (input_types, output_types) let write_output_ticks output_size = - Tick_model.value_read_from_memory output_size + Tick_model.(with_error output_size (value_read_from_memory output_size)) let write_output = Host_funcs.Host_func @@ -713,8 +716,8 @@ let store_has_type = let output_types = Types.[NumType I32Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -let store_has_ticks key_size = - Tick_model.(read_key_in_memory key_size + tree_access) +let store_has_ticks key_size result = + Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) let store_has = Host_funcs.Host_func @@ -730,7 +733,7 @@ let store_has = ~key_offset ~key_length in - (durable, [value r], store_has_ticks key_length) + (durable, [value r], store_has_ticks key_length r) | _ -> raise Bad_input) let store_delete_name = "tezos_store_delete" @@ -742,8 +745,8 @@ let store_delete_type = let output_types = Vector.of_list [Types.NumType I32Type] in Types.FuncType (input_types, output_types) -let store_delete_ticks key_size = - Tick_model.(read_key_in_memory key_size + tree_deletion) +let store_delete_ticks key_size result = + Tick_model.(with_error result (read_key_in_memory key_size + tree_deletion)) let store_delete = Host_funcs.Host_func @@ -761,7 +764,7 @@ let store_delete = in ( Durable.to_storage durable, [value code], - store_delete_ticks key_length ) + store_delete_ticks key_length code ) | _ -> raise Bad_input) let store_value_size_name = "tezos_store_value_size" @@ -774,8 +777,8 @@ let store_value_size_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -let store_value_size_ticks key_size = - Tick_model.(read_key_in_memory key_size + tree_access) +let store_value_size_ticks key_size result = + Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) let store_value_size = let open Lwt_syntax in @@ -792,7 +795,7 @@ let store_value_size = ~key_offset ~key_length in - (durable, [value res], store_value_size_ticks key_length) + (durable, [value res], store_value_size_ticks key_length res) | _ -> raise Bad_input) let store_list_size_name = "tezos_store_list_size" @@ -805,8 +808,8 @@ let store_list_size_type = let output_types = Types.[NumType I64Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -let store_list_size_ticks key_size = - Tick_model.(read_key_in_memory key_size + tree_access) +let store_list_size_ticks key_size result = + Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) let store_list_size = Host_funcs.Host_func @@ -824,7 +827,7 @@ let store_list_size = in ( Durable.to_storage durable, [Values.(Num (I64 result))], - store_list_size_ticks key_length ) + store_list_size_ticks key_length (Int64.to_int32 result) ) | _ -> raise Bad_input) let store_get_nth_key_name = "tezos_store_get_nth_key_list" @@ -844,8 +847,8 @@ let store_get_nth_key_type = let output_types = Types.[NumType I32Type] |> Vector.of_list in Types.FuncType (input_types, output_types) -let store_get_nth_key_ticks key_size = - Tick_model.(read_key_in_memory key_size + tree_access) +let store_get_nth_key_ticks key_size result = + Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) let store_get_nth_key = Host_funcs.Host_func @@ -871,7 +874,7 @@ let store_get_nth_key = ~dst ~max_size in - (durable, [value result], store_get_nth_key_ticks key_length) + (durable, [value result], store_get_nth_key_ticks key_length result) | _ -> raise Bad_input) let store_copy_name = "tezos_store_copy" @@ -885,11 +888,13 @@ let store_copy_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -let store_copy_ticks from_key_size to_key_size = +let store_copy_ticks from_key_size to_key_size result = Tick_model.( - read_key_in_memory from_key_size - + read_key_in_memory to_key_size - + tree_copy) + with_error + result + (read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_copy)) let store_copy = Host_funcs.Host_func @@ -915,7 +920,7 @@ let store_copy = in ( Durable.to_storage durable, [value code], - store_copy_ticks from_key_length to_key_length ) + store_copy_ticks from_key_length to_key_length code ) | _ -> raise Bad_input) let store_move_name = "tezos_store_move" @@ -929,11 +934,13 @@ let store_move_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -let store_move_ticks from_key_size to_key_size = +let store_move_ticks from_key_size to_key_size result = Tick_model.( - read_key_in_memory from_key_size - + read_key_in_memory to_key_size - + tree_move) + with_error + result + (read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_move)) let store_move = Host_funcs.Host_func @@ -959,7 +966,7 @@ let store_move = in ( Durable.to_storage durable, [value code], - store_move_ticks to_key_length from_key_length ) + store_move_ticks to_key_length from_key_length code ) | _ -> raise Bad_input) let store_read_name = "tezos_store_read" @@ -981,9 +988,11 @@ let store_read_type = let store_read_ticks key_size value_size = Tick_model.( - read_key_in_memory key_size - + tree_access - + value_written_in_memory value_size) + with_error + value_size + (read_key_in_memory key_size + + tree_access + + value_written_in_memory value_size)) let store_read = Host_funcs.Host_func @@ -1077,11 +1086,13 @@ let store_write_type = let output_types = Vector.of_list Types.[NumType I32Type] in Types.FuncType (input_types, output_types) -let store_write_ticks key_size value_size = +let store_write_ticks key_size value_size result = Tick_model.( - read_key_in_memory key_size - + tree_access - + value_read_from_memory value_size) + with_error + result + (read_key_in_memory key_size + + tree_access + + value_read_from_memory value_size)) let store_write = Host_funcs.Host_func @@ -1109,7 +1120,7 @@ let store_write = in ( Durable.to_storage durable, [value code], - store_write_ticks key_length num_bytes ) + store_write_ticks key_length num_bytes code ) | _ -> raise Bad_input) let lookup_opt name = -- GitLab From a7cb08667d600d530e765282723f046e300fa013 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Tue, 17 Jan 2023 14:54:15 +0100 Subject: [PATCH 4/4] WASM: make the tick model abstract This implies conversion functions and rewriting a bit the error handling to une thunks to prevent raising errors early. --- src/lib_scoru_wasm/host_funcs.ml | 95 +++++++++++++++---------- src/lib_webassembly/exec/eval.ml | 5 +- src/lib_webassembly/exec/tick_model.ml | 42 +++++++++++ src/lib_webassembly/exec/tick_model.mli | 59 ++++++++++++++- 4 files changed, 160 insertions(+), 41 deletions(-) diff --git a/src/lib_scoru_wasm/host_funcs.ml b/src/lib_scoru_wasm/host_funcs.ml index 867d5e41a19e..6b2334a5f7a1 100644 --- a/src/lib_scoru_wasm/host_funcs.ml +++ b/src/lib_scoru_wasm/host_funcs.ml @@ -598,28 +598,34 @@ end module Tick_model = struct include Tezos_webassembly_interpreter.Tick_model + (* Note that we cannot have negative key length since their unsigned + representation is taken into account, and it will result in an error before + actually reading the memory. As such, it is safe to use the "unsafe" + version here. *) + let read_key_in_memory key_length = - Z.of_int32 key_length * ticks_per_byte_read + of_int32_exn key_length * ticks_per_byte_read let value_written_in_memory value_size = - Z.of_int32 value_size * ticks_per_byte_written + of_int32_exn value_size * ticks_per_byte_written let value_read_from_memory value_size = - Z.of_int32 value_size * ticks_per_byte_read + of_int32_exn value_size * ticks_per_byte_read - let tree_access = Z.one + let tree_access = one - let tree_move = Z.one + let tree_move = one - let tree_copy = Z.one + let tree_copy = one - let tree_deletion = Z.one + let tree_deletion = one - let tree_write = Z.one + let tree_write = one - let tree_read = Z.one + let tree_read = one - let with_error result ticks = if result < 0l then nop else ticks + let with_error result compute_ticks = + if result < 0l then nop else compute_ticks () end let value i = Values.(Num (I32 i)) @@ -634,7 +640,8 @@ let read_input_type = let read_input_name = "tezos_read_input" let read_input_ticks input_size = - Tick_model.(with_error input_size (value_written_in_memory input_size)) + Tick_model.( + with_error input_size (fun () -> value_written_in_memory input_size) |> to_z) let read_input = Host_funcs.Host_func @@ -664,7 +671,9 @@ let write_output_type = Types.FuncType (input_types, output_types) let write_output_ticks output_size = - Tick_model.(with_error output_size (value_read_from_memory output_size)) + Tick_model.( + with_error output_size (fun () -> value_read_from_memory output_size) + |> to_z) let write_output = Host_funcs.Host_func @@ -704,7 +713,7 @@ let write_debug ~implem = let+ () = run ~memory ~src ~num_bytes in (* Write_debug is considered a no-op, it shouldn't take more than the default ticks. *) - (durable, [], Tick_model.nop) + (durable, [], Tick_model.(to_z nop)) | _ -> raise Bad_input) let store_has_name = "tezos_store_has" @@ -717,7 +726,9 @@ let store_has_type = Types.FuncType (input_types, output_types) let store_has_ticks key_size result = - Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) + Tick_model.( + with_error result (fun () -> read_key_in_memory key_size + tree_access) + |> to_z) let store_has = Host_funcs.Host_func @@ -746,7 +757,9 @@ let store_delete_type = Types.FuncType (input_types, output_types) let store_delete_ticks key_size result = - Tick_model.(with_error result (read_key_in_memory key_size + tree_deletion)) + Tick_model.( + with_error result (fun () -> read_key_in_memory key_size + tree_deletion) + |> to_z) let store_delete = Host_funcs.Host_func @@ -778,7 +791,9 @@ let store_value_size_type = Types.FuncType (input_types, output_types) let store_value_size_ticks key_size result = - Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) + Tick_model.( + with_error result (fun () -> read_key_in_memory key_size + tree_access) + |> to_z) let store_value_size = let open Lwt_syntax in @@ -809,7 +824,9 @@ let store_list_size_type = Types.FuncType (input_types, output_types) let store_list_size_ticks key_size result = - Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) + Tick_model.( + with_error result (fun () -> read_key_in_memory key_size + tree_access) + |> to_z) let store_list_size = Host_funcs.Host_func @@ -848,7 +865,9 @@ let store_get_nth_key_type = Types.FuncType (input_types, output_types) let store_get_nth_key_ticks key_size result = - Tick_model.(with_error result (read_key_in_memory key_size + tree_access)) + Tick_model.( + with_error result (fun () -> read_key_in_memory key_size + tree_access) + |> to_z) let store_get_nth_key = Host_funcs.Host_func @@ -890,11 +909,11 @@ let store_copy_type = let store_copy_ticks from_key_size to_key_size result = Tick_model.( - with_error - result - (read_key_in_memory from_key_size - + read_key_in_memory to_key_size - + tree_copy)) + with_error result (fun () -> + read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_copy) + |> to_z) let store_copy = Host_funcs.Host_func @@ -936,11 +955,11 @@ let store_move_type = let store_move_ticks from_key_size to_key_size result = Tick_model.( - with_error - result - (read_key_in_memory from_key_size - + read_key_in_memory to_key_size - + tree_move)) + with_error result (fun () -> + read_key_in_memory from_key_size + + read_key_in_memory to_key_size + + tree_move) + |> to_z) let store_move = Host_funcs.Host_func @@ -988,11 +1007,11 @@ let store_read_type = let store_read_ticks key_size value_size = Tick_model.( - with_error - value_size - (read_key_in_memory key_size - + tree_access - + value_written_in_memory value_size)) + with_error value_size (fun () -> + read_key_in_memory key_size + + tree_access + + value_written_in_memory value_size) + |> to_z) let store_read = Host_funcs.Host_func @@ -1088,11 +1107,11 @@ let store_write_type = let store_write_ticks key_size value_size result = Tick_model.( - with_error - result - (read_key_in_memory key_size - + tree_access - + value_read_from_memory value_size)) + with_error result (fun () -> + read_key_in_memory key_size + + tree_access + + value_read_from_memory value_size) + |> to_z) let store_write = Host_funcs.Host_func diff --git a/src/lib_webassembly/exec/eval.ml b/src/lib_webassembly/exec/eval.ml index 0e9a984460b4..3fba1c6e559a 100644 --- a/src/lib_webassembly/exec/eval.ml +++ b/src/lib_webassembly/exec/eval.ml @@ -1607,8 +1607,9 @@ let reveal_step reveal module_reg payload = in let vs, es = inv.code in let vs = Vector.cons (Num (I32 bytes_count)) vs in + (* The number of bytes cannot be negative per construction. *) let ticks_to_consume = - Tick_model.(Z.of_int32 bytes_count * ticks_per_byte_written) + Tick_model.(of_int32_exn bytes_count * ticks_per_byte_written) in { config with @@ -1622,7 +1623,7 @@ let reveal_step reveal module_reg payload = { code = (vs, es); fresh_frame = None; - remaining_ticks = ticks_to_consume; + remaining_ticks = Tick_model.to_z ticks_to_consume; } ) ); } | _ -> raise (Reveal_error Reveal_step) diff --git a/src/lib_webassembly/exec/tick_model.ml b/src/lib_webassembly/exec/tick_model.ml index 7ca7d1bea924..ea3be77d673d 100644 --- a/src/lib_webassembly/exec/tick_model.ml +++ b/src/lib_webassembly/exec/tick_model.ml @@ -1,5 +1,47 @@ type tick = Z.t +let zero = Z.zero + +let one = Z.one + +let of_int32 i = if i < 0l then None else Some (Z.of_int32 i) + +let of_int32_exn i = + match of_int32 i with + | Some i -> i + | None -> raise (Invalid_argument "Ticks cannot be negative") + +let to_int32 t = try Some (Z.to_int32 t) with Z.Overflow -> None + +let to_int32_exn t = + match to_int32 t with + | Some i -> i + | None -> + raise + (Invalid_argument + "Ticks cannot be contained in the signed int32 representation") + +let of_int64 i = if i < 0L then None else Some (Z.of_int64 i) + +let of_int64_exn i = + match of_int64 i with + | Some i -> i + | None -> raise (Invalid_argument "Ticks cannot be negative") + +let to_int64 t = try Some (Z.to_int64 t) with Z.Overflow -> None + +let to_int64_exn t = + match to_int64 t with + | Some i -> i + | None -> + raise + (Invalid_argument + "Ticks cannot be contained in the signed int64 representation") + +let to_z t = t + +let of_z z = if Z.Compare.(z < Z.zero) then None else Some z + let ( + ) = Z.add let ( * ) = Z.mul diff --git a/src/lib_webassembly/exec/tick_model.mli b/src/lib_webassembly/exec/tick_model.mli index 5bff6f489943..782b39e642c3 100644 --- a/src/lib_webassembly/exec/tick_model.mli +++ b/src/lib_webassembly/exec/tick_model.mli @@ -1,7 +1,64 @@ (** This module implements a generic tick model. These values were benchmarked from `MemoryCopy`, `MemoryFill` and `MemoryInit`. *) -type tick = Z.t +type tick + +(* Tick of cost `0`. *) +val zero : tick + +(* Tick of cost `1`. *) +val one : tick + +(** [of_int32 i] returns the tick representation of [i], or {None} if [i] + is negative. +*) +val of_int32 : int32 -> tick option + +(** [of_int32_exn i] returns the tick representation of [i]. + + @raise Invalid_argument if [i] is negative. +*) +val of_int32_exn : int32 -> tick + +(** [to_int32 t] returns the signed int32 representation of [t] or {None} if [t] + overflows the signed int32 representation. +*) +val to_int32 : tick -> int32 option + +(** [to_int32_exn t] returns the signed int32 representation of [t]. + + @raise Invalid_argument if [t] overflows the signed int32 representation. +*) +val to_int32_exn : tick -> int32 + +(** [of_int64 i] returns the tick representation of [i], or {None} if [i] + is negative. +*) +val of_int64 : int64 -> tick option + +(** [of_int64_exn i] returns the tick representation of [i]. + + @raise Invalid_argument if [i] is negative. +*) +val of_int64_exn : int64 -> tick + +(** [to_int64 t] returns the signed int64 representation of [t] or {None} if [t] + overflows the signed int64 representation. +*) +val to_int64 : tick -> int64 option + +(** [to_int64_exn t] returns the signed int64 representation of [t]. + + @raise Invalid_argument if [t] overflows the signed int64 representation. +*) +val to_int64_exn : tick -> int64 + +(* [of_z z] returns the tick representation of [z], or {None} if [z] + is negative. *) +val of_z : Z.t -> tick option + +(* [to_z t] returns the Z representation of a tick counter [t]. *) +val to_z : tick -> Z.t (** [t1 + t2] returns the sum of [t1] and [t2] *) val ( + ) : tick -> tick -> tick -- GitLab