From 59448e1d91544ef63b2a09c810d867d58af9b1dc Mon Sep 17 00:00:00 2001 From: John David Pressman Date: Tue, 4 Feb 2020 20:27:56 -0800 Subject: [PATCH 1/3] Add partial draft of Current library documentation and unit tests for Current.amount --- gitlab-pages/docs/reference/current.md | 217 +++++++++++++++++++++++++ src/test/contracts/amount.ligo | 8 + src/test/contracts/amount.mligo | 2 +- src/test/contracts/amount.religo | 7 + src/test/integration_tests.ml | 38 +++++ 5 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 gitlab-pages/docs/reference/current.md create mode 100644 src/test/contracts/amount.ligo create mode 100644 src/test/contracts/amount.religo diff --git a/gitlab-pages/docs/reference/current.md b/gitlab-pages/docs/reference/current.md new file mode 100644 index 000000000..39afd3e76 --- /dev/null +++ b/gitlab-pages/docs/reference/current.md @@ -0,0 +1,217 @@ +--- +id: current-reference +title: Current +--- + +## Current.balance() : tez + +Get the balance for the contract. + + + + +```pascaligo +function main (const p : unit; const s: tez) : list(operation) * storage is + ((nil : list(operation)), balance) +``` + +```cameligo +let main (p, s : unit * storage) = + ([] : operation list), balance +``` + +```reasonligo +let main = (p: unit, storage) => ([]: list(operation), balance); +``` + + + +## Current.time() : timestamp + +Returns the current time as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time). + +In LIGO, timestamps are type compatible in operations with `int`(s). This lets you set e.g. time constraints for your smart contracts like this: + +### Examples + +#### 24 hours from now + + +```pascaligo group=b +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; +``` + + +```cameligo group=b +let today: timestamp = Current.time +let one_day: int = 86400 +let in_24_hrs: timestamp = today + one_day +let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp) +let one_day_later: timestamp = some_date + one_day +``` + + +```reasonligo group=b +let today: timestamp = Current.time; +let one_day: int = 86400; +let in_24_hrs: timestamp = today + one_day; +let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp); +let one_day_later: timestamp = some_date + one_day; +``` + + + +#### 24 hours ago + + +```pascaligo group=c +const today: timestamp = now; +const one_day: int = 86400; +const in_24_hrs: timestamp = today - one_day; +``` + + +```cameligo group=c +let today: timestamp = Current.time +let one_day: int = 86400 +let in_24_hrs: timestamp = today - one_day +``` + + +```reasonligo group=c +let today: timestamp = Current.time; +let one_day: int = 86400; +let in_24_hrs: timestamp = today - one_day; +``` + + + +#### Comparing timestamps + +You can also compare timestamps using the same comparison operators as for numbers: + + + +```pascaligo group=c +const not_tommorow: bool = (now = in_24_hrs) +``` + + +```cameligo group=c +let not_tomorrow: bool = (Current.time = in_24_hrs) +``` + + +```reasonligo group=c +let not_tomorrow: bool = (Current.time == in_24_hrs); +``` + + + + +## Current.amount() : tez + +Get the amount of tez provided by the sender to complete this transaction. + + + + +```pascaligo +function check (const p: unit) : int is + begin + var result : int := 0; + if amount = 100tz then + result := 42 + else + result := 0 + end with result +``` + + +```cameligo +let check_ (p: unit) : int = if Current.amount = 100tz then 42 else 0 +``` + + +```reasonligo +let check_ = (p: unit) : int => + if (Current.amount == 100tz) { + 42; + } + else { + 0; + }; +``` + + + +## Current.gas() + +## Current.sender() : address + +Get the address that initiated the current transaction. + +## Current.address + +## Current.self_address + +## Current.implicit_account + + + + +```pascaligo +function main (const kh: key_hash) : contract(unit) is implicit_account(kh) +``` + + +```cameligo +let main (kh: key_hash) : unit contract = Current.implicit_account kh +``` + + +```reasonligo +let main = (kh: key_hash): contract(unit) => Current.implicit_account(kh); +``` + + + +## Current.source + +## Current.failwith + + + + +```pascaligo +function main (const p : param; const s : unit) : list(operation) * unit is + block { + case p of + | Zero (n) -> if n > 0n then failwith("fail") else skip + | Pos (n) -> if n > 0n then skip else failwith("fail") + end + } + with ((nil : list(operation)), s) +``` + + +```cameligo +let main (p: unit) storage = + if true then failwith "This contract always fails" else () +``` + + +```reasonligo +let main = (p: unit, storage) => + if (true) { + failwith("This contract always fails"); + } else { + (); + }; +``` + + diff --git a/src/test/contracts/amount.ligo b/src/test/contracts/amount.ligo new file mode 100644 index 000000000..8d4961be8 --- /dev/null +++ b/src/test/contracts/amount.ligo @@ -0,0 +1,8 @@ +function check (const p: unit) : int is + begin + var result : int := 0; + if amount = 100tz then + result := 42 + else + result := 0 + end with result diff --git a/src/test/contracts/amount.mligo b/src/test/contracts/amount.mligo index 995f165fe..8e31e2d0c 100644 --- a/src/test/contracts/amount.mligo +++ b/src/test/contracts/amount.mligo @@ -1 +1 @@ -let check = if Current.amount > 100tz then 42 else 0 \ No newline at end of file +let check_ (p: unit) : int = if Current.amount = 100tz then 42 else 0 diff --git a/src/test/contracts/amount.religo b/src/test/contracts/amount.religo new file mode 100644 index 000000000..9d19491b8 --- /dev/null +++ b/src/test/contracts/amount.religo @@ -0,0 +1,7 @@ +let check_ = (p: unit) : int => + if (Current.amount == 100tz) { + 42; + } + else { + 0; + }; diff --git a/src/test/integration_tests.ml b/src/test/integration_tests.ml index 204d2fc55..fc9eec250 100644 --- a/src/test/integration_tests.ml +++ b/src/test/integration_tests.ml @@ -1774,6 +1774,41 @@ let balance_constant_religo () : unit result = let expected = e_tuple [e_list []; e_mutez 4000000000000] in expect_eq program "main" input expected +let amount () : unit result = + let%bind program = type_file "./contracts/amount.ligo" in + let input = e_unit () in + let expected = e_int 42 in + let amount = + match Memory_proto_alpha.Protocol.Alpha_context.Tez.of_string "100" with + | Some t -> t + | None -> Memory_proto_alpha.Protocol.Alpha_context.Tez.one + in + let options = Proto_alpha_utils.Memory_proto_alpha.make_options ~amount () in + expect_eq ~options program "check" input expected + +let amount_mligo () : unit result = + let%bind program = mtype_file "./contracts/amount.mligo" in + let input = e_unit () in + let expected = e_int 42 in + let amount = + match Memory_proto_alpha.Protocol.Alpha_context.Tez.of_string "100" with + | Some t -> t + | None -> Memory_proto_alpha.Protocol.Alpha_context.Tez.one + in + let options = Proto_alpha_utils.Memory_proto_alpha.make_options ~amount () in + expect_eq ~options program "check_" input expected + +let amount_religo () : unit result = + let%bind program = retype_file "./contracts/amount.religo" in + let input = e_unit () in + let expected = e_int 42 in + let amount = + match Memory_proto_alpha.Protocol.Alpha_context.Tez.of_string "100" with + | Some t -> t + | None -> Memory_proto_alpha.Protocol.Alpha_context.Tez.one + in + let options = Proto_alpha_utils.Memory_proto_alpha.make_options ~amount () in + expect_eq ~options program "check_" input expected let addr_test program = let open Proto_alpha_utils.Memory_proto_alpha in @@ -2339,6 +2374,9 @@ let main = test_suite "Integration (End to End)" [ test "balance constant" balance_constant ; test "balance constant (mligo)" balance_constant_mligo ; test "balance constant (religo)" balance_constant_religo ; + test "amount" amount ; + test "amount (mligo)" amount_mligo ; + test "amount (religo)" amount_religo ; test "address" address ; test "address (mligo)" address_mligo ; test "address (religo)" address_religo ; From a0c2978b9fb575853b95b31833bc3bbc1946e755 Mon Sep 17 00:00:00 2001 From: John David Pressman Date: Tue, 25 Feb 2020 05:13:25 -0800 Subject: [PATCH 2/3] Add Current.x reference page to the docs --- gitlab-pages/docs/reference/current.md | 121 +++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/gitlab-pages/docs/reference/current.md b/gitlab-pages/docs/reference/current.md index 39afd3e76..554f4e0a5 100644 --- a/gitlab-pages/docs/reference/current.md +++ b/gitlab-pages/docs/reference/current.md @@ -1,6 +1,6 @@ --- id: current-reference -title: Current +title: Current - Things relating to the current execution context --- ## Current.balance() : tez @@ -149,17 +149,87 @@ let check_ = (p: unit) : int => -## Current.gas() - ## Current.sender() : address Get the address that initiated the current transaction. -## Current.address + -## Current.self_address + +```pascaligo +function main (const p: unit) : address is sender +``` -## Current.implicit_account + +```cameligo +let main (p: unit) : address = Current.sender +``` + + +```reasonligo +let main = (p: unit) : address => Current.sender; +``` + + + +## Current.address(c: a' contract) : address + +Get the address associated with a `contract`. + + + + +```pascaligo +function main (const p : key_hash) : address is block { + const c : contract(unit) = implicit_account(p) ; +} with address(c) +``` + + +```cameligo +let main (p : key_hash) = + let c : unit contract = Current.implicit_account p in + Current.address c +``` + + +```reasonligo +let main = (p : key_hash) : address => { + let c : contract(unit) = Current.implicit_account(p) ; + Current.address(c) ; +}; +``` + + + +## Current.self_address() : address + +Get the address of the currently running contract. + + + + +```pascaligo +function main (const p: unit) : address is self_address +``` + + +```cameligo +let main (p: unit) : address = Current.self_address +``` + + +```reasonligo +let main = (p: unit): address => Current.self_address; +``` + + + +## Current.implicit_account(p: key_hash) : a' contract + +Get the default contract associated with an on-chain keypair. This contract +doesn't execute code, instead it exists to receive money on behalf of a keys +owner. @@ -180,9 +250,44 @@ let main = (kh: key_hash): contract(unit) => Current.implicit_account(kh); -## Current.source +## Current.source() : address -## Current.failwith +Get the _originator_ of the current transaction. That is, if a chain of transactions +led to the current execution get the address that began the chain. Not to be confused +with `Current.sender`, which gives the address of the contract or user which directly +caused the current transaction. + +> ⚠️ +> There are a few caveats you should keep in mind before using `SOURCE` over `SENDER`: +> +> 1. SOURCE will never be a contract, so if you want to allow contracts (multisigs etc) to operate your contract, you need to use SENDER +> 2. https://vessenes.com/tx-origin-and-ethereum-oh-my/ -- in general it is somewhat unsafe to assume that SOURCE understands everything that's going to happen in a transaction. If SOURCE transfers to a malicious (or sufficiently attackable) contract, that contract might potentially transfer to yours, without SOURCE's consent. So if you are using SOURCE for authentication, you risk being confused. A good historical example of this is bakers paying out delegation rewards. Naive bakers did (and probably still do) just use tezos-client to transfer to whatever KT1 delegates they had, even if those KT1 were malicious scripts. + + + + +```pascaligo +function main (const p: unit) : address is source +``` + + +```cameligo +let main (p: unit) : address = Current.source +``` + + +```reasonligo +let main = (p: unit) : address => Current.source; +``` + + + +## Current.failwith(error_message: string) : a' + +Cause the contract to fail with an error message. + +> ⚠ Using this currently requires a type annotation on the failwith to unify it +> with the type of whatever other code branch it's on. From 936418615ea91a0580a93561eb84128c14f9ec9b Mon Sep 17 00:00:00 2001 From: John David Pressman Date: Tue, 25 Feb 2020 05:26:04 -0800 Subject: [PATCH 3/3] Add Current.x reference page to doc tests --- gitlab-pages/docs/reference/current.md | 17 +++++++---------- src/test/md_file_tests.ml | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/gitlab-pages/docs/reference/current.md b/gitlab-pages/docs/reference/current.md index 554f4e0a5..c9f1945d8 100644 --- a/gitlab-pages/docs/reference/current.md +++ b/gitlab-pages/docs/reference/current.md @@ -11,17 +11,17 @@ Get the balance for the contract. ```pascaligo -function main (const p : unit; const s: tez) : list(operation) * storage is +function main (const p : unit; const s: tez) : list(operation) * tez is ((nil : list(operation)), balance) ``` ```cameligo -let main (p, s : unit * storage) = +let main (p, s : unit * tez) = ([] : operation list), balance ``` ```reasonligo -let main = (p: unit, storage) => ([]: list(operation), balance); +let main = ((p,s): (unit, tez)) => ([]: list(operation), balance); ``` @@ -293,25 +293,22 @@ Cause the contract to fail with an error message. ```pascaligo -function main (const p : param; const s : unit) : list(operation) * unit is +function main (const p : int; const s : unit) : list(operation) * unit is block { - case p of - | Zero (n) -> if n > 0n then failwith("fail") else skip - | Pos (n) -> if n > 0n then skip else failwith("fail") - end + if p > 10 then failwith("fail") else skip; } with ((nil : list(operation)), s) ``` ```cameligo -let main (p: unit) storage = +let main (p,s: unit * unit) = if true then failwith "This contract always fails" else () ``` ```reasonligo -let main = (p: unit, storage) => +let main = ((p,s): (unit, unit)) => if (true) { failwith("This contract always fails"); } else { diff --git a/src/test/md_file_tests.ml b/src/test/md_file_tests.ml index 15e7e1767..c728a929d 100644 --- a/src/test/md_file_tests.ml +++ b/src/test/md_file_tests.ml @@ -128,6 +128,7 @@ let md_files = [ "/gitlab-pages/docs/reference/big_map.md"; "/gitlab-pages/docs/reference/string.md"; "/gitlab-pages/docs/reference/crypto.md"; + "/gitlab-pages/docs/reference/current.md"; ] let md_root = "../../gitlab-pages/docs/language-basics/"