Merge branch 'enfore_not_allowing_big_maps' into 'dev'

Give an error when nesting a big_map inside another big_map

See merge request ligolang/ligo!518
This commit is contained in:
Sander 2020-03-26 10:39:18 +00:00
commit 91a6affdad
10 changed files with 177 additions and 19 deletions

View File

@ -9,8 +9,12 @@ import Syntax from '@theme/Syntax';
import SyntaxTitle from '@theme/SyntaxTitle'; import SyntaxTitle from '@theme/SyntaxTitle';
A lazily deserialized map that's intended to store large amounts of data. A lazily deserialized map that's intended to store large amounts of data.
Lazily means that storage is read or written per key on demand. Therefore
there are no `map`, `fold`, and `iter` operations as in
[Map](./map-reference).
The gast costs of deserialized maps are higher than standard maps as data is lazily deserialized. The gast costs of big maps are higher than standard maps as data is lazily
deserialized.
<SyntaxTitle syntax="pascaligo"> <SyntaxTitle syntax="pascaligo">
type big_map ('key, 'value) type big_map ('key, 'value)
@ -47,15 +51,17 @@ type register = (address, move) big_map
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
The type of a big map from values of type `key` to The type of a big map from values of type `key` to
values of type `value` is `big_map (key, value)`. values of type `value` is `big_map(key, value)`.
```reasonligo group=big_map ```reasonligo group=big_map
type move = (int, int); type move = (int, int);
type register = big_map (address, move); type register = big_map(address, move);
``` ```
</Syntax> </Syntax>
Be aware that a `big_map` cannot appear inside another `big_map`.
<SyntaxTitle syntax="pascaligo"> <SyntaxTitle syntax="pascaligo">
function empty : big_map ('key, 'value) function empty : big_map ('key, 'value)
</SyntaxTitle> </SyntaxTitle>
@ -63,7 +69,7 @@ function empty : big_map ('key, 'value)
val empty : ('key, 'value) big_map val empty : ('key, 'value) big_map
</SyntaxTitle> </SyntaxTitle>
<SyntaxTitle syntax="reasonligo"> <SyntaxTitle syntax="reasonligo">
let empty: big_map ('key, 'value) let empty: big_map('key, 'value)
</SyntaxTitle> </SyntaxTitle>
Create an empty big_map. Create an empty big_map.
@ -91,7 +97,7 @@ let empty : register = Big_map.empty
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
```reasonligo group=big_map ```reasonligo group=big_map
let empty : register = Big_map.empty let empty: register = Big_map.empty
``` ```
</Syntax> </Syntax>
@ -141,7 +147,7 @@ let moves : register =
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
```reasonligo group=big_map ```reasonligo group=big_map
let moves : register = let moves: register =
Big_map.literal ([ Big_map.literal ([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)),
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]);
@ -156,7 +162,7 @@ function find_opt : 'key -> big_map ('key, 'value) -> option 'value
val find_opt : 'key -> ('key, 'value) big_map -> 'value option val find_opt : 'key -> ('key, 'value) big_map -> 'value option
</SyntaxTitle> </SyntaxTitle>
<SyntaxTitle syntax="reasonligo"> <SyntaxTitle syntax="reasonligo">
let find_opt : ('key, big_map ('key, 'value)) => option ('value) let find_opt: ('key, big_map ('key, 'value)) => option ('value)
</SyntaxTitle> </SyntaxTitle>
Retrieve a value from a big map with the given key. Retrieve a value from a big map with the given key.
@ -191,8 +197,8 @@ let my_balance : move option =
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
```reasonligo group=big_map ```reasonligo group=big_map
let my_balance : option (move) = let my_balance: option (move) =
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, moves); Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
``` ```
</Syntax> </Syntax>
@ -204,7 +210,7 @@ function update : 'key -> option 'value -> big_map ('key, 'value) -> big_map ('k
val update: 'key -> 'value option -> ('key, 'value) big_map -> ('key, 'value) big_map val update: 'key -> 'value option -> ('key, 'value) big_map -> ('key, 'value) big_map
</SyntaxTitle> </SyntaxTitle>
<SyntaxTitle syntax="reasonligo"> <SyntaxTitle syntax="reasonligo">
let update: ('key, option('value), big_map ('key, 'value)) => big_map ('key, 'value) let update: ('key, option('value), big_map('key, 'value)) => big_map('key, 'value)
</SyntaxTitle> </SyntaxTitle>
Note: when `None` is used as a value, the value is removed from the big_map. Note: when `None` is used as a value, the value is removed from the big_map.
@ -255,9 +261,9 @@ let updated_map : register =
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
```reasonligo group=big_map ```reasonligo group=big_map
let updated_map : register = let updated_map: register =
Big_map.update Big_map.update
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some ((4,9)), moves); (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
``` ```
</Syntax> </Syntax>
@ -292,7 +298,7 @@ let add (m : register) : register =
```reasonligo group=big_map ```reasonligo group=big_map
let add = (m: register): register => let add = (m: register): register =>
Big_map.add Big_map.add
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (4,9), m); (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (4,9), m);
``` ```
</Syntax> </Syntax>
@ -305,7 +311,7 @@ function remove: 'key -> big_map ('key, 'value) -> big_map ('key, 'value)
val remove: 'key -> ('key, 'value) big_map -> ('key, 'value) big_map val remove: 'key -> ('key, 'value) big_map -> ('key, 'value) big_map
</SyntaxTitle> </SyntaxTitle>
<SyntaxTitle syntax="reasonligo"> <SyntaxTitle syntax="reasonligo">
let remove: ('key, big_map ('key, 'value)) => big_map ('key, 'value) let remove: ('key, big_map('key, 'value)) => big_map('key, 'value)
</SyntaxTitle> </SyntaxTitle>
<Syntax syntax="pascaligo"> <Syntax syntax="pascaligo">
@ -340,8 +346,8 @@ let updated_map : register =
<Syntax syntax="reasonligo"> <Syntax syntax="reasonligo">
```reasonligo group=big_map ```reasonligo group=big_map
let updated_map : register = let updated_map: register =
Big_map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) Big_map.remove(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves)
``` ```
</Syntax> </Syntax>

View File

@ -1338,6 +1338,58 @@ let%expect_test _ =
ligo: in file "self_bad_entrypoint_format.ligo", line 8, characters 52-58. bad entrypoint format: entrypoint "Toto" is badly formatted. We expect "%bar" for entrypoint Bar and "%default" when no entrypoint used {"location":"in file \"self_bad_entrypoint_format.ligo\", line 8, characters 52-58"} ligo: in file "self_bad_entrypoint_format.ligo", line 8, characters 52-58. bad entrypoint format: entrypoint "Toto" is badly formatted. We expect "%bar" for entrypoint Bar and "%default" when no entrypoint used {"location":"in file \"self_bad_entrypoint_format.ligo\", line 8, characters 52-58"}
If you're not sure how to fix this error, you can
do one of the following:
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}];
run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_1.religo"; "main"];
[%expect {|
ligo: It looks like you have nested a big map inside another big map. This is not supported. : {}
If you're not sure how to fix this error, you can
do one of the following:
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}];
run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_2.religo"; "main"];
[%expect {|
ligo: It looks like you have nested a big map inside another big map. This is not supported. : {}
If you're not sure how to fix this error, you can
do one of the following:
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}];
run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_3.religo"; "main"];
[%expect {|
ligo: It looks like you have nested a big map inside another big map. This is not supported. : {}
If you're not sure how to fix this error, you can
do one of the following:
* Visit our documentation: https://ligolang.org/docs/intro/what-and-why/
* Ask a question on our Discord: https://discord.gg/9rhYaEt
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
* Check the changelog by running 'ligo changelog' |}];
run_ligo_bad ["compile-contract"; bad_contract "nested_bigmap_4.religo"; "main"];
[%expect {|
ligo: It looks like you have nested a big map inside another big map. This is not supported. : {}
If you're not sure how to fix this error, you can If you're not sure how to fix this error, you can
do one of the following: do one of the following:

View File

@ -6,7 +6,7 @@ let all_expression_mapper = [
Literals.peephole_expression ; Literals.peephole_expression ;
] ]
let all_type_expression_mapper = [ let all_type_expression_mapper = [
Entrypoints_lenght_limit.peephole_type_expression ; Entrypoints_length_limit.peephole_type_expression ;
] ]
let all_exp = List.map (fun el -> Helpers.Expression el) all_expression_mapper let all_exp = List.map (fun el -> Helpers.Expression el) all_expression_mapper

View File

@ -0,0 +1,56 @@
open Ast_typed
open Trace
type contract_pass_data = Contract_passes.contract_pass_data
module Errors = struct
let no_nested_bigmap () =
let title = (thunk ("It looks like you have nested a big map inside another big map. This is not supported. ")) in
let message () = "" in
let data = [
(* ("location" , fun () -> Format.asprintf "%a" Location.pp loc) TODO once types have an actual location *)
] in
error ~data title message ()
end
let rec check_no_nested_bigmap is_in_bigmap e =
match e.type_content with
| T_operator (TC_big_map (_, _)) when is_in_bigmap ->
fail @@ Errors.no_nested_bigmap
| T_operator (TC_big_map (key, value)) ->
let%bind _ = check_no_nested_bigmap false key in
let%bind _ = check_no_nested_bigmap true value in
ok ()
| T_operator (TC_contract t)
| T_operator (TC_option t)
| T_operator (TC_list t)
| T_operator (TC_set t) ->
let%bind _ = check_no_nested_bigmap is_in_bigmap t in
ok ()
| T_operator (TC_map (a, b)) ->
let%bind _ = check_no_nested_bigmap is_in_bigmap a in
let%bind _ = check_no_nested_bigmap is_in_bigmap b in
ok ()
| T_operator (TC_arrow (a, b)) ->
let%bind _ = check_no_nested_bigmap false a in
let%bind _ = check_no_nested_bigmap false b in
ok ()
| T_sum s ->
let es = CMap.to_list s in
let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in
ok ()
| T_record elm ->
let es = LMap.to_list elm in
let%bind _ = bind_map_list (fun l -> check_no_nested_bigmap is_in_bigmap l) es in
ok ()
| T_arrow { type1; type2 } ->
let%bind _ = check_no_nested_bigmap false type1 in
let%bind _ = check_no_nested_bigmap false type2 in
ok ()
| T_variable _
| T_constant _ ->
ok ()
let self_typing : contract_pass_data -> expression -> (bool * contract_pass_data * expression) result = fun dat el ->
let%bind _ = check_no_nested_bigmap false el.type_expression in
ok (true, dat, el)

View File

@ -6,6 +6,7 @@ let all_passes = [
let contract_passes = [ let contract_passes = [
Contract_passes.self_typing ; Contract_passes.self_typing ;
No_nested_big_map.self_typing ;
] ]
let all_program = let all_program =

View File

@ -0,0 +1,10 @@
type bar = big_map (nat, int);
/* this should result in an error as nested big_maps are not supported: */
type storage = big_map (int, bar);
type return = (list (operation), storage);
let main = ((ignore, store): (unit, storage)): return => {
([]: list(operation), store)
};

View File

@ -0,0 +1,9 @@
/* this should result in an error as nested big_maps are not supported: */
type storage = big_map (nat, big_map (int, string));
type return = (list (operation), storage);
let main = ((ignore, store): (unit, storage)): return => {
([]: list(operation), store)
};

View File

@ -0,0 +1,15 @@
type bar = big_map (nat, int);
type foo = {
a: int,
b: bar
};
/* this should result in an error as nested big_maps are not supported: */
type storage = big_map(nat, foo);
type return = (list (operation), storage);
let main = ((ignore, store): (unit, storage)): return => {
([]: list(operation), store)
};

View File

@ -0,0 +1,9 @@
/* this should result in an error as nested big_maps are not supported: */
type storage = map (int, big_map (nat, big_map (int, string)));
type return = (list (operation), storage);
let main = ((ignore, store): (unit, storage)): return => {
([]: list(operation), store)
};