Michelson: adds LOOP_LEFT, ITER, MAP body
This commit is contained in:
parent
c387ed823a
commit
6c992b58df
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
module Ed25519 = Environment.Ed25519
|
module Ed25519 = Environment.Ed25519
|
||||||
open Client_proto_args
|
open Client_proto_args
|
||||||
|
|
||||||
open Michelson_v1_printer
|
open Michelson_v1_printer
|
||||||
|
|
||||||
module Program = Client_aliases.Alias (struct
|
module Program = Client_aliases.Alias (struct
|
||||||
|
@ -94,6 +94,8 @@ type prim =
|
|||||||
| I_UNIT
|
| I_UNIT
|
||||||
| I_UPDATE
|
| I_UPDATE
|
||||||
| I_XOR
|
| I_XOR
|
||||||
|
| I_ITER
|
||||||
|
| I_LOOP_LEFT
|
||||||
| T_bool
|
| T_bool
|
||||||
| T_contract
|
| T_contract
|
||||||
| T_int
|
| T_int
|
||||||
@ -214,6 +216,8 @@ let string_of_prim = function
|
|||||||
| I_UNIT -> "UNIT"
|
| I_UNIT -> "UNIT"
|
||||||
| I_UPDATE -> "UPDATE"
|
| I_UPDATE -> "UPDATE"
|
||||||
| I_XOR -> "XOR"
|
| I_XOR -> "XOR"
|
||||||
|
| I_ITER -> "ITER"
|
||||||
|
| I_LOOP_LEFT -> "LOOP_LEFT"
|
||||||
| T_bool -> "bool"
|
| T_bool -> "bool"
|
||||||
| T_contract -> "contract"
|
| T_contract -> "contract"
|
||||||
| T_int -> "int"
|
| T_int -> "int"
|
||||||
@ -315,6 +319,8 @@ let prim_of_string = function
|
|||||||
| "UNIT" -> ok I_UNIT
|
| "UNIT" -> ok I_UNIT
|
||||||
| "UPDATE" -> ok I_UPDATE
|
| "UPDATE" -> ok I_UPDATE
|
||||||
| "XOR" -> ok I_XOR
|
| "XOR" -> ok I_XOR
|
||||||
|
| "ITER" -> ok I_ITER
|
||||||
|
| "LOOP_LEFT" -> ok I_LOOP_LEFT
|
||||||
| "bool" -> ok T_bool
|
| "bool" -> ok T_bool
|
||||||
| "contract" -> ok T_contract
|
| "contract" -> ok T_contract
|
||||||
| "int" -> ok T_int
|
| "int" -> ok T_int
|
||||||
@ -457,24 +463,26 @@ let prim_encoding =
|
|||||||
| I_UNIT -> 78
|
| I_UNIT -> 78
|
||||||
| I_UPDATE -> 79
|
| I_UPDATE -> 79
|
||||||
| I_XOR -> 80
|
| I_XOR -> 80
|
||||||
| T_bool -> 81
|
| I_ITER -> 81
|
||||||
| T_contract -> 82
|
| I_LOOP_LEFT -> 82
|
||||||
| T_int -> 83
|
| T_bool -> 83
|
||||||
| T_key -> 84
|
| T_contract -> 84
|
||||||
| T_key_hash -> 85
|
| T_int -> 85
|
||||||
| T_lambda -> 86
|
| T_key -> 86
|
||||||
| T_list -> 87
|
| T_key_hash -> 87
|
||||||
| T_map -> 88
|
| T_lambda -> 88
|
||||||
| T_nat -> 89
|
| T_list -> 89
|
||||||
| T_option -> 90
|
| T_map -> 90
|
||||||
| T_or -> 91
|
| T_nat -> 91
|
||||||
| T_pair -> 92
|
| T_option -> 92
|
||||||
| T_set -> 93
|
| T_or -> 93
|
||||||
| T_signature -> 94
|
| T_pair -> 94
|
||||||
| T_string -> 95
|
| T_set -> 95
|
||||||
| T_tez -> 96
|
| T_signature -> 96
|
||||||
| T_timestamp -> 97
|
| T_string -> 97
|
||||||
| T_unit -> 99 in
|
| T_tez -> 98
|
||||||
|
| T_timestamp -> 99
|
||||||
|
| T_unit -> 100 in
|
||||||
let of_int_map = [|
|
let of_int_map = [|
|
||||||
K_parameter ;
|
K_parameter ;
|
||||||
K_return ;
|
K_return ;
|
||||||
@ -557,6 +565,8 @@ let prim_encoding =
|
|||||||
I_UNIT ;
|
I_UNIT ;
|
||||||
I_UPDATE ;
|
I_UPDATE ;
|
||||||
I_XOR ;
|
I_XOR ;
|
||||||
|
I_ITER ;
|
||||||
|
I_LOOP_LEFT ;
|
||||||
T_bool ;
|
T_bool ;
|
||||||
T_contract ;
|
T_contract ;
|
||||||
T_int ;
|
T_int ;
|
||||||
@ -576,7 +586,7 @@ let prim_encoding =
|
|||||||
T_timestamp ;
|
T_timestamp ;
|
||||||
T_unit |] in
|
T_unit |] in
|
||||||
let of_int i =
|
let of_int i =
|
||||||
if Compare.Int.(i >= 0 || i <= 99) then
|
if Compare.Int.(i >= 0 || i <= 100) then
|
||||||
of_int_map.(i)
|
of_int_map.(i)
|
||||||
else
|
else
|
||||||
raise Data_encoding.No_case_matched in
|
raise Data_encoding.No_case_matched in
|
||||||
|
@ -92,6 +92,8 @@ type prim =
|
|||||||
| I_UNIT
|
| I_UNIT
|
||||||
| I_UPDATE
|
| I_UPDATE
|
||||||
| I_XOR
|
| I_XOR
|
||||||
|
| I_ITER
|
||||||
|
| I_LOOP_LEFT
|
||||||
| T_bool
|
| T_bool
|
||||||
| T_contract
|
| T_contract
|
||||||
| T_int
|
| T_int
|
||||||
|
@ -142,6 +142,17 @@ let rec interp
|
|||||||
return (ret :: tail, qta, ctxt, origination))
|
return (ret :: tail, qta, ctxt, origination))
|
||||||
l ([], qta, ctxt, origination) >>=? fun (res, qta, ctxt, origination) ->
|
l ([], qta, ctxt, origination) >>=? fun (res, qta, ctxt, origination) ->
|
||||||
logged_return ~origination (Item (res, rest), qta, ctxt)
|
logged_return ~origination (Item (res, rest), qta, ctxt)
|
||||||
|
| List_map_body body, Item (l, rest) ->
|
||||||
|
let rec help rest qta = function
|
||||||
|
| [] -> logged_return ~origination (Item ([], rest), qta, ctxt)
|
||||||
|
| hd :: tl ->
|
||||||
|
step origination qta ctxt body (Item (hd, rest))
|
||||||
|
>>=? fun (Item (hd, rest), qta, _, _) ->
|
||||||
|
help rest qta tl
|
||||||
|
>>=? fun (Item (tl, rest), qta, ctxt, origination) ->
|
||||||
|
logged_return ~origination (Item (hd :: tl, rest), qta, ctxt)
|
||||||
|
in help rest qta l >>=? fun (res, qta, ctxt, origination) ->
|
||||||
|
logged_return ~origination (res, qta - 1, ctxt)
|
||||||
| List_reduce, Item (lam, Item (l, Item (init, rest))) ->
|
| List_reduce, Item (lam, Item (l, Item (init, rest))) ->
|
||||||
fold_left_s
|
fold_left_s
|
||||||
(fun (partial, qta, ctxt, origination) arg ->
|
(fun (partial, qta, ctxt, origination) arg ->
|
||||||
@ -154,6 +165,14 @@ let rec interp
|
|||||||
let len = List.length list in
|
let len = List.length list in
|
||||||
let len = Script_int.(abs (of_int len)) in
|
let len = Script_int.(abs (of_int len)) in
|
||||||
logged_return (Item (len, rest), qta - 1, ctxt)
|
logged_return (Item (len, rest), qta - 1, ctxt)
|
||||||
|
| List_iter body, Item (l, init_stack) ->
|
||||||
|
fold_left_s
|
||||||
|
(fun (stack, qta, ctxt, origination) arg ->
|
||||||
|
step origination qta ctxt body (Item (arg, stack))
|
||||||
|
>>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
return (stack, qta, ctxt, origination))
|
||||||
|
(init_stack, qta, ctxt, origination) l >>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
logged_return ~origination (stack, qta, ctxt)
|
||||||
(* sets *)
|
(* sets *)
|
||||||
| Empty_set t, rest ->
|
| Empty_set t, rest ->
|
||||||
logged_return (Item (empty_set t, rest), qta - 1, ctxt)
|
logged_return (Item (empty_set t, rest), qta - 1, ctxt)
|
||||||
@ -177,6 +196,15 @@ let rec interp
|
|||||||
return (partial, qta, ctxt, origination))
|
return (partial, qta, ctxt, origination))
|
||||||
(init, qta, ctxt, origination) items >>=? fun (res, qta, ctxt, origination) ->
|
(init, qta, ctxt, origination) items >>=? fun (res, qta, ctxt, origination) ->
|
||||||
logged_return ~origination (Item (res, rest), qta, ctxt)
|
logged_return ~origination (Item (res, rest), qta, ctxt)
|
||||||
|
| Set_iter body, Item (set, init_stack) ->
|
||||||
|
fold_left_s
|
||||||
|
(fun (stack, qta, ctxt, origination) arg ->
|
||||||
|
step origination qta ctxt body (Item (arg, stack))
|
||||||
|
>>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
return (stack, qta, ctxt, origination))
|
||||||
|
(init_stack, qta, ctxt, origination)
|
||||||
|
(set_fold (fun e acc -> e :: acc) set []) >>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
logged_return ~origination (stack, qta, ctxt)
|
||||||
| Set_mem, Item (v, Item (set, rest)) ->
|
| Set_mem, Item (v, Item (set, rest)) ->
|
||||||
logged_return (Item (set_mem v set, rest), qta - 1, ctxt)
|
logged_return (Item (set_mem v set, rest), qta - 1, ctxt)
|
||||||
| Set_update, Item (v, Item (presence, Item (set, rest))) ->
|
| Set_update, Item (v, Item (presence, Item (set, rest))) ->
|
||||||
@ -206,6 +234,16 @@ let rec interp
|
|||||||
return (partial, qta, ctxt, origination))
|
return (partial, qta, ctxt, origination))
|
||||||
(init, qta, ctxt, origination) items >>=? fun (res, qta, ctxt, origination) ->
|
(init, qta, ctxt, origination) items >>=? fun (res, qta, ctxt, origination) ->
|
||||||
logged_return ~origination (Item (res, rest), qta, ctxt)
|
logged_return ~origination (Item (res, rest), qta, ctxt)
|
||||||
|
| Map_iter body, Item (map, init_stack) ->
|
||||||
|
let items =
|
||||||
|
List.rev (map_fold (fun k v acc -> (k, v) :: acc) map []) in
|
||||||
|
fold_left_s
|
||||||
|
(fun (stack, qta, ctxt, origination) arg ->
|
||||||
|
step origination qta ctxt body (Item (arg, stack))
|
||||||
|
>>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
return (stack, qta, ctxt, origination))
|
||||||
|
(init_stack, qta, ctxt, origination) items >>=? fun (stack, qta, ctxt, origination) ->
|
||||||
|
logged_return ~origination (stack, qta, ctxt)
|
||||||
| Map_mem, Item (v, Item (map, rest)) ->
|
| Map_mem, Item (v, Item (map, rest)) ->
|
||||||
logged_return (Item (map_mem v map, rest), qta - 1, ctxt)
|
logged_return (Item (map_mem v map, rest), qta - 1, ctxt)
|
||||||
| Map_get, Item (v, Item (map, rest)) ->
|
| Map_get, Item (v, Item (map, rest)) ->
|
||||||
@ -364,6 +402,11 @@ let rec interp
|
|||||||
step origination (qta - 1) ctxt descr trans
|
step origination (qta - 1) ctxt descr trans
|
||||||
| Loop _, Item (false, rest) ->
|
| Loop _, Item (false, rest) ->
|
||||||
logged_return (rest, qta, ctxt)
|
logged_return (rest, qta, ctxt)
|
||||||
|
| Loop_left body, Item (L v, rest) ->
|
||||||
|
step origination qta ctxt body (Item (v, rest)) >>=? fun (trans, qta, ctxt, origination) ->
|
||||||
|
step origination (qta - 1) ctxt descr trans
|
||||||
|
| Loop_left _, Item (R v, rest) ->
|
||||||
|
logged_return (Item (v, rest), qta, ctxt)
|
||||||
| Dip b, Item (ign, rest) ->
|
| Dip b, Item (ign, rest) ->
|
||||||
step origination qta ctxt b rest >>=? fun (res, qta, ctxt, origination) ->
|
step origination qta ctxt b rest >>=? fun (res, qta, ctxt, origination) ->
|
||||||
logged_return ~origination (Item (ign, res), qta, ctxt)
|
logged_return ~origination (Item (ign, res), qta, ctxt)
|
||||||
|
@ -41,6 +41,9 @@ type error += Bad_stack_item of int
|
|||||||
type error += Inconsistent_annotations of string * string
|
type error += Inconsistent_annotations of string * string
|
||||||
type error += Inconsistent_type_annotations : Script.location * _ ty * _ ty -> error
|
type error += Inconsistent_type_annotations : Script.location * _ ty * _ ty -> error
|
||||||
type error += Unexpected_annotation of Script.location
|
type error += Unexpected_annotation of Script.location
|
||||||
|
type error += Invalid_map_body : Script.location * _ stack_ty -> error
|
||||||
|
type error += Invalid_map_block_fail of Script.location
|
||||||
|
type error += Invalid_iter_body : Script.location * _ stack_ty * _ stack_ty -> error
|
||||||
|
|
||||||
(* Value typing errors *)
|
(* Value typing errors *)
|
||||||
type error += Invalid_constant : Script.location * Script.expr * _ ty -> error
|
type error += Invalid_constant : Script.location * Script.expr * _ ty -> error
|
||||||
@ -173,7 +176,9 @@ let namespace = function
|
|||||||
| I_TRANSFER_TOKENS
|
| I_TRANSFER_TOKENS
|
||||||
| I_UNIT
|
| I_UNIT
|
||||||
| I_UPDATE
|
| I_UPDATE
|
||||||
| I_XOR -> Instr_namespace
|
| I_XOR
|
||||||
|
| I_ITER
|
||||||
|
| I_LOOP_LEFT -> Instr_namespace
|
||||||
| T_bool
|
| T_bool
|
||||||
| T_contract
|
| T_contract
|
||||||
| T_int
|
| T_int
|
||||||
@ -1134,10 +1139,25 @@ and parse_instr
|
|||||||
let branch ibt ibf =
|
let branch ibt ibf =
|
||||||
{ loc ; instr = If_cons (ibt, ibf) ; bef ; aft = ibt.aft } in
|
{ loc ; instr = If_cons (ibt, ibf) ; bef ; aft = ibt.aft } in
|
||||||
merge_branches loc btr bfr { branch }
|
merge_branches loc btr bfr { branch }
|
||||||
|
| Prim (loc, I_SIZE, [], instr_annot),
|
||||||
|
Item_t (List_t _, rest, _) ->
|
||||||
|
return (typed loc (List_size, Item_t (Nat_t, rest, instr_annot)))
|
||||||
| Prim (loc, I_MAP, [], instr_annot),
|
| Prim (loc, I_MAP, [], instr_annot),
|
||||||
Item_t (Lambda_t (param, ret), Item_t (List_t elt, rest, _), _) ->
|
Item_t (Lambda_t (param, ret), Item_t (List_t elt, rest, _), _) ->
|
||||||
check_item_ty elt param loc I_MAP 2 2 >>=? fun (Eq _) ->
|
check_item_ty elt param loc I_MAP 2 2 >>=? fun (Eq _) ->
|
||||||
return (typed loc (List_map, Item_t (List_t ret, rest, instr_annot)))
|
return (typed loc (List_map, Item_t (List_t ret, rest, instr_annot)))
|
||||||
|
| Prim (loc, I_MAP, [ body ], instr_annot),
|
||||||
|
(Item_t (List_t elt, starting_rest, _)) ->
|
||||||
|
check_kind [ Seq_kind ] body >>=? fun () ->
|
||||||
|
parse_instr ?type_logger tc_context ctxt body (Item_t (elt, starting_rest, None)) >>=? begin function
|
||||||
|
| Typed ({ aft = Item_t (ret, rest, _) } as ibody) ->
|
||||||
|
trace
|
||||||
|
(Invalid_map_body (loc, ibody.aft))
|
||||||
|
(Lwt.return (stack_ty_eq 1 rest starting_rest)) >>=? fun (Eq _) ->
|
||||||
|
return (typed loc (List_map_body ibody, Item_t (List_t ret, rest, instr_annot)))
|
||||||
|
| Typed { aft } -> fail (Invalid_map_body (loc, aft))
|
||||||
|
| Failed _ -> fail (Invalid_map_block_fail loc)
|
||||||
|
end
|
||||||
| Prim (loc, I_REDUCE, [], instr_annot),
|
| Prim (loc, I_REDUCE, [], instr_annot),
|
||||||
Item_t (Lambda_t (Pair_t ((pelt, _), (pr, _)), r),
|
Item_t (Lambda_t (Pair_t ((pelt, _), (pr, _)), r),
|
||||||
Item_t (List_t elt, Item_t (init, rest, _), _), _) ->
|
Item_t (List_t elt, Item_t (init, rest, _), _), _) ->
|
||||||
@ -1148,6 +1168,20 @@ and parse_instr
|
|||||||
| Prim (loc, I_SIZE, [], instr_annot),
|
| Prim (loc, I_SIZE, [], instr_annot),
|
||||||
Item_t (List_t _, rest, _) ->
|
Item_t (List_t _, rest, _) ->
|
||||||
return (typed loc (List_size, Item_t (Nat_t, rest, instr_annot)))
|
return (typed loc (List_size, Item_t (Nat_t, rest, instr_annot)))
|
||||||
|
| Prim (loc, I_ITER, [ body ], instr_annot),
|
||||||
|
Item_t (List_t elt, rest, _) ->
|
||||||
|
check_kind [ Seq_kind ] body >>=? fun () ->
|
||||||
|
fail_unexpected_annot loc instr_annot >>=? fun () ->
|
||||||
|
parse_instr ?type_logger tc_context ctxt body (Item_t (elt, rest, None)) >>=? begin function
|
||||||
|
| Typed ({ aft } as ibody) ->
|
||||||
|
trace
|
||||||
|
(Invalid_iter_body (loc, rest, ibody.aft))
|
||||||
|
(Lwt.return (stack_ty_eq 1 aft rest)) >>=? fun (Eq _) ->
|
||||||
|
return (typed loc (List_iter ibody, rest))
|
||||||
|
| Failed { descr } ->
|
||||||
|
let ibody = descr rest in
|
||||||
|
return (typed loc (List_iter ibody, rest))
|
||||||
|
end
|
||||||
(* sets *)
|
(* sets *)
|
||||||
| Prim (loc, I_EMPTY_SET, [ t ], instr_annot),
|
| Prim (loc, I_EMPTY_SET, [ t ], instr_annot),
|
||||||
rest ->
|
rest ->
|
||||||
@ -1167,6 +1201,21 @@ and parse_instr
|
|||||||
check_item_ty elt pelt loc I_REDUCE 2 3 >>=? fun (Eq _) ->
|
check_item_ty elt pelt loc I_REDUCE 2 3 >>=? fun (Eq _) ->
|
||||||
check_item_ty init r loc I_REDUCE 3 3 >>=? fun (Eq _) ->
|
check_item_ty init r loc I_REDUCE 3 3 >>=? fun (Eq _) ->
|
||||||
return (typed loc (Set_reduce, Item_t (r, rest, instr_annot)))
|
return (typed loc (Set_reduce, Item_t (r, rest, instr_annot)))
|
||||||
|
| Prim (loc, I_ITER, [ body ], annot),
|
||||||
|
Item_t (Set_t comp_elt, rest, _) ->
|
||||||
|
check_kind [ Seq_kind ] body >>=? fun () ->
|
||||||
|
fail_unexpected_annot loc annot >>=? fun () ->
|
||||||
|
let elt = ty_of_comparable_ty comp_elt in
|
||||||
|
parse_instr ?type_logger tc_context ctxt body (Item_t (elt, rest, None)) >>=? begin function
|
||||||
|
| Typed ({ aft } as ibody) ->
|
||||||
|
trace
|
||||||
|
(Invalid_iter_body (loc, rest, ibody.aft))
|
||||||
|
(Lwt.return (stack_ty_eq 1 aft rest)) >>=? fun (Eq _) ->
|
||||||
|
return (typed loc (Set_iter ibody, rest))
|
||||||
|
| Failed { descr } ->
|
||||||
|
let ibody = descr rest in
|
||||||
|
return (typed loc (Set_iter ibody, rest))
|
||||||
|
end
|
||||||
| Prim (loc, I_MEM, [], instr_annot),
|
| Prim (loc, I_MEM, [], instr_annot),
|
||||||
Item_t (v, Item_t (Set_t elt, rest, _), _) ->
|
Item_t (v, Item_t (Set_t elt, rest, _), _) ->
|
||||||
let elt = ty_of_comparable_ty elt in
|
let elt = ty_of_comparable_ty elt in
|
||||||
@ -1203,6 +1252,23 @@ and parse_instr
|
|||||||
check_item_ty r pr loc I_REDUCE 1 3 >>=? fun (Eq _) ->
|
check_item_ty r pr loc I_REDUCE 1 3 >>=? fun (Eq _) ->
|
||||||
check_item_ty init r loc I_REDUCE 3 3 >>=? fun (Eq _) ->
|
check_item_ty init r loc I_REDUCE 3 3 >>=? fun (Eq _) ->
|
||||||
return (typed loc (Map_reduce, Item_t (r, rest, instr_annot)))
|
return (typed loc (Map_reduce, Item_t (r, rest, instr_annot)))
|
||||||
|
| Prim (loc, I_ITER, [ body ], instr_annot),
|
||||||
|
Item_t (Map_t (comp_elt, element_ty), rest, _) ->
|
||||||
|
check_kind [ Seq_kind ] body >>=? fun () ->
|
||||||
|
fail_unexpected_annot loc instr_annot >>=? fun () ->
|
||||||
|
let key = ty_of_comparable_ty comp_elt in
|
||||||
|
parse_instr ?type_logger tc_context ctxt body
|
||||||
|
(Item_t (Pair_t ((key, None), (element_ty, None)), rest, None))
|
||||||
|
>>=? begin function
|
||||||
|
| Typed ({ aft } as ibody) ->
|
||||||
|
trace
|
||||||
|
(Invalid_iter_body (loc, rest, ibody.aft))
|
||||||
|
(Lwt.return (stack_ty_eq 1 aft rest)) >>=? fun (Eq _) ->
|
||||||
|
return (typed loc (Map_iter ibody, rest))
|
||||||
|
| Failed { descr } ->
|
||||||
|
let ibody = descr rest in
|
||||||
|
return (typed loc (Map_iter ibody, rest))
|
||||||
|
end
|
||||||
| Prim (loc, I_MEM, [], instr_annot),
|
| Prim (loc, I_MEM, [], instr_annot),
|
||||||
Item_t (vk, Item_t (Map_t (ck, _), rest, _), _) ->
|
Item_t (vk, Item_t (Map_t (ck, _), rest, _), _) ->
|
||||||
let k = ty_of_comparable_ty ck in
|
let k = ty_of_comparable_ty ck in
|
||||||
@ -1279,6 +1345,20 @@ and parse_instr
|
|||||||
let ibody = descr (Item_t (Bool_t, rest, stack_annot)) in
|
let ibody = descr (Item_t (Bool_t, rest, stack_annot)) in
|
||||||
return (typed loc (Loop ibody, rest))
|
return (typed loc (Loop ibody, rest))
|
||||||
end
|
end
|
||||||
|
| Prim (loc, I_LOOP_LEFT, [ body ], instr_annot),
|
||||||
|
(Item_t (Union_t ((tl, tl_annot), (tr, tr_annot)), rest, _) as stack) ->
|
||||||
|
check_kind [ Seq_kind ] body >>=? fun () ->
|
||||||
|
fail_unexpected_annot loc instr_annot >>=? fun () ->
|
||||||
|
parse_instr ?type_logger tc_context ctxt body (Item_t (tl, rest, tl_annot)) >>=? begin function
|
||||||
|
| Typed ibody ->
|
||||||
|
trace
|
||||||
|
(Unmatched_branches (loc, ibody.aft, stack))
|
||||||
|
(Lwt.return (stack_ty_eq 1 ibody.aft stack)) >>=? fun (Eq _) ->
|
||||||
|
return (typed loc (Loop_left ibody, (Item_t (tr, rest, tr_annot))))
|
||||||
|
| Failed { descr } ->
|
||||||
|
let ibody = descr (Item_t (Union_t ((tl, tl_annot), (tr, tr_annot)), rest, None)) in
|
||||||
|
return (typed loc (Loop_left ibody, Item_t (tr, rest, tr_annot)))
|
||||||
|
end
|
||||||
| Prim (loc, I_LAMBDA, [ arg ; ret ; code ], instr_annot),
|
| Prim (loc, I_LAMBDA, [ arg ; ret ; code ], instr_annot),
|
||||||
stack ->
|
stack ->
|
||||||
(Lwt.return (parse_ty arg)) >>=? fun (Ex_ty arg, arg_annot) ->
|
(Lwt.return (parse_ty arg)) >>=? fun (Ex_ty arg, arg_annot) ->
|
||||||
@ -1584,8 +1664,8 @@ and parse_instr
|
|||||||
| I_H | I_STEPS_TO_QUOTA
|
| I_H | I_STEPS_TO_QUOTA
|
||||||
as name), (_ :: _ as l), _), _ ->
|
as name), (_ :: _ as l), _), _ ->
|
||||||
fail (Invalid_arity (loc, name, 0, List.length l))
|
fail (Invalid_arity (loc, name, 0, List.length l))
|
||||||
| Prim (loc, (I_NONE | I_LEFT | I_RIGHT | I_NIL
|
| Prim (loc, (I_NONE | I_LEFT | I_RIGHT | I_NIL | I_MAP | I_ITER
|
||||||
| I_EMPTY_SET | I_DIP | I_LOOP
|
| I_EMPTY_SET | I_DIP | I_LOOP | I_LOOP_LEFT
|
||||||
as name), ([]
|
as name), ([]
|
||||||
| _ :: _ :: _ as l), _), _ ->
|
| _ :: _ :: _ as l), _), _ ->
|
||||||
fail (Invalid_arity (loc, name, 1, List.length l))
|
fail (Invalid_arity (loc, name, 1, List.length l))
|
||||||
@ -1628,7 +1708,7 @@ and parse_instr
|
|||||||
stack ->
|
stack ->
|
||||||
fail (Bad_stack (loc, name, 1, stack))
|
fail (Bad_stack (loc, name, 1, stack))
|
||||||
| Prim (loc, (I_SWAP | I_PAIR | I_CONS
|
| Prim (loc, (I_SWAP | I_PAIR | I_CONS
|
||||||
| I_MAP | I_GET | I_MEM | I_EXEC
|
| I_GET | I_MEM | I_EXEC
|
||||||
| I_CHECK_SIGNATURE | I_ADD | I_SUB | I_MUL
|
| I_CHECK_SIGNATURE | I_ADD | I_SUB | I_MUL
|
||||||
| I_EDIV | I_AND | I_OR | I_XOR
|
| I_EDIV | I_AND | I_OR | I_XOR
|
||||||
| I_LSL | I_LSR | I_CONCAT as name), _, _),
|
| I_LSL | I_LSR | I_CONCAT as name), _, _),
|
||||||
@ -1639,7 +1719,7 @@ and parse_instr
|
|||||||
fail @@ unexpected expr [ Seq_kind ] Instr_namespace
|
fail @@ unexpected expr [ Seq_kind ] Instr_namespace
|
||||||
[ I_DROP ; I_DUP ; I_SWAP ; I_SOME ; I_UNIT ;
|
[ I_DROP ; I_DUP ; I_SWAP ; I_SOME ; I_UNIT ;
|
||||||
I_PAIR ; I_CAR ; I_CDR ; I_CONS ;
|
I_PAIR ; I_CAR ; I_CDR ; I_CONS ;
|
||||||
I_MEM ; I_UPDATE ; I_MAP ; I_REDUCE ;
|
I_MEM ; I_UPDATE ; I_MAP ; I_REDUCE ; I_ITER ;
|
||||||
I_GET ; I_EXEC ; I_FAIL ; I_SIZE ;
|
I_GET ; I_EXEC ; I_FAIL ; I_SIZE ;
|
||||||
I_CONCAT ; I_ADD ; I_SUB ;
|
I_CONCAT ; I_ADD ; I_SUB ;
|
||||||
I_MUL ; I_EDIV ; I_OR ; I_AND ; I_XOR ;
|
I_MUL ; I_EDIV ; I_OR ; I_AND ; I_XOR ;
|
||||||
@ -2203,7 +2283,47 @@ let () =
|
|||||||
| _ -> None)
|
| _ -> None)
|
||||||
(fun (Ex_ty tya, Ex_ty tyb) ->
|
(fun (Ex_ty tya, Ex_ty tyb) ->
|
||||||
Inconsistent_types (tya, tyb)) ;
|
Inconsistent_types (tya, tyb)) ;
|
||||||
|
register_error_kind
|
||||||
|
`Permanent
|
||||||
|
~id:"invalidMapBody"
|
||||||
|
~title: "Invalid map body"
|
||||||
|
~description:
|
||||||
|
"The body of a map block did not match the expected type"
|
||||||
|
(obj2
|
||||||
|
(req "loc" Script.location_encoding)
|
||||||
|
(req "bodyType" ex_stack_ty_enc))
|
||||||
|
(function
|
||||||
|
| Invalid_map_body (loc, stack) ->
|
||||||
|
Some (loc, Ex_stack_ty stack)
|
||||||
|
| _ -> None)
|
||||||
|
(fun (loc, Ex_stack_ty stack) ->
|
||||||
|
Invalid_map_body (loc, stack)) ;
|
||||||
|
register_error_kind
|
||||||
|
`Permanent
|
||||||
|
~id:"invalidMapBlockFail"
|
||||||
|
~title:"FAIL instruction occurred as body of map block"
|
||||||
|
~description:"FAIL cannot be the only instruction in the body.\
|
||||||
|
The propper type of the return list cannot be inferred."
|
||||||
|
(obj1 (req "loc" Script.location_encoding))
|
||||||
|
(function
|
||||||
|
| Invalid_map_block_fail loc -> Some loc
|
||||||
|
| _ -> None)
|
||||||
|
(fun loc -> Invalid_map_block_fail loc) ;
|
||||||
|
register_error_kind
|
||||||
|
`Permanent
|
||||||
|
~id:"invalidIterBody"
|
||||||
|
~title:"ITER body returned wrong stack type"
|
||||||
|
~description:"The body of an ITER instruction\
|
||||||
|
must result in the same stack type as before\
|
||||||
|
the ITER."
|
||||||
|
(obj3
|
||||||
|
(req "loc" Script.location_encoding)
|
||||||
|
(req "befStack" ex_stack_ty_enc)
|
||||||
|
(req "aftStack" ex_stack_ty_enc))
|
||||||
|
(function
|
||||||
|
| Invalid_iter_body (loc, bef, aft) -> Some (loc, Ex_stack_ty bef, Ex_stack_ty aft)
|
||||||
|
| _ -> None)
|
||||||
|
(fun (loc, Ex_stack_ty bef, Ex_stack_ty aft) -> Invalid_iter_body (loc, bef, aft)) ;
|
||||||
(* Toplevel errors *)
|
(* Toplevel errors *)
|
||||||
register_error_kind
|
register_error_kind
|
||||||
`Permanent
|
`Permanent
|
||||||
|
@ -47,6 +47,9 @@ type error += Transfer_in_lambda of Script.location
|
|||||||
type error += Transfer_in_dip of Script.location
|
type error += Transfer_in_dip of Script.location
|
||||||
type error += Bad_stack_length
|
type error += Bad_stack_length
|
||||||
type error += Bad_stack_item of int
|
type error += Bad_stack_item of int
|
||||||
|
type error += Invalid_map_body : Script.location * _ Script_typed_ir.stack_ty -> error
|
||||||
|
type error += Invalid_map_block_fail of Script.location
|
||||||
|
type error += Invalid_iter_body : Script.location * _ Script_typed_ir.stack_ty * _ Script_typed_ir.stack_ty -> error
|
||||||
|
|
||||||
(* Value typing errors *)
|
(* Value typing errors *)
|
||||||
type error += Invalid_constant : Script.location * Script.expr * _ Script_typed_ir.ty -> error
|
type error += Invalid_constant : Script.location * Script.expr * _ Script_typed_ir.ty -> error
|
||||||
|
@ -134,10 +134,15 @@ and ('bef, 'aft) instr =
|
|||||||
('a list * 'bef, 'aft) instr
|
('a list * 'bef, 'aft) instr
|
||||||
| List_map :
|
| List_map :
|
||||||
(('param, 'ret) lambda * ('param list * 'rest), 'ret list * 'rest) instr
|
(('param, 'ret) lambda * ('param list * 'rest), 'ret list * 'rest) instr
|
||||||
|
| List_map_body : ('a * 'rest, 'b * 'rest) descr ->
|
||||||
|
('a list * 'rest, 'b list * 'rest) instr
|
||||||
| List_reduce :
|
| List_reduce :
|
||||||
(('param * 'res, 'res) lambda *
|
(('param * 'res, 'res) lambda *
|
||||||
('param list * ('res * 'rest)), 'res * 'rest) instr
|
('param list * ('res * 'rest)), 'res * 'rest) instr
|
||||||
| List_size : ('a list * 'rest, n num * 'rest) instr
|
| List_size : ('a list * 'rest, n num * 'rest) instr
|
||||||
|
| List_iter :
|
||||||
|
('a * 'rest, 'rest) descr ->
|
||||||
|
('a list * 'rest, 'rest) instr
|
||||||
(* sets *)
|
(* sets *)
|
||||||
| Empty_set : 'a comparable_ty ->
|
| Empty_set : 'a comparable_ty ->
|
||||||
('rest, 'a set * 'rest) instr
|
('rest, 'a set * 'rest) instr
|
||||||
@ -146,6 +151,9 @@ and ('bef, 'aft) instr =
|
|||||||
| Set_reduce :
|
| Set_reduce :
|
||||||
(('param * 'res, 'res) lambda *
|
(('param * 'res, 'res) lambda *
|
||||||
('param set * ('res * 'rest)), 'res * 'rest) instr
|
('param set * ('res * 'rest)), 'res * 'rest) instr
|
||||||
|
| Set_iter :
|
||||||
|
('a * 'rest, 'rest) descr ->
|
||||||
|
('a set * 'rest, 'rest) instr
|
||||||
| Set_mem :
|
| Set_mem :
|
||||||
('elt * ('elt set * 'rest), bool * 'rest) instr
|
('elt * ('elt set * 'rest), bool * 'rest) instr
|
||||||
| Set_update :
|
| Set_update :
|
||||||
@ -159,6 +167,9 @@ and ('bef, 'aft) instr =
|
|||||||
| Map_reduce :
|
| Map_reduce :
|
||||||
((('a * 'v) * 'res, 'res) lambda *
|
((('a * 'v) * 'res, 'res) lambda *
|
||||||
(('a, 'v) map * ('res * 'rest)), 'res * 'rest) instr
|
(('a, 'v) map * ('res * 'rest)), 'res * 'rest) instr
|
||||||
|
| Map_iter :
|
||||||
|
(('a * 'v) * 'rest, 'rest) descr ->
|
||||||
|
(('a, 'v) map * 'rest, 'rest) instr
|
||||||
| Map_mem :
|
| Map_mem :
|
||||||
('a * (('a, 'v) map * 'rest), bool * 'rest) instr
|
('a * (('a, 'v) map * 'rest), bool * 'rest) instr
|
||||||
| Map_get :
|
| Map_get :
|
||||||
@ -263,6 +274,8 @@ and ('bef, 'aft) instr =
|
|||||||
(bool * 'bef, 'aft) instr
|
(bool * 'bef, 'aft) instr
|
||||||
| Loop : ('rest, bool * 'rest) descr ->
|
| Loop : ('rest, bool * 'rest) descr ->
|
||||||
(bool * 'rest, 'rest) instr
|
(bool * 'rest, 'rest) instr
|
||||||
|
| Loop_left : ('a * 'rest, ('a, 'b) union * 'rest) descr ->
|
||||||
|
(('a, 'b) union * 'rest, 'b * 'rest) instr
|
||||||
| Dip : ('bef, 'aft) descr ->
|
| Dip : ('bef, 'aft) descr ->
|
||||||
('top * 'bef, 'top * 'aft) instr
|
('top * 'bef, 'top * 'aft) instr
|
||||||
| Exec :
|
| Exec :
|
||||||
|
@ -207,6 +207,8 @@ module Script : sig
|
|||||||
| I_UNIT
|
| I_UNIT
|
||||||
| I_UPDATE
|
| I_UPDATE
|
||||||
| I_XOR
|
| I_XOR
|
||||||
|
| I_ITER
|
||||||
|
| I_LOOP_LEFT
|
||||||
| T_bool
|
| T_bool
|
||||||
| T_contract
|
| T_contract
|
||||||
| T_int
|
| T_int
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
parameter unit
|
parameter unit;
|
||||||
code
|
code
|
||||||
{ # This contract will never accept a incoming transaction
|
{ # This contract will never accept a incoming transaction
|
||||||
FAIL}
|
FAIL};
|
||||||
return unit
|
return unit;
|
||||||
storage unit
|
storage unit;
|
||||||
|
6
test/contracts/list_iter.tz
Normal file
6
test/contracts/list_iter.tz
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
parameter (list int);
|
||||||
|
storage unit;
|
||||||
|
return int;
|
||||||
|
code { CAR; PUSH int 1; SWAP;
|
||||||
|
ITER { MUL };
|
||||||
|
UNIT; SWAP; PAIR}
|
6
test/contracts/list_iter2.tz
Normal file
6
test/contracts/list_iter2.tz
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
parameter (list string);
|
||||||
|
return string;
|
||||||
|
storage unit;
|
||||||
|
code { CAR; PUSH string ""; SWAP;
|
||||||
|
ITER { CONCAT };
|
||||||
|
UNIT; SWAP; PAIR}
|
6
test/contracts/list_map_block.tz
Normal file
6
test/contracts/list_map_block.tz
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
parameter (list int);
|
||||||
|
return (list int);
|
||||||
|
storage unit;
|
||||||
|
code { CAR; PUSH int 0; SWAP;
|
||||||
|
MAP { DIP{DUP}; ADD; DIP{PUSH int 1; ADD}};
|
||||||
|
UNIT; SWAP; PAIR; DIP{DROP}}
|
8
test/contracts/loop_left.tz
Normal file
8
test/contracts/loop_left.tz
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
parameter (list string);
|
||||||
|
return (list string);
|
||||||
|
storage unit;
|
||||||
|
code { CAR; NIL string; SWAP; PAIR; LEFT (list string);
|
||||||
|
LOOP_LEFT { DUP; CAR; DIP{CDR};
|
||||||
|
IF_CONS { SWAP; DIP{CONS}; PAIR; LEFT (list string) }
|
||||||
|
{ RIGHT (pair (list string) (list string)) }; };
|
||||||
|
UNIT; SWAP; PAIR }
|
7
test/contracts/map_iter.tz
Normal file
7
test/contracts/map_iter.tz
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
parameter (map int int);
|
||||||
|
return (pair int int);
|
||||||
|
storage unit;
|
||||||
|
code { CAR; PUSH int 0; DUP; PAIR; SWAP;
|
||||||
|
ITER { DIP {DUP; CAR; DIP{CDR}}; DUP; # Last instr
|
||||||
|
DIP{CAR; ADD}; SWAP; DIP{CDR; ADD}; PAIR };
|
||||||
|
UNIT; SWAP; PAIR}
|
4
test/contracts/set_iter.tz
Normal file
4
test/contracts/set_iter.tz
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
parameter (set int);
|
||||||
|
return int;
|
||||||
|
storage unit;
|
||||||
|
code { CAR; PUSH int 0; SWAP; ITER { ADD }; UNIT; SWAP; PAIR }
|
@ -13,6 +13,6 @@ code { DUP; DUP;
|
|||||||
DIIP{CDAR}; # Place contracts below numbers
|
DIIP{CDAR}; # Place contracts below numbers
|
||||||
DIP{CADR}; # Get actual rain
|
DIP{CADR}; # Get actual rain
|
||||||
CDDAR; # Get rain threshold
|
CDDAR; # Get rain threshold
|
||||||
CMPLT; IF {CAR @lt; ANNOT @winner} {CDR @geq; ANNOT @winner}; # Select contract to receive tokens
|
CMPLT; IF {CAR @winner} {CDR @winner}; # Select contract to receive tokens
|
||||||
BALANCE; UNIT; TRANSFER_TOKENS; # Setup and execute transfer
|
BALANCE; UNIT; TRANSFER_TOKENS; # Setup and execute transfer
|
||||||
PAIR }; # Save storage
|
PAIR }; # Save storage
|
||||||
|
@ -15,9 +15,16 @@ key2=bar
|
|||||||
$client gen keys $key1
|
$client gen keys $key1
|
||||||
$client gen keys $key2
|
$client gen keys $key2
|
||||||
|
|
||||||
|
CONTRACT_PATH=contracts
|
||||||
|
|
||||||
printf "\n\n"
|
printf "\n\n"
|
||||||
|
|
||||||
CONTRACT_PATH=contracts
|
# Assert well typed
|
||||||
|
echo "Typechecking contracts in '${CONTRACT_PATH}'"
|
||||||
|
ls $CONTRACT_PATH \
|
||||||
|
| xargs -I{} $client typecheck program $CONTRACT_PATH/{} > /dev/null
|
||||||
|
|
||||||
|
printf "All contracts are well typed\n\n"
|
||||||
|
|
||||||
# FORMAT: assert_output contract_file storage input expected_result
|
# FORMAT: assert_output contract_file storage input expected_result
|
||||||
|
|
||||||
@ -94,6 +101,19 @@ assert_output $CONTRACT_PATH/map_id.tz Unit '(Map (Item 0 1))' '(Map (Item 0 1))
|
|||||||
assert_output $CONTRACT_PATH/map_id.tz Unit '(Map (Item 0 0))' '(Map (Item 0 0))'
|
assert_output $CONTRACT_PATH/map_id.tz Unit '(Map (Item 0 0))' '(Map (Item 0 0))'
|
||||||
assert_output $CONTRACT_PATH/map_id.tz Unit '(Map (Item 0 0) (Item 3 4))' '(Map (Item 0 0) (Item 3 4))'
|
assert_output $CONTRACT_PATH/map_id.tz Unit '(Map (Item 0 0) (Item 3 4))' '(Map (Item 0 0) (Item 3 4))'
|
||||||
|
|
||||||
|
# Map block on lists
|
||||||
|
assert_output $CONTRACT_PATH/list_map_block.tz Unit '(List)' 'List'
|
||||||
|
assert_output $CONTRACT_PATH/list_map_block.tz Unit '(List 1 1 1 1)' '(List 1 2 3 4)'
|
||||||
|
assert_output $CONTRACT_PATH/list_map_block.tz Unit '(List 1 2 3 0)' '(List 1 3 5 3)'
|
||||||
|
|
||||||
|
# List iter
|
||||||
|
assert_output $CONTRACT_PATH/list_iter.tz Unit '(List 10 2 1)' 20
|
||||||
|
assert_output $CONTRACT_PATH/list_iter.tz Unit '(List 3 6 9)' 162
|
||||||
|
|
||||||
|
assert_output $CONTRACT_PATH/list_iter2.tz Unit '(List "a" "b" "c")' '"cba"'
|
||||||
|
assert_output $CONTRACT_PATH/list_iter2.tz Unit '(List)' '""'
|
||||||
|
|
||||||
|
|
||||||
# Identity on sets
|
# Identity on sets
|
||||||
assert_output $CONTRACT_PATH/set_id.tz Unit '(Set "a" "b" "c")' '(Set "a" "b" "c")'
|
assert_output $CONTRACT_PATH/set_id.tz Unit '(Set "a" "b" "c")' '(Set "a" "b" "c")'
|
||||||
assert_output $CONTRACT_PATH/set_id.tz Unit '(Set)' 'Set'
|
assert_output $CONTRACT_PATH/set_id.tz Unit '(Set)' 'Set'
|
||||||
@ -110,6 +130,11 @@ assert_output $CONTRACT_PATH/set_size.tz Unit '(Set 1)' 1
|
|||||||
assert_output $CONTRACT_PATH/set_size.tz Unit '(Set 1 2 3)' 3
|
assert_output $CONTRACT_PATH/set_size.tz Unit '(Set 1 2 3)' 3
|
||||||
assert_output $CONTRACT_PATH/set_size.tz Unit '(Set 1 2 3 4 5 6)' 6
|
assert_output $CONTRACT_PATH/set_size.tz Unit '(Set 1 2 3 4 5 6)' 6
|
||||||
|
|
||||||
|
# Set iter
|
||||||
|
assert_output $CONTRACT_PATH/set_iter.tz Unit '(Set)' 0
|
||||||
|
assert_output $CONTRACT_PATH/set_iter.tz Unit '(Set 1)' 1
|
||||||
|
assert_output $CONTRACT_PATH/set_iter.tz Unit '(Set -100 1 2 3)' '-94'
|
||||||
|
|
||||||
# Map size
|
# Map size
|
||||||
assert_output $CONTRACT_PATH/map_size.tz Unit '(Map)' 0
|
assert_output $CONTRACT_PATH/map_size.tz Unit '(Map)' 0
|
||||||
assert_output $CONTRACT_PATH/map_size.tz Unit '(Map (Item "a" 1))' 1
|
assert_output $CONTRACT_PATH/map_size.tz Unit '(Map (Item "a" 1))' 1
|
||||||
@ -154,6 +179,10 @@ assert_output $CONTRACT_PATH/get_map_value.tz \
|
|||||||
'(Map (Item "1" "one") (Item "2" "two"))' \
|
'(Map (Item "1" "one") (Item "2" "two"))' \
|
||||||
'"1"' '(Some "one")'
|
'"1"' '(Some "one")'
|
||||||
|
|
||||||
|
# Map iter
|
||||||
|
assert_output $CONTRACT_PATH/map_iter.tz Unit '(Map (Item 0 100) (Item 2 100))' '(Pair 2 200)'
|
||||||
|
assert_output $CONTRACT_PATH/map_iter.tz Unit '(Map (Item 1 1) (Item 2 100))' '(Pair 3 101)'
|
||||||
|
|
||||||
# Return True if True branch of if was taken and False otherwise
|
# Return True if True branch of if was taken and False otherwise
|
||||||
assert_output $CONTRACT_PATH/if.tz Unit True True
|
assert_output $CONTRACT_PATH/if.tz Unit True True
|
||||||
assert_output $CONTRACT_PATH/if.tz Unit False False
|
assert_output $CONTRACT_PATH/if.tz Unit False False
|
||||||
@ -168,6 +197,10 @@ assert_output $CONTRACT_PATH/reverse.tz Unit '(List "c" "b" "a")' '(List "a" "b"
|
|||||||
assert_output $CONTRACT_PATH/reverse_loop.tz Unit '(List )' 'List'
|
assert_output $CONTRACT_PATH/reverse_loop.tz Unit '(List )' 'List'
|
||||||
assert_output $CONTRACT_PATH/reverse_loop.tz Unit '(List "c" "b" "a")' '(List "a" "b" "c")'
|
assert_output $CONTRACT_PATH/reverse_loop.tz Unit '(List "c" "b" "a")' '(List "a" "b" "c")'
|
||||||
|
|
||||||
|
# Reverse using LOOP_LEFT
|
||||||
|
assert_output $CONTRACT_PATH/loop_left.tz Unit '(List )' 'List'
|
||||||
|
assert_output $CONTRACT_PATH/loop_left.tz Unit '(List "c" "b" "a")' '(List "a" "b" "c")'
|
||||||
|
|
||||||
# Exec concat contract
|
# Exec concat contract
|
||||||
assert_output $CONTRACT_PATH/exec_concat.tz Unit '""' '"_abc"'
|
assert_output $CONTRACT_PATH/exec_concat.tz Unit '""' '"_abc"'
|
||||||
assert_output $CONTRACT_PATH/exec_concat.tz Unit '"test"' '"test_abc"'
|
assert_output $CONTRACT_PATH/exec_concat.tz Unit '"test"' '"test_abc"'
|
||||||
|
Loading…
Reference in New Issue
Block a user