diff --git a/gitlab-pages/docs/language-basics/sets-lists-touples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md similarity index 99% rename from gitlab-pages/docs/language-basics/sets-lists-touples.md rename to gitlab-pages/docs/language-basics/sets-lists-tuples.md index 321470da7..65cb1343e 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-touples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -1,5 +1,5 @@ --- -id: sets-lists-touples +id: sets-lists-tuples title: Sets, Lists, Tuples --- diff --git a/gitlab-pages/docs/language-basics/tezos-specific.md b/gitlab-pages/docs/language-basics/tezos-specific.md new file mode 100644 index 000000000..2e40d786a --- /dev/null +++ b/gitlab-pages/docs/language-basics/tezos-specific.md @@ -0,0 +1,146 @@ +--- +id: tezos-specific +title: Tezos Domain-Specific Operations +--- + +LIGO is a language for writing Tezos smart contracts. It would be a little odd if +it didn't have any Tezos specific functions. This page will tell you about them. + +## Pack and Unpack + +Michelson provides the `PACK` and `UNPACK` instructions for data serialization. +`PACK` converts Michelson data structures to a binary format, and `UNPACK` +reverses it. This functionality can be accessed from within LIGO. + +> ⚠️ `PACK` and `UNPACK` are features of Michelson that are intended to be used by people that really know what they're doing. There are several failure cases (such as `UNPACK`ing a lambda from an untrusted source), most of which are beyond the scope of this document. Don't use these functions without doing your homework first. + + + + +```pascaligo +function id_string (const p : string) : option(string) is block { + const packed : bytes = bytes_pack(p) ; +} with (bytes_unpack(packed): option(string)) +``` + + +```cameligo +let id_string (p: string) : string option = + let packed: bytes = Bytes.pack p in + ((Bytes.unpack packed): string option) +``` + + +```reasonligo +let id_string = (p: string) : option(string) => { + let packed : bytes = Bytes.pack(p); + ((Bytes.unpack(packed)): option(string)); +}; +``` + + + +## Hashing Keys + +It's often desirable to hash a public key. In Michelson, certain data structures +such as maps will not allow the use of the `key` type. Even if this weren't the case +hashes are much smaller than keys, and storage on blockchains comes at a cost premium. +You can hash keys with the `key_hash` type and associated built in function. + + + + +```pascaligo +function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block { + var ret : bool := False ; + var kh2 : key_hash := crypto_hash_key(k2) ; + if kh1 = kh2 then ret := True else skip; +} with (ret, kh2) +``` + + +```cameligo +let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash = + let kh2 : key_hash = Crypto.hash_key k2 in + if kh1 = kh2 + then (true, kh2) + else (false, kh2) +``` + + +```reasonligo +let check_hash_key = (kh1_k2: (key_hash, key)) : (bool, key_hash) => { + let kh1, k2 = kh1_k2; + let kh2 : key_hash = Crypto.hash_key(k2); + if (kh1 == kh2) { + (true, kh2); + } + else { + (false, kh2); + } +}; +``` + + + +## Checking Signatures + +Sometimes a contract will want to check that a message has been signed by a +particular key. For example, a point-of-sale system might want a customer to +sign a transaction so it can be processed asynchronously. You can do this in LIGO +using the `key` and `signature` types. + +> ⚠️ There is no way to *generate* a signed message in LIGO. This is because that would require storing a private key on chain, at which point it isn't very private anymore. + + + + +```pascaligo +function check_signature + (const pk: key; + const signed: signature; + const msg: bytes) : bool + is crypto_check(pk, signed, msg) +``` + + +```cameligo +let check_signature (pk, signed, msg: key * signature * bytes) : bool = + Crypto.check pk signed msg +``` + + +```reasonligo +let check_signature = (param: (key, signature, bytes)) : bool => { + let pk, signed, msg = param; + Crypto.check(pk, signed, msg); +}; +``` + + + +## Getting The Contract's Own Address + +Often you want to get the address of the contract being executed. You can do it with +`self_address`. + +> ⚠️ Due to limitations in Michelson, self_address in a contract is only allowed at the entry-point level. Using it in a utility function will cause an error. + + + + +```pascaligo +const current_addr : address = self_address; +``` + + +```cameligo +let current_addr : address = Current.self_address +``` + + +```reasonligo +let current_addr : address = Current.self_address; +``` + + diff --git a/gitlab-pages/website/sidebars.json b/gitlab-pages/website/sidebars.json index 00fe28d25..717fa03ec 100644 --- a/gitlab-pages/website/sidebars.json +++ b/gitlab-pages/website/sidebars.json @@ -11,7 +11,8 @@ "language-basics/loops", "language-basics/unit-option-pattern-matching", "language-basics/maps-records", - "language-basics/sets-lists-touples" + "language-basics/sets-lists-tuples", + "language-basics/tezos-specific" ], "Advanced": [ "advanced/timestamps-addresses", diff --git a/src/test/contracts/bytes_unpack.mligo b/src/test/contracts/bytes_unpack.mligo new file mode 100644 index 000000000..e33f09b47 --- /dev/null +++ b/src/test/contracts/bytes_unpack.mligo @@ -0,0 +1,11 @@ +let id_string (p: string) : string option = + let packed: bytes = Bytes.pack p in + ((Bytes.unpack packed): string option) + +let id_int (p: int) : int option = + let packed: bytes = Bytes.pack p in + ((Bytes.unpack packed): int option) + +let id_address (p: address) : address option = + let packed: bytes = Bytes.pack p in + ((Bytes.unpack packed): address option) diff --git a/src/test/contracts/bytes_unpack.religo b/src/test/contracts/bytes_unpack.religo new file mode 100644 index 000000000..86b291ecc --- /dev/null +++ b/src/test/contracts/bytes_unpack.religo @@ -0,0 +1,14 @@ +let id_string = (p: string) : option(string) => { + let packed : bytes = Bytes.pack(p); + ((Bytes.unpack(packed)): option(string)); +}; + +let id_int = (p: int) : option(int) => { + let packed: bytes = Bytes.pack(p); + ((Bytes.unpack(packed)): option(int)); +}; + +let id_address = (p: address) : option(address) => { + let packed: bytes = Bytes.pack(p); + ((Bytes.unpack(packed)): option(address)); +}; diff --git a/src/test/contracts/check_signature.ligo b/src/test/contracts/check_signature.ligo new file mode 100644 index 000000000..231b726ea --- /dev/null +++ b/src/test/contracts/check_signature.ligo @@ -0,0 +1,2 @@ +function check_signature (const pk: key; const signed: signature; const msg: bytes) : bool is + crypto_check(pk, signed, msg) diff --git a/src/test/contracts/check_signature.mligo b/src/test/contracts/check_signature.mligo new file mode 100644 index 000000000..ecd56eb4b --- /dev/null +++ b/src/test/contracts/check_signature.mligo @@ -0,0 +1,2 @@ +let check_signature (pk, signed, msg: key * signature * bytes) : bool = + Crypto.check pk signed msg diff --git a/src/test/contracts/check_signature.religo b/src/test/contracts/check_signature.religo new file mode 100644 index 000000000..9d2f266ce --- /dev/null +++ b/src/test/contracts/check_signature.religo @@ -0,0 +1,4 @@ +let check_signature = (param: (key, signature, bytes)) : bool => { + let pk, signed, msg = param; + Crypto.check(pk, signed, msg); +}; diff --git a/src/test/contracts/key_hash.mligo b/src/test/contracts/key_hash.mligo new file mode 100644 index 000000000..830ea3496 --- /dev/null +++ b/src/test/contracts/key_hash.mligo @@ -0,0 +1,5 @@ +let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash = + let kh2 : key_hash = Crypto.hash_key k2 in + if kh1 = kh2 + then (true, kh2) + else (false, kh2) diff --git a/src/test/contracts/key_hash.religo b/src/test/contracts/key_hash.religo new file mode 100644 index 000000000..f3b8e8976 --- /dev/null +++ b/src/test/contracts/key_hash.religo @@ -0,0 +1,10 @@ +let check_hash_key = (kh1_k2: (key_hash, key)) : (bool, key_hash) => { + let kh1, k2 = kh1_k2; + let kh2 : key_hash = Crypto.hash_key(k2); + if (kh1 == kh2) { + (true, kh2); + } + else { + (false, kh2); + } +}; diff --git a/src/test/integration_tests.ml b/src/test/integration_tests.ml index 6abe31226..4a0a190c7 100644 --- a/src/test/integration_tests.ml +++ b/src/test/integration_tests.ml @@ -1901,6 +1901,67 @@ let key_hash () : unit result = let%bind () = expect_eq program "check_hash_key" make_input make_expected in ok () +let key_hash_mligo () : unit result = + let open Tezos_crypto in + let (raw_pkh,raw_pk,_) = Signature.generate_key () in + let pkh_str = Signature.Public_key_hash.to_b58check raw_pkh in + let pk_str = Signature.Public_key.to_b58check raw_pk in + let%bind program = mtype_file "./contracts/key_hash.mligo" in + let make_input = e_pair (e_key_hash pkh_str) (e_key pk_str) in + let make_expected = e_pair (e_bool true) (e_key_hash pkh_str) in + let%bind () = expect_eq program "check_hash_key" make_input make_expected in + ok () + +let key_hash_religo () : unit result = + let open Tezos_crypto in + let (raw_pkh,raw_pk,_) = Signature.generate_key () in + let pkh_str = Signature.Public_key_hash.to_b58check raw_pkh in + let pk_str = Signature.Public_key.to_b58check raw_pk in + let%bind program = retype_file "./contracts/key_hash.religo" in + let make_input = e_pair (e_key_hash pkh_str) (e_key pk_str) in + let make_expected = e_pair (e_bool true) (e_key_hash pkh_str) in + let%bind () = expect_eq program "check_hash_key" make_input make_expected in + ok () + +let check_signature () : unit result = + let open Tezos_crypto in + let (_, raw_pk, sk) = Signature.generate_key () in + let pk_str = Signature.Public_key.to_b58check raw_pk in + let signed = Signature.sign sk (Bytes.of_string "hello world") in + let%bind program = type_file "./contracts/check_signature.ligo" in + let make_input = e_tuple [e_key pk_str ; + e_signature (Signature.to_b58check signed) ; + e_bytes_ofbytes (Bytes.of_string "hello world")] in + let make_expected = e_bool true in + let%bind () = expect_eq program "check_signature" make_input make_expected in + ok () + +let check_signature_mligo () : unit result = + let open Tezos_crypto in + let (_, raw_pk, sk) = Signature.generate_key () in + let pk_str = Signature.Public_key.to_b58check raw_pk in + let signed = Signature.sign sk (Bytes.of_string "hello world") in + let%bind program = mtype_file "./contracts/check_signature.mligo" in + let make_input = e_tuple [e_key pk_str ; + e_signature (Signature.to_b58check signed) ; + e_bytes_ofbytes (Bytes.of_string "hello world")] in + let make_expected = e_bool true in + let%bind () = expect_eq program "check_signature" make_input make_expected in + ok () + +let check_signature_religo () : unit result = + let open Tezos_crypto in + let (_, raw_pk, sk) = Signature.generate_key () in + let pk_str = Signature.Public_key.to_b58check raw_pk in + let signed = Signature.sign sk (Bytes.of_string "hello world") in + let%bind program = retype_file "./contracts/check_signature.religo" in + let make_input = e_tuple [e_key pk_str ; + e_signature (Signature.to_b58check signed) ; + e_bytes_ofbytes (Bytes.of_string "hello world")] in + let make_expected = e_bool true in + let%bind () = expect_eq program "check_signature" make_input make_expected in + ok () + let curry () : unit result = let%bind program = mtype_file "./contracts/curry.mligo" in let%bind () = @@ -1975,6 +2036,26 @@ let bytes_unpack () : unit result = let%bind () = expect_eq program "id_address" (e_address addr) (e_some (e_address addr)) in ok () +let bytes_unpack_mligo () : unit result = + let%bind program = mtype_file "./contracts/bytes_unpack.mligo" in + let%bind () = expect_eq program "id_string" (e_string "teststring") (e_some (e_string "teststring")) in + let%bind () = expect_eq program "id_int" (e_int 42) (e_some (e_int 42)) in + let open Proto_alpha_utils.Memory_proto_alpha in + let addr = Protocol.Alpha_context.Contract.to_b58check @@ + (List.nth dummy_environment.identities 0).implicit_contract in + let%bind () = expect_eq program "id_address" (e_address addr) (e_some (e_address addr)) in + ok () + +let bytes_unpack_religo () : unit result = + let%bind program = retype_file "./contracts/bytes_unpack.religo" in + let%bind () = expect_eq program "id_string" (e_string "teststring") (e_some (e_string "teststring")) in + let%bind () = expect_eq program "id_int" (e_int 42) (e_some (e_int 42)) in + let open Proto_alpha_utils.Memory_proto_alpha in + let addr = Protocol.Alpha_context.Contract.to_b58check @@ + (List.nth dummy_environment.identities 0).implicit_contract in + let%bind () = expect_eq program "id_address" (e_address addr) (e_some (e_address addr)) in + ok () + let empty_case () : unit result = let%bind program = type_file "./contracts/empty_case.ligo" in let%bind () = @@ -2019,7 +2100,14 @@ let empty_case_religo () : unit result = let main = test_suite "Integration (End to End)" [ test "bytes unpack" bytes_unpack ; + test "bytes unpack (mligo)" bytes_unpack_mligo ; + test "bytes unpack (religo)" bytes_unpack_religo ; test "key hash" key_hash ; + test "key hash (mligo)" key_hash_mligo ; + test "key hash (religo)" key_hash_religo ; + test "check signature" check_signature ; + test "check signature (mligo)" check_signature_mligo ; + test "check signature (religo)" check_signature_religo ; test "chain id" chain_id ; test "type alias" type_alias ; test "function" function_ ; diff --git a/src/test/md_file_tests.ml b/src/test/md_file_tests.ml index 0401648d0..86aefeb89 100644 --- a/src/test/md_file_tests.ml +++ b/src/test/md_file_tests.ml @@ -100,10 +100,11 @@ let md_files = [ "/gitlab-pages/docs/language-basics/strings.md"; "/gitlab-pages/docs/language-basics/maps-records.md"; "/gitlab-pages/docs/language-basics/variables-and-constants.md"; - "/gitlab-pages/docs/language-basics/sets-lists-touples.md"; + "/gitlab-pages/docs/language-basics/sets-lists-tuples.md"; "/gitlab-pages/docs/language-basics/operators.md"; "/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md"; "/gitlab-pages/docs/language-basics/loops.md"; + "/gitlab-pages/docs/language-basics/tezos-specific.md"; "/gitlab-pages/docs/contributors/big-picture/back-end.md"; "/gitlab-pages/docs/contributors/big-picture/vendors.md"; "/gitlab-pages/docs/contributors/big-picture/front-end.md";