Merge branch 'docs/michelson-specific' into 'dev'
[LIGO-358] Add Tezos-specific LIGO features docs See merge request ligolang/ligo!335
This commit is contained in:
commit
b50479b96b
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
id: sets-lists-touples
|
id: sets-lists-tuples
|
||||||
title: Sets, Lists, Tuples
|
title: Sets, Lists, Tuples
|
||||||
---
|
---
|
||||||
|
|
146
gitlab-pages/docs/language-basics/tezos-specific.md
Normal file
146
gitlab-pages/docs/language-basics/tezos-specific.md
Normal file
@ -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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function id_string (const p : string) : option(string) is block {
|
||||||
|
const packed : bytes = bytes_pack(p) ;
|
||||||
|
} with (bytes_unpack(packed): option(string))
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let id_string (p: string) : string option =
|
||||||
|
let packed: bytes = Bytes.pack p in
|
||||||
|
((Bytes.unpack packed): string option)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let id_string = (p: string) : option(string) => {
|
||||||
|
let packed : bytes = Bytes.pack(p);
|
||||||
|
((Bytes.unpack(packed)): option(string));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```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-->
|
||||||
|
```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-->
|
||||||
|
```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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function check_signature
|
||||||
|
(const pk: key;
|
||||||
|
const signed: signature;
|
||||||
|
const msg: bytes) : bool
|
||||||
|
is crypto_check(pk, signed, msg)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let check_signature (pk, signed, msg: key * signature * bytes) : bool =
|
||||||
|
Crypto.check pk signed msg
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let check_signature = (param: (key, signature, bytes)) : bool => {
|
||||||
|
let pk, signed, msg = param;
|
||||||
|
Crypto.check(pk, signed, msg);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
const current_addr : address = self_address;
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let current_addr : address = Current.self_address
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let current_addr : address = Current.self_address;
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
@ -11,7 +11,8 @@
|
|||||||
"language-basics/loops",
|
"language-basics/loops",
|
||||||
"language-basics/unit-option-pattern-matching",
|
"language-basics/unit-option-pattern-matching",
|
||||||
"language-basics/maps-records",
|
"language-basics/maps-records",
|
||||||
"language-basics/sets-lists-touples"
|
"language-basics/sets-lists-tuples",
|
||||||
|
"language-basics/tezos-specific"
|
||||||
],
|
],
|
||||||
"Advanced": [
|
"Advanced": [
|
||||||
"advanced/timestamps-addresses",
|
"advanced/timestamps-addresses",
|
||||||
|
11
src/test/contracts/bytes_unpack.mligo
Normal file
11
src/test/contracts/bytes_unpack.mligo
Normal file
@ -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)
|
14
src/test/contracts/bytes_unpack.religo
Normal file
14
src/test/contracts/bytes_unpack.religo
Normal file
@ -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));
|
||||||
|
};
|
2
src/test/contracts/check_signature.ligo
Normal file
2
src/test/contracts/check_signature.ligo
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
function check_signature (const pk: key; const signed: signature; const msg: bytes) : bool is
|
||||||
|
crypto_check(pk, signed, msg)
|
2
src/test/contracts/check_signature.mligo
Normal file
2
src/test/contracts/check_signature.mligo
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
let check_signature (pk, signed, msg: key * signature * bytes) : bool =
|
||||||
|
Crypto.check pk signed msg
|
4
src/test/contracts/check_signature.religo
Normal file
4
src/test/contracts/check_signature.religo
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
let check_signature = (param: (key, signature, bytes)) : bool => {
|
||||||
|
let pk, signed, msg = param;
|
||||||
|
Crypto.check(pk, signed, msg);
|
||||||
|
};
|
5
src/test/contracts/key_hash.mligo
Normal file
5
src/test/contracts/key_hash.mligo
Normal file
@ -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)
|
10
src/test/contracts/key_hash.religo
Normal file
10
src/test/contracts/key_hash.religo
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
};
|
@ -1901,6 +1901,67 @@ let key_hash () : unit result =
|
|||||||
let%bind () = expect_eq program "check_hash_key" make_input make_expected in
|
let%bind () = expect_eq program "check_hash_key" make_input make_expected in
|
||||||
ok ()
|
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 curry () : unit result =
|
||||||
let%bind program = mtype_file "./contracts/curry.mligo" in
|
let%bind program = mtype_file "./contracts/curry.mligo" in
|
||||||
let%bind () =
|
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
|
let%bind () = expect_eq program "id_address" (e_address addr) (e_some (e_address addr)) in
|
||||||
ok ()
|
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 empty_case () : unit result =
|
||||||
let%bind program = type_file "./contracts/empty_case.ligo" in
|
let%bind program = type_file "./contracts/empty_case.ligo" in
|
||||||
let%bind () =
|
let%bind () =
|
||||||
@ -2019,7 +2100,14 @@ let empty_case_religo () : unit result =
|
|||||||
|
|
||||||
let main = test_suite "Integration (End to End)" [
|
let main = test_suite "Integration (End to End)" [
|
||||||
test "bytes unpack" bytes_unpack ;
|
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" 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 "chain id" chain_id ;
|
||||||
test "type alias" type_alias ;
|
test "type alias" type_alias ;
|
||||||
test "function" function_ ;
|
test "function" function_ ;
|
||||||
|
@ -100,10 +100,11 @@ let md_files = [
|
|||||||
"/gitlab-pages/docs/language-basics/strings.md";
|
"/gitlab-pages/docs/language-basics/strings.md";
|
||||||
"/gitlab-pages/docs/language-basics/maps-records.md";
|
"/gitlab-pages/docs/language-basics/maps-records.md";
|
||||||
"/gitlab-pages/docs/language-basics/variables-and-constants.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/operators.md";
|
||||||
"/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md";
|
"/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md";
|
||||||
"/gitlab-pages/docs/language-basics/loops.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/back-end.md";
|
||||||
"/gitlab-pages/docs/contributors/big-picture/vendors.md";
|
"/gitlab-pages/docs/contributors/big-picture/vendors.md";
|
||||||
"/gitlab-pages/docs/contributors/big-picture/front-end.md";
|
"/gitlab-pages/docs/contributors/big-picture/front-end.md";
|
||||||
|
Loading…
Reference in New Issue
Block a user