diff --git a/CHANGES.rst b/CHANGES.rst index 248207d9da56105846d93386fb3f398978010e33..342f2788dd0b0fd3a2f9e1d942eba3fcac25fef9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,7 @@ Node Client ------ +- Adding client commands to generate, open and verify a time-lock. - The ``typecheck script`` command can now be used to typecheck several scripts. @@ -73,3 +74,5 @@ Smart Rollup WASM Debugger Miscellaneous ------------- + +- Updating and re-enabling the time-lock Michelson commands. diff --git a/docs/alpha/michelson.rst b/docs/alpha/michelson.rst index 8965c8218f71d0af27a172eac5e4be0983db559a..0ce06c64ff0bf53957a5b921cd68aa1b3788f44e 100644 --- a/docs/alpha/michelson.rst +++ b/docs/alpha/michelson.rst @@ -1170,9 +1170,9 @@ Type ``'a`` must be comparable (the ``COMPARE`` primitive must be defined over i Operations on timelock ~~~~~~~~~~~~~~~~~~~~~~ -- ``OPEN_CHEST``: opens a timelocked chest given its key and the time. The results can be bytes - if the opening is correct, or a boolean indicating whether the chest was incorrect, - or its opening was. See :doc:`Timelock ` for more information. +- ``OPEN_CHEST``: opens a timelocked chest given its key and the time. The + result is a byte option depending if the opening is correct or not. See + :doc:`Timelock ` for more information. :: diff --git a/docs/alpha/timelock.rst b/docs/alpha/timelock.rst index 22624a2556d3e87dd3dd9f7ff5eeb712451c9e6e..ccc1adc1a774392d33e0c39616458aabc4b491af 100644 --- a/docs/alpha/timelock.rst +++ b/docs/alpha/timelock.rst @@ -33,9 +33,9 @@ for more details). Time-lock encryption allows for encrypting a message so it can be decrypted in two ways. -Either the author of the ciphertext provides a plaintext +Either the author of the ciphertext produces a plaintext (and a proof of correct decryption) -by providing the information used to generate the timelock. +by providing the information used to generate the time-lock. Or, a sequential computation can decrypt the ciphertext after a computation requiring ``T`` sequential operations (modular squaring in our case), for some pre-determined constant ``T``. @@ -105,13 +105,8 @@ and the following opcode: or indicates that the ``chest`` or the ``chest_key`` is malicious. If we open the chest with a correctly generated chest key, the instruction pushes -``Left bytes`` on top of the stack where the bytes are -cryptographically guaranteed to be the bytes the chest provider time-locked. -If the ciphertext does not decrypt under the symmetric key that was time-locked, it pushes on the stack -``Right False``. -If the provided symmetric key was not the one time-locked -(detectable due to the time-lock proof), -it pushes on the stack ``Right True``. +``Some bytes`` on top of the stack where the bytes are +cryptographically guaranteed to be the bytes the chest provider time-locked, otherwise ``None``. Note that the implementation uses an authenticated encryption scheme, so one can detect if someone provides a wrong key while fooling the time-lock proof. This is doable only by someone knowing the factorization of the RSA modulus. @@ -125,11 +120,11 @@ Implementation of the time-lock puzzle The implementation of the time-lock puzzle and proof scheme is located in :src:`src/lib_crypto/timelock.ml`. -To facilitate the use of time-locks, commands have also been implemented in Octez client to generate a ``chest`` and ``chest_key`` as well as to open and verify them. An additional command ``precompute`` was implemented to fasten the timelock ``chest`` generation. +To facilitate the use of time-locks, commands have also been implemented in Octez client to generate a ``chest`` and ``chest_key`` as well as to open and verify them. An additional command ``precompute`` was implemented to fasten the time-lock ``chest`` generation. For more information on the client commands, see :doc:`cli-commands`. Example ------- -A coin flip contract on Tezos source code `here `__ gives an example of using time-lock. Beware this contract is for educational purpose only and is not secure. +A coin flip contract on Tezos source code :src:`` gives an example of using time-lock. Beware, this contract is for educational purpose only, and is not secure. diff --git a/src/lib_crypto/timelock.ml b/src/lib_crypto/timelock.ml index a0be27d2daecb97c6f147125fb673777a47509f0..d82cdb1935b4cc1e39ac041fc641f97f7e9f958f 100644 --- a/src/lib_crypto/timelock.ml +++ b/src/lib_crypto/timelock.ml @@ -329,7 +329,7 @@ type chest_key = timelock_proof let chest_key_encoding = proof_encoding -type opening_result = Correct of Bytes.t | Bogus_cipher | Bogus_opening +type opening_result = Correct of Bytes.t | Bogus_opening let create_chest_and_chest_key ?(precompute_path = None) ~payload ~time () = let locked_value, proof = diff --git a/src/lib_crypto/timelock.mli b/src/lib_crypto/timelock.mli index 026792381182b36f8ffb85c13c7af2935057577a..0f2cfd33598bb38b5702b8a1599cedbbd863f1dd 100644 --- a/src/lib_crypto/timelock.mli +++ b/src/lib_crypto/timelock.mli @@ -200,7 +200,7 @@ val chest_key_encoding : chest_key Data_encoding.t fault. Otherwise we return [Correct payload] where [payload] is the content that had originally been put in the chest. *) -type opening_result = Correct of Bytes.t | Bogus_cipher | Bogus_opening +type opening_result = Correct of Bytes.t | Bogus_opening (** Takes a chest, chest key and time and tries to recover the underlying plaintext. See the documentation of opening_result. *) diff --git a/src/lib_protocol_environment/environment_V9.ml b/src/lib_protocol_environment/environment_V9.ml index 2be89ec833c1a75edbe11d08ae977df1d75fafe3..65142cf59d7431e0802b425e3856ef7aac810c3c 100644 --- a/src/lib_protocol_environment/environment_V9.ml +++ b/src/lib_protocol_environment/environment_V9.ml @@ -98,9 +98,10 @@ module type T = sig ('m, 'pr, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t and type Error_monad.shell_tztrace = Error_monad.tztrace and type 'a Error_monad.shell_tzresult = ('a, Error_monad.tztrace) result - and type Timelock.chest = Tezos_crypto.Timelock.chest - and type Timelock.chest_key = Tezos_crypto.Timelock.chest_key - and type Timelock.opening_result = Tezos_crypto.Timelock.opening_result + and type Timelock.chest = Tezos_crypto.Timelock_legacy.chest + and type Timelock.chest_key = Tezos_crypto.Timelock_legacy.chest_key + and type Timelock.opening_result = + Tezos_crypto.Timelock_legacy.opening_result and module Sapling = Tezos_sapling.Core.Validator and type ('a, 'b) Either.t = ('a, 'b) Stdlib.Either.t and type Bls.Primitive.Fr.t = Bls12_381.Fr.t @@ -308,7 +309,7 @@ struct module P256 = Tezos_crypto.Signature.P256 module Bls = Tezos_crypto.Signature.Bls module Signature = Tezos_crypto.Signature.V1 - module Timelock = Tezos_crypto.Timelock + module Timelock = Tezos_crypto.Timelock_legacy module Vdf = Class_group_vdf.Vdf_self_contained module S = struct diff --git a/src/lib_protocol_environment/environment_V9.mli b/src/lib_protocol_environment/environment_V9.mli index 4213335f5bc111aa7cd9aa2733b3e412d7cb84f6..b789aa3932ed1d73a524b80961a4fce626d4be37 100644 --- a/src/lib_protocol_environment/environment_V9.mli +++ b/src/lib_protocol_environment/environment_V9.mli @@ -98,9 +98,10 @@ module type T = sig ('m, 'pr, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t and type Error_monad.shell_tztrace = Error_monad.tztrace and type 'a Error_monad.shell_tzresult = ('a, Error_monad.tztrace) result - and type Timelock.chest = Tezos_crypto.Timelock.chest - and type Timelock.chest_key = Tezos_crypto.Timelock.chest_key - and type Timelock.opening_result = Tezos_crypto.Timelock.opening_result + and type Timelock.chest = Tezos_crypto.Timelock_legacy.chest + and type Timelock.chest_key = Tezos_crypto.Timelock_legacy.chest_key + and type Timelock.opening_result = + Tezos_crypto.Timelock_legacy.opening_result and module Sapling = Tezos_sapling.Core.Validator and type ('a, 'b) Either.t = ('a, 'b) Stdlib.Either.t and type Bls.Primitive.Fr.t = Bls12_381.Fr.t diff --git a/src/lib_protocol_environment/sigs/v10.ml b/src/lib_protocol_environment/sigs/v10.ml index ffc23f56a327827e064f455c5793e22c5e2f0a69..3c934e3943db8718e316b8af5f7419cb31c8d507 100644 --- a/src/lib_protocol_environment/sigs/v10.ml +++ b/src/lib_protocol_environment/sigs/v10.ml @@ -10238,14 +10238,12 @@ type chest_key val chest_key_encoding : chest_key Data_encoding.t (** Result of the opening of a chest. - The opening can fail in two way which we distinguish to blame the right person. - One can provide a false unlocked_value or unlocked_proof, in which case - we return [Bogus_opening] and the provider of the chest key is at fault. - Otherwise, one can lock the wrong key or put garbage in the ciphertext in which case - we return [Bogus_cipher] and the provider of the chest is at fault. + The opening can fail if one provides a false unlocked_value or + unlocked_proof, in which case we return [Bogus_opening] and the provider of + the chest key is at fault. Otherwise we return [Correct payload] where payload was what had originally been put in the chest. *) -type opening_result = Correct of Bytes.t | Bogus_cipher | Bogus_opening +type opening_result = Correct of Bytes.t | Bogus_opening (** Takes a chest, chest key and time and tries to recover the underlying plaintext. See the documentation of opening_result. *) diff --git a/src/lib_protocol_environment/sigs/v10/timelock.mli b/src/lib_protocol_environment/sigs/v10/timelock.mli index 4151d194a00445120d8d1f1a13f6e37a51ad0406..9a12f7e775edd9b146253637252e52fe68adc7de 100644 --- a/src/lib_protocol_environment/sigs/v10/timelock.mli +++ b/src/lib_protocol_environment/sigs/v10/timelock.mli @@ -35,14 +35,12 @@ type chest_key val chest_key_encoding : chest_key Data_encoding.t (** Result of the opening of a chest. - The opening can fail in two way which we distinguish to blame the right person. - One can provide a false unlocked_value or unlocked_proof, in which case - we return [Bogus_opening] and the provider of the chest key is at fault. - Otherwise, one can lock the wrong key or put garbage in the ciphertext in which case - we return [Bogus_cipher] and the provider of the chest is at fault. + The opening can fail if one provides a false unlocked_value or + unlocked_proof, in which case we return [Bogus_opening] and the provider of + the chest key is at fault. Otherwise we return [Correct payload] where payload was what had originally been put in the chest. *) -type opening_result = Correct of Bytes.t | Bogus_cipher | Bogus_opening +type opening_result = Correct of Bytes.t | Bogus_opening (** Takes a chest, chest key and time and tries to recover the underlying plaintext. See the documentation of opening_result. *) diff --git a/src/lib_store/unix/test/test_consistency.ml b/src/lib_store/unix/test/test_consistency.ml index d45c3d2e2013bea801eba98007996206585bf026..fb74e7d67845f39fbe0640104de638535108f38a 100644 --- a/src/lib_store/unix/test/test_consistency.ml +++ b/src/lib_store/unix/test/test_consistency.ml @@ -39,7 +39,7 @@ let nb_protocols = 5 let register_protocol ~hash ~sources = let module M = struct include - Registered_protocol.Register_embedded_V9 + Registered_protocol.Register_embedded_V10 (Tezos_protocol_environment_demo_noops) (Tezos_protocol_demo_noops.Protocol) (struct diff --git a/src/proto_017_PtNairob/lib_benchmarks_proto/interpreter_benchmarks.ml b/src/proto_017_PtNairob/lib_benchmarks_proto/interpreter_benchmarks.ml index e9f600aa81fabf62b36f929fe49cc803cfc085c3..dcbd5a798a229a24fc12a7a9cfb40953d742c44b 100644 --- a/src/proto_017_PtNairob/lib_benchmarks_proto/interpreter_benchmarks.ml +++ b/src/proto_017_PtNairob/lib_benchmarks_proto/interpreter_benchmarks.ml @@ -28,7 +28,7 @@ let ns = Interpreter_model.ns let fv = Interpreter_model.fv -module Timelock_samplers = Tezos_crypto.Timelock +module Timelock_samplers = Tezos_crypto.Timelock_legacy open Protocol (* ------------------------------------------------------------------------- *) diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 757fbb8111a4d5c3c9f0f701004b36b419461555..f2dbfb15609e3c5a522ad9f31bb3a55aa0ea7d21 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -3325,7 +3325,6 @@ let commands_rw () = cctxt#message "Timelock chest verified, message is: %s" m | Bogus_opening -> cctxt#error "Error opening: incorrect proof or unlocked value." - | Bogus_cipher -> cctxt#error "Error deciphering." in return_unit); ] diff --git a/src/proto_alpha/lib_plugin/script_interpreter_logging.ml b/src/proto_alpha/lib_plugin/script_interpreter_logging.ml index 663283db73ec2f9a5727fd0431cd7df410f1a8a8..02e3cb47107bb6beb618fd06ca5475eebdb86984 100644 --- a/src/proto_alpha/lib_plugin/script_interpreter_logging.ml +++ b/src/proto_alpha/lib_plugin/script_interpreter_logging.ml @@ -1702,7 +1702,7 @@ module Stack_utils = struct reconstruct = (fun k -> IJoin_tickets (loc, ty, k)); } | IOpen_chest (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) -> - let s = Item_t (or_bytes_bool_t, s) in + let s = Item_t (option_bytes_t, s) in ok @@ Ex_split_kinstr { diff --git a/src/proto_alpha/lib_protocol/script_interpreter.ml b/src/proto_alpha/lib_protocol/script_interpreter.ml index e90e9c291fb0689bab4037e0035dee90420be853..add9a6e0a8f8bbf982c1d8ab2b81b131a9a6d669 100644 --- a/src/proto_alpha/lib_protocol/script_interpreter.ml +++ b/src/proto_alpha/lib_protocol/script_interpreter.ml @@ -1637,12 +1637,11 @@ module Raw = struct Therefore no proof can be correct.*) let accu = match Script_int.to_int time_z with - | None -> R false + | None -> None | Some time -> ( match Script_timelock.open_chest chest chest_key ~time with - | Correct bytes -> L bytes - | Bogus_cipher -> R false - | Bogus_opening -> R true) + | Correct bytes -> Some bytes + | Bogus_opening -> None) in (step [@ocaml.tailcall]) g gas k ks accu stack | IEmit {tag; ty = event_type; unparsed_ty; k; loc = _} -> diff --git a/src/proto_alpha/lib_protocol/script_ir_translator.ml b/src/proto_alpha/lib_protocol/script_ir_translator.ml index 4093dccc15385cefc3e4498e63d35390abeca56d..37cb52cc176d7b810b8c1fcc46211ed69b530664 100644 --- a/src/proto_alpha/lib_protocol/script_ir_translator.ml +++ b/src/proto_alpha/lib_protocol/script_ir_translator.ml @@ -4296,7 +4296,7 @@ and parse_instr : | ( Prim (loc, I_OPEN_CHEST, [], _), Item_t (Chest_key_t, Item_t (Chest_t, Item_t (Nat_t, rest))) ) -> let instr = {apply = (fun k -> IOpen_chest (loc, k))} in - typed ctxt loc instr (Item_t (or_bytes_bool_t, rest)) + typed ctxt loc instr (Item_t (option_bytes_t, rest)) (* Events *) | Prim (loc, I_EMIT, [], annot), Item_t (data, rest) -> check_packable ~allow_contract:false loc data >>?= fun () -> diff --git a/src/proto_alpha/lib_protocol/script_typed_ir.ml b/src/proto_alpha/lib_protocol/script_typed_ir.ml index 7494c7b1a4416f06758edd4d16cd0ae645b2d4f6..eacc421362bf37ce13ac59c7209f0596a8c79e8f 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir.ml +++ b/src/proto_alpha/lib_protocol/script_typed_ir.ml @@ -1105,7 +1105,7 @@ and ('before_top, 'before, 'result_top, 'result) kinstr = Script.location * 'a comparable_ty * ('a ticket option, 'S, 'r, 'F) kinstr -> (('a ticket, 'a ticket) pair, 'S, 'r, 'F) kinstr | IOpen_chest : - Script.location * ((bytes, bool) or_, 'S, 'r, 'F) kinstr + Script.location * (bytes option, 'S, 'r, 'F) kinstr -> ( Script_timelock.chest_key, Script_timelock.chest * (n num * 'S), 'r, diff --git a/src/proto_alpha/lib_protocol/script_typed_ir.mli b/src/proto_alpha/lib_protocol/script_typed_ir.mli index f798213b2e68367b5b215ef30b5d1d8780b2aaf2..834641ddf2b5f57c7536ed35790ff280a0412e0e 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir.mli +++ b/src/proto_alpha/lib_protocol/script_typed_ir.mli @@ -1133,7 +1133,7 @@ and ('before_top, 'before, 'result_top, 'result) kinstr = Script.location * 'a comparable_ty * ('a ticket option, 'S, 'r, 'F) kinstr -> (('a ticket, 'a ticket) pair, 'S, 'r, 'F) kinstr | IOpen_chest : - Script.location * ((bytes, bool) or_, 'S, 'r, 'F) kinstr + Script.location * (bytes option, 'S, 'r, 'F) kinstr -> ( Script_timelock.chest_key, Script_timelock.chest * (n num * 'S), 'r, diff --git a/src/proto_alpha/lib_protocol/test/integration/michelson/contracts/timelock_flip.tz b/src/proto_alpha/lib_protocol/test/integration/michelson/contracts/timelock_flip.tz index 3f981c8e35fba88d46c8b6a21ef9d89f6d976522..1d0c6cbe2c40e3ad9bed48a4f957bc47aec7f138 100644 --- a/src/proto_alpha/lib_protocol/test/integration/michelson/contracts/timelock_flip.tz +++ b/src/proto_alpha/lib_protocol/test/integration/michelson/contracts/timelock_flip.tz @@ -71,7 +71,7 @@ code { UNPAIR 5; SWAP; DIP 2 {PUSH nat 1024}; OPEN_CHEST; - IF_LEFT { # If the chest opens successfully, + IF_SOME { # If the chest opens successfully, # we compare the guess with the locked value. DUP 4; COMPARE; @@ -80,10 +80,8 @@ code { UNPAIR 5; PUSH bytes 0x00} { # else we store 0x01 PUSH bytes 0x01}} - { IF { # We store 0x11 for the first type of failure - PUSH bytes 0x11} - { # We store 0x10 for the second type of failure - PUSH bytes 0x10}}; + { # We store 0x10 in case of failure + PUSH bytes 0x10}; DIG 3; PAIR; DIG 2; diff --git a/tezt/tests/timelock.ml b/tezt/tests/timelock.ml index e2805572c02da546b7673fa9633fc5ab533abd9f..40c2a412b5679e21a5b81c34fa545c6a71b5dbde 100644 --- a/tezt/tests/timelock.ml +++ b/tezt/tests/timelock.ml @@ -356,40 +356,7 @@ let test_contract_error_opening ~protocol () = let giver = Constant.bootstrap3.alias in let arg = "Right (Right " ^ chest_key_to_string chest_key ^ ")" in let* () = Client.transfer ~burn_cap ~amount ~giver ~receiver ~arg client in - let* b_result = assert_storage ~chest ~guess ~msg:"0x11" client receiver in - assert (b_init && b_guess && b_result) ; - unit - -let test_contract_incorrect_chest ~protocol () = - let* client, receiver = originate_contract protocol "timelock_flip.tz" in - (* bootstrap2 starts a coin toss game by submitting a fake chest *) - let str = "head" in - let* chest_file, chest, _, _ = create_timelock client path time str in - let* chest = - let* _, chest2, _, _ = create_timelock client path "10" "fake" in - return {chest with ciphertext = chest2.ciphertext} - in - let chest = chest_to_string chest in - let giver = Constant.bootstrap2.alias in - let arg = "Left " ^ chest in - let* () = Client.transfer ~burn_cap ~amount ~giver ~receiver ~arg client in - let* b_init = assert_storage ~chest client receiver in - (* bootstrap3 submits their guess *) - let giver = Constant.bootstrap3.alias in - let guess = Bytes.of_string "tail" |> bytes_to_string in - let arg = "Right (Left " ^ guess ^ ")" in - let* () = Client.transfer ~burn_cap ~amount ~giver ~receiver ~arg client in - let* b_guess = assert_storage ~chest ~guess ~msg:"0xb0" client receiver in - (* bootstrap3 opens the chest to finish the game *) - let* _chest_key_file, chest_key = open_timelock client path chest_file time in - let giver = Constant.bootstrap3.alias in - let arg = "Right (Right " ^ chest_key_to_string chest_key ^ ")" in - let* () = Client.transfer ~burn_cap ~amount ~giver ~receiver ~arg client in - (* The expecting message in the contract here is 0x01 and not 0x10. - This is because the error Bogus_cipher is not currently used. Indeed, if - the decryption fails, the timelock library returns Bytes.empty instead of - the said exception. *) - let* b_result = assert_storage ~chest ~guess ~msg:"0x01" client receiver in + let* b_result = assert_storage ~chest ~guess ~msg:"0x10" client receiver in assert (b_init && b_guess && b_result) ; unit @@ -407,5 +374,4 @@ let register ~protocols = ("Incorrect guess test on timelock", test_contract_incorrect_guess); ("Guess too late test on timelock", test_contract_guess_too_late); ("Error opening test on timelock", test_contract_error_opening); - ("Incorrect chest test on timelock", test_contract_incorrect_chest); ]