diff --git a/gitlab-pages/docs/advanced/timestamps-addresses.md b/gitlab-pages/docs/advanced/timestamps-addresses.md index b3f1d2a0a..adc11a833 100644 --- a/gitlab-pages/docs/advanced/timestamps-addresses.md +++ b/gitlab-pages/docs/advanced/timestamps-addresses.md @@ -19,6 +19,8 @@ const today: timestamp = now; ``` +> When running code with ligo CLI, the option `--predecessor-timestamp` allows you to control what `now` returns. + ### Timestamp arithmetic In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. time constraints for your smart contracts like this: @@ -30,6 +32,8 @@ In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. tim const today: timestamp = now; const one_day: int = 86400; const in_24_hrs: timestamp = today + one_day; +const some_date: timestamp = ("2000-01-01T10:10:10Z" : timestamp); +const one_day_later: timestamp = some_date + one_day; ``` diff --git a/src/bin/cli.ml b/src/bin/cli.ml index edc0d9b44..61c4e7a4e 100644 --- a/src/bin/cli.ml +++ b/src/bin/cli.ml @@ -90,7 +90,7 @@ let predecessor_timestamp = let open Arg in let info = let docv = "PREDECESSOR_TIMESTAMP" in - let doc = "$(docv) is the pedecessor_timestamp the michelson interpreter transaction will use (e.g. '2000-01-01T10:10:10Z')" in + let doc = "$(docv) is the pedecessor_timestamp (now value) the michelson interpreter will use (e.g. '2000-01-01T10:10:10Z')" in info ~docv ~doc ["predecessor-timestamp"] in value @@ opt (some string) None info @@ -156,7 +156,7 @@ let measure_contract = (Term.ret term , Term.info ~doc cmdname) let compile_parameter = - let f source_file entry_point expression syntax display_format michelson_format = + let f source_file entry_point expression syntax amount sender source predecessor_timestamp display_format michelson_format = toplevel ~display_format @@ let%bind simplified = Compile.Of_source.compile source_file (Syntax_name syntax) in let%bind typed_prg,state = Compile.Of_simplified.compile simplified in @@ -174,11 +174,12 @@ let compile_parameter = let%bind compiled_param = Compile.Of_mini_c.aggregate_and_compile_expression mini_c_prg mini_c_param in let%bind () = Compile.Of_typed.assert_equal_contract_type Check_parameter entry_point typed_prg typed_param in let%bind () = Compile.Of_michelson.assert_equal_contract_type Check_parameter michelson_prg compiled_param in - let%bind value = Run.evaluate_expression compiled_param.expr compiled_param.expr_ty in + let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in + let%bind value = Run.evaluate_expression ~options compiled_param.expr compiled_param.expr_ty in ok @@ Format.asprintf "%a\n" (Main.Display.michelson_pp michelson_format) value in let term = - Term.(const f $ source_file 0 $ entry_point 1 $ expression "PARAMETER" 2 $ syntax $ display_format $ michelson_code_format) in + Term.(const f $ source_file 0 $ entry_point 1 $ expression "PARAMETER" 2 $ syntax $ amount $ sender $ source $ predecessor_timestamp $ display_format $ michelson_code_format) in let cmdname = "compile-parameter" in let doc = "Subcommand: compile parameters to a michelson expression. The resulting michelson expression can be passed as an argument in a transaction which calls a contract." in (Term.ret term , Term.info ~doc cmdname) @@ -213,7 +214,7 @@ let interpret = let compile_storage = - let f source_file entry_point expression syntax display_format michelson_format = + let f source_file entry_point expression syntax amount sender source predecessor_timestamp display_format michelson_format = toplevel ~display_format @@ let%bind simplified = Compile.Of_source.compile source_file (Syntax_name syntax) in let%bind typed_prg,state = Compile.Of_simplified.compile simplified in @@ -231,11 +232,12 @@ let compile_storage = let%bind compiled_param = Compile.Of_mini_c.compile_expression mini_c_param in let%bind () = Compile.Of_typed.assert_equal_contract_type Check_storage entry_point typed_prg typed_param in let%bind () = Compile.Of_michelson.assert_equal_contract_type Check_storage michelson_prg compiled_param in - let%bind value = Run.evaluate_expression compiled_param.expr compiled_param.expr_ty in + let%bind options = Run.make_dry_run_options {predecessor_timestamp ; amount ; sender ; source } in + let%bind value = Run.evaluate_expression ~options compiled_param.expr compiled_param.expr_ty in ok @@ Format.asprintf "%a\n" (Main.Display.michelson_pp michelson_format) value in let term = - Term.(const f $ source_file 0 $ entry_point 1 $ expression "STORAGE" 2 $ syntax $ display_format $ michelson_code_format) in + Term.(const f $ source_file 0 $ entry_point 1 $ expression "STORAGE" 2 $ syntax $ amount $ sender $ source $ predecessor_timestamp $ display_format $ michelson_code_format) in let cmdname = "compile-storage" in let doc = "Subcommand: compile an initial storage in ligo syntax to a michelson expression. The resulting michelson expression can be passed as an argument in a transaction which originates a contract." in (Term.ret term , Term.info ~doc cmdname) diff --git a/src/bin/expect_tests/contract_tests.ml b/src/bin/expect_tests/contract_tests.ml index 0ce94f98d..b9b34c076 100644 --- a/src/bin/expect_tests/contract_tests.ml +++ b/src/bin/expect_tests/contract_tests.ml @@ -30,6 +30,10 @@ let%expect_test _ = () +let%expect_test _ = + run_ligo_good [ "compile-storage" ; contract "timestamp.ligo" ; "main" ; "now" ; "--predecessor-timestamp" ; "2042-01-01T00:00:00Z" ] ; + [%expect {| "2042-01-01T00:00:01Z" |}] + let%expect_test _ = run_ligo_good [ "compile-contract" ; contract "coase.ligo" ; "main" ] ; [%expect {| @@ -936,3 +940,7 @@ let%expect_test _ = let%expect_test _ = run_ligo_bad [ "compile-contract" ; contract "bad_address_format.religo" ; "main" ] ; [%expect {| ligo: in file "bad_address_format.religo", line 2, characters 25-47. Badly formatted address "KT1badaddr": {"location":"in file \"bad_address_format.religo\", line 2, characters 25-47"} |}] + +let%expect_test _ = + run_ligo_bad [ "compile-contract" ; contract "bad_timestamp.ligo" ; "main" ] ; + [%expect {| ligo: in file "bad_timestamp.ligo", line 5, characters 29-43. Badly formatted timestamp "badtimestamp": {"location":"in file \"bad_timestamp.ligo\", line 5, characters 29-43"} |}] \ No newline at end of file diff --git a/src/bin/expect_tests/help_tests.ml b/src/bin/expect_tests/help_tests.ml index e804c8283..af5ab1797 100644 --- a/src/bin/expect_tests/help_tests.ml +++ b/src/bin/expect_tests/help_tests.ml @@ -176,6 +176,9 @@ let%expect_test _ = contract. OPTIONS + --amount=AMOUNT (absent=0) + AMOUNT is the amount the michelson interpreter will use. + --format=DISPLAY_FORMAT, --display-format=DISPLAY_FORMAT (absent=human-readable) DISPLAY_FORMAT is the format that will be used by the CLI. @@ -194,11 +197,23 @@ let%expect_test _ = compile-contract for the resulting Michelson. Available formats are 'text' (default), 'json' and 'hex'. + --predecessor-timestamp=PREDECESSOR_TIMESTAMP + PREDECESSOR_TIMESTAMP is the pedecessor_timestamp (now value) the + michelson interpreter will use (e.g. '2000-01-01T10:10:10Z') + -s SYNTAX, --syntax=SYNTAX (absent=auto) SYNTAX is the syntax that will be used. Currently supported syntaxes are "pascaligo" and "cameligo". By default, the syntax is guessed from the extension (.ligo and .mligo, respectively). + --sender=SENDER + SENDER is the sender the michelson interpreter transaction will + use. + + --source=SOURCE + SOURCE is the source the michelson interpreter transaction will + use. + --version Show version information. |} ] ; @@ -226,6 +241,9 @@ let%expect_test _ = STORAGE_EXPRESSION is the expression that will be compiled. OPTIONS + --amount=AMOUNT (absent=0) + AMOUNT is the amount the michelson interpreter will use. + --format=DISPLAY_FORMAT, --display-format=DISPLAY_FORMAT (absent=human-readable) DISPLAY_FORMAT is the format that will be used by the CLI. @@ -244,11 +262,23 @@ let%expect_test _ = compile-contract for the resulting Michelson. Available formats are 'text' (default), 'json' and 'hex'. + --predecessor-timestamp=PREDECESSOR_TIMESTAMP + PREDECESSOR_TIMESTAMP is the pedecessor_timestamp (now value) the + michelson interpreter will use (e.g. '2000-01-01T10:10:10Z') + -s SYNTAX, --syntax=SYNTAX (absent=auto) SYNTAX is the syntax that will be used. Currently supported syntaxes are "pascaligo" and "cameligo". By default, the syntax is guessed from the extension (.ligo and .mligo, respectively). + --sender=SENDER + SENDER is the sender the michelson interpreter transaction will + use. + + --source=SOURCE + SOURCE is the source the michelson interpreter transaction will + use. + --version Show version information. |} ] ; @@ -294,8 +324,8 @@ let%expect_test _ = `plain' whenever the TERM env var is `dumb' or undefined. --predecessor-timestamp=PREDECESSOR_TIMESTAMP - PREDECESSOR_TIMESTAMP is the pedecessor_timestamp the michelson - interpreter transaction will use (e.g. '2000-01-01T10:10:10Z') + PREDECESSOR_TIMESTAMP is the pedecessor_timestamp (now value) the + michelson interpreter will use (e.g. '2000-01-01T10:10:10Z') -s SYNTAX, --syntax=SYNTAX (absent=auto) SYNTAX is the syntax that will be used. Currently supported @@ -352,8 +382,8 @@ let%expect_test _ = `plain' whenever the TERM env var is `dumb' or undefined. --predecessor-timestamp=PREDECESSOR_TIMESTAMP - PREDECESSOR_TIMESTAMP is the pedecessor_timestamp the michelson - interpreter transaction will use (e.g. '2000-01-01T10:10:10Z') + PREDECESSOR_TIMESTAMP is the pedecessor_timestamp (now value) the + michelson interpreter will use (e.g. '2000-01-01T10:10:10Z') -s SYNTAX, --syntax=SYNTAX (absent=auto) SYNTAX is the syntax that will be used. Currently supported @@ -405,8 +435,8 @@ let%expect_test _ = `plain' whenever the TERM env var is `dumb' or undefined. --predecessor-timestamp=PREDECESSOR_TIMESTAMP - PREDECESSOR_TIMESTAMP is the pedecessor_timestamp the michelson - interpreter transaction will use (e.g. '2000-01-01T10:10:10Z') + PREDECESSOR_TIMESTAMP is the pedecessor_timestamp (now value) the + michelson interpreter will use (e.g. '2000-01-01T10:10:10Z') -s SYNTAX, --syntax=SYNTAX (absent=auto) SYNTAX is the syntax that will be used. Currently supported diff --git a/src/passes/3-self_ast_simplified/tezos_type_annotation.ml b/src/passes/3-self_ast_simplified/tezos_type_annotation.ml index d79ab628d..a6436257f 100644 --- a/src/passes/3-self_ast_simplified/tezos_type_annotation.ml +++ b/src/passes/3-self_ast_simplified/tezos_type_annotation.ml @@ -1,11 +1,29 @@ open Ast_simplified open Trace +module Errors = struct + let bad_string_timestamp ts loc () = + let title = (thunk ("Badly formatted timestamp \""^ts^"\"")) in + let message () = "" in + let data = [ + ("location" , fun () -> Format.asprintf "%a" Location.pp loc) + ] in + error ~data title message () +end +open Errors + let peephole_expression : expression -> expression result = fun e -> let return expression = ok { e with expression } in match e.expression with | E_ascription (e' , t) as e -> ( match (e'.expression , t.type_expression') with + | (E_literal (Literal_int i) , T_constant (TC_timestamp)) -> return @@ E_literal (Literal_timestamp i) + | (E_literal (Literal_string str) , T_constant (TC_timestamp)) -> + let%bind time = + trace_option (bad_string_timestamp str e'.location) + @@ Memory_proto_alpha.Protocol.Alpha_context.Timestamp.of_notation str in + let itime = Int64.to_int @@ Tezos_utils.Time.Protocol.to_seconds time in + return @@ E_literal (Literal_timestamp itime) | (E_literal (Literal_string str) , T_constant (TC_address)) -> return @@ E_literal (Literal_address str) | (E_literal (Literal_string str) , T_constant (TC_bytes)) -> ( let%bind e' = e'_bytes str in @@ -13,4 +31,4 @@ let peephole_expression : expression -> expression result = fun e -> ) | _ -> return e ) - | e -> return e + | e -> return e \ No newline at end of file diff --git a/src/test/contracts/bad_timestamp.ligo b/src/test/contracts/bad_timestamp.ligo new file mode 100644 index 000000000..42850560d --- /dev/null +++ b/src/test/contracts/bad_timestamp.ligo @@ -0,0 +1,7 @@ +type storage_ is timestamp + +function main(const p : unit; const s : storage_) : list(operation) * storage_ is + block { + var toto : timestamp := ("badtimestamp" : timestamp); + } + with ((nil: list(operation)), toto) \ No newline at end of file diff --git a/src/test/contracts/timestamp.ligo b/src/test/contracts/timestamp.ligo new file mode 100644 index 000000000..4e105c45f --- /dev/null +++ b/src/test/contracts/timestamp.ligo @@ -0,0 +1,3 @@ +type storage_ is timestamp + +function main(const p : unit; const s : storage_) : list(operation) * storage_ is ((nil: list(operation)), now) \ No newline at end of file