Deprecate AEVM (#375)

* Deprecate AEVM

* Fix test, changelog

* Restore old rebar

* rebar lock fix

* undo export

Co-authored-by: Gaith Hallak <gaithhallak@gmail.com>

* undo export

Co-authored-by: Gaith Hallak <gaithhallak@gmail.com>

* Solve GH suggestions

* Fix the docs

* update docs

* Remove unused tests

* undo weird change

Co-authored-by: Gaith Hallak <gaithhallak@gmail.com>
This commit is contained in:
Radosław Rowicki
2022-05-10 15:33:59 +02:00
committed by GitHub
parent c5bfcd3bdc
commit 0af45dfd19
49 changed files with 219 additions and 4291 deletions
+53 -88
View File
@@ -5,7 +5,6 @@
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
-define(DUMMY_HASH_WORD, 16#123).
-define(DUMMY_HASH, <<0:30/unit:8, 127, 119>>). %% 16#123
-define(DUMMY_HASH_LIT, "#0000000000000000000000000000000000000000000000000000000000000123").
sandbox(Code) ->
@@ -20,12 +19,6 @@ sandbox(Code) ->
{error, loop}
end.
malicious_from_binary_test() ->
CircularList = from_words([32, 1, 32]), %% Xs = 1 :: Xs
{ok, {error, circular_references}} = ?SANDBOX(aeb_heap:from_binary({list, word}, CircularList)),
{ok, {error, {binary_too_short, _}}} = ?SANDBOX(aeb_heap:from_binary(word, <<1, 2, 3, 4>>)),
ok.
from_words(Ws) ->
<< <<(from_word(W))/binary>> || W <- Ws >>.
@@ -37,23 +30,14 @@ from_word(S) when is_list(S) ->
<<Len:256, Bin/binary>>.
encode_decode_test() ->
encode_decode(word, 42),
42 = encode_decode(word, 42),
-1 = encode_decode(signed_word, -1),
<<"Hello world">> = encode_decode(string, <<"Hello world">>),
{} = encode_decode({tuple, []}, {}),
{42} = encode_decode({tuple, [word]}, {42}),
{42, 0} = encode_decode({tuple, [word, word]}, {42, 0}),
[] = encode_decode({list, word}, []),
[32] = encode_decode({list, word}, [32]),
none = encode_decode({option, word}, none),
{some, 1} = encode_decode({option, word}, {some, 1}),
string = encode_decode(typerep, string),
word = encode_decode(typerep, word),
{list, word} = encode_decode(typerep, {list, word}),
{tuple, [word]} = encode_decode(typerep, {tuple, [word]}),
1 = encode_decode(word, 1),
0 = encode_decode(word, 0),
Tests =
[42, 1, 0 -1, <<"Hello">>,
{tuple, {}}, {tuple, {42}}, {tuple, {21, 37}},
[], [42], [21, 37],
{variant, [0, 1], 0, {}}, {variant, [0, 1], 1, {42}}, {variant, [2], 0, {21, 37}},
{typerep, string}, {typerep, integer}, {typerep, {list, integer}}, {typerep, {tuple, [integer]}}
],
[?assertEqual(Test, encode_decode(Test)) || Test <- Tests],
ok.
encode_decode_sophia_test() ->
@@ -95,55 +79,44 @@ to_sophia_value_mcl_bls12_381_test() ->
to_sophia_value_neg_test() ->
Code = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
" entrypoint f(x : int) : string = \"hello\"\n" ],
{error, [Err1]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12)),
?assertEqual("Data error:\nFailed to decode binary as type string\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12), [{backend, fate}]),
?assertEqual("Data error:\nFailed to decode binary as type string\n", aeso_errors:pp(Err2)),
{error, [Err1]} = aeso_compiler:to_sophia_value(Code, "f", ok, encode(12)),
?assertEqual("Data error:\nCannot translate FATE value 12\n of Sophia type string\n", aeso_errors:pp(Err1)),
{error, [Err3]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12)),
?assertEqual("Data error:\nCould not interpret the revert message\n", aeso_errors:pp(Err3)),
{error, [Err4]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12), [{backend, fate}]),
?assertEqual("Data error:\nCould not deserialize the revert message\n", aeso_errors:pp(Err4)),
{error, [Err2]} = aeso_compiler:to_sophia_value(Code, "f", revert, encode(12)),
?assertEqual("Data error:\nCould not deserialize the revert message\n", aeso_errors:pp(Err2)),
ok.
encode_calldata_neg_test() ->
Code = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
" entrypoint f(x : int) : string = \"hello\"\n" ],
ExpErr1 = "Type error at line 5, col 34:\nCannot unify `int` and `bool`\n"
"when checking the application of\n"
" `x : (int) => string`\n"
" `f : (int) => string`\n"
"to arguments\n"
" `true : bool`\n",
{error, [Err1]} = aeso_compiler:create_calldata(Code, "x", ["true"]),
{error, [Err1]} = aeso_compiler:create_calldata(Code, "f", ["true"]),
?assertEqual(ExpErr1, aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:create_calldata(Code, "x", ["true"], [{backend, fate}]),
?assertEqual(ExpErr1, aeso_errors:pp(Err2)),
ok.
decode_calldata_neg_test() ->
Code1 = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
" entrypoint f(x : int) : string = \"hello\"\n" ],
Code2 = [ "contract Foo =\n"
" entrypoint x(y : string) : int = 42\n" ],
" entrypoint f(x : string) : int = 42\n" ],
{ok, CallDataAEVM} = aeso_compiler:create_calldata(Code1, "x", ["42"]),
{ok, CallDataFATE} = aeso_compiler:create_calldata(Code1, "x", ["42"], [{backend, fate}]),
{ok, CallDataFATE} = aeso_compiler:create_calldata(Code1, "f", ["42"]),
{error, [Err1]} = aeso_compiler:decode_calldata(Code2, "x", CallDataAEVM),
?assertEqual("Data error:\nFailed to decode calldata as type {tuple,[string]}\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:decode_calldata(Code2, "x", <<1,2,3>>, [{backend, fate}]),
?assertEqual("Data error:\nFailed to decode calldata binary\n", aeso_errors:pp(Err2)),
{error, [Err3]} = aeso_compiler:decode_calldata(Code2, "x", CallDataFATE, [{backend, fate}]),
?assertEqual("Data error:\nCannot translate FATE value \"*\"\n to Sophia type (string)\n", aeso_errors:pp(Err3)),
{error, [Err1]} = aeso_compiler:decode_calldata(Code2, "f", <<1,2,3>>),
?assertEqual("Data error:\nFailed to decode calldata binary\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:decode_calldata(Code2, "f", CallDataFATE),
?assertEqual("Data error:\nCannot translate FATE value \"*\"\n to Sophia type (string)\n", aeso_errors:pp(Err2)),
{error, [Err4]} = aeso_compiler:decode_calldata(Code2, "y", CallDataAEVM),
?assertEqual("Data error at line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err4)),
{error, [Err5]} = aeso_compiler:decode_calldata(Code2, "y", CallDataFATE, [{backend, fate}]),
?assertEqual("Data error at line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err5)),
{error, [Err3]} = aeso_compiler:decode_calldata(Code2, "x", CallDataFATE),
?assertEqual("Data error at line 1, col 1:\nFunction 'x' is missing in contract\n", aeso_errors:pp(Err3)),
ok.
@@ -156,8 +129,7 @@ encode_decode_sophia_string(SophiaType, String) ->
, " datatype variant = Red | Blue(map(string, int))\n"
, " entrypoint foo : arg_type => arg_type\n" ],
case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_code]) of
{ok, _, {[Type], _}, [Arg]} ->
io:format("Type ~p~n", [Type]),
{ok, _, [Arg]} ->
Data = encode(Arg),
case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_code]) of
{ok, Sophia} ->
@@ -173,30 +145,32 @@ encode_decode_sophia_string(SophiaType, String) ->
calldata_test() ->
[42, <<"foobar">>] = encode_decode_calldata("foo", ["int", "string"], ["42", "\"foobar\""]),
Map = #{ <<"a">> => 4 },
[{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] =
[{variant, [0,1], 1, {#{ <<"a">> := 4 }}}, {tuple, {{tuple, {<<"b">>, 5}}, {variant, [0,1], 0, {}}}}] =
encode_decode_calldata("foo", ["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]),
[?DUMMY_HASH_WORD, 16#456] = encode_decode_calldata("foo", ["bytes(32)", "address"],
[?DUMMY_HASH_LIT, "ak_1111111111111111111111111111113AFEFpt5"]),
[?DUMMY_HASH_WORD, ?DUMMY_HASH_WORD] =
[{bytes, <<291:256>>}, {address, <<1110:256>>}] =
encode_decode_calldata("foo", ["bytes(32)", "address"],
[?DUMMY_HASH_LIT, "ak_1111111111111111111111111111113AFEFpt5"]),
[{bytes, <<291:256>>}, {bytes, <<291:256>>}] =
encode_decode_calldata("foo", ["bytes(32)", "hash"], [?DUMMY_HASH_LIT, ?DUMMY_HASH_LIT]),
[119, {0, 0}] = encode_decode_calldata("foo", ["int", "signature"], ["119", [$# | lists:duplicate(128, $0)]]),
[119, {bytes, <<0:64/unit:8>>}] = encode_decode_calldata("foo", ["int", "signature"], ["119", [$# | lists:duplicate(128, $0)]]),
[16#456] = encode_decode_calldata("foo", ["Remote"], ["ct_1111111111111111111111111111113AFEFpt5"]),
[{contract, <<1110:256>>}] = encode_decode_calldata("foo", ["Remote"], ["ct_1111111111111111111111111111113AFEFpt5"]),
ok.
calldata_init_test() ->
encode_decode_calldata("init", ["int"], ["42"], {tuple, [typerep, word]}),
encode_decode_calldata("init", ["int"], ["42"]),
Code = parameterized_contract("foo", ["int"]),
encode_decode_calldata_(Code, "init", [], {tuple, [typerep, {tuple, []}]}).
encode_decode_calldata_(Code, "init", []),
ok.
calldata_indent_test() ->
Test = fun(Extra) ->
Code = parameterized_contract(Extra, "foo", ["int"]),
encode_decode_calldata_(Code, "foo", ["42"], word)
encode_decode_calldata_(Code, "foo", ["42"])
end,
Test(" stateful entrypoint bla() = ()"),
Test(" type x = int"),
@@ -225,9 +199,9 @@ oracle_test() ->
"contract OracleTest =\n"
" entrypoint question(o, q : oracle_query(list(string), option(int))) =\n"
" Oracle.get_question(o, q)\n",
{ok, _, {[word, word], {list, string}}, [16#123, 16#456]} =
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], [no_code]),
?assertEqual({ok, "question", [{oracle, <<291:256>>}, {oracle_query, <<1110:256>>}]},
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], [no_code])),
ok.
@@ -243,35 +217,26 @@ permissive_literals_fail_test() ->
ok.
encode_decode_calldata(FunName, Types, Args) ->
encode_decode_calldata(FunName, Types, Args, word).
encode_decode_calldata(FunName, Types, Args, RetType) ->
Code = parameterized_contract(FunName, Types),
encode_decode_calldata_(Code, FunName, Args, RetType).
encode_decode_calldata_(Code, FunName, Args).
encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
encode_decode_calldata_(Code, FunName, Args) ->
{ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []),
{ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_code]),
?assertEqual(RetType, RetVMType),
CalldataType = {tuple, [word, {tuple, ArgTypes}]},
{ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata),
{ok, _, _} = aeso_compiler:check_call(Code, FunName, Args, [no_code]),
case FunName of
"init" ->
ok;
[];
_ ->
{ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata, []),
Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ],
?assertMatch({X, X}, {Args, Values})
end,
tuple_to_list(ArgTuple).
{ok, FateArgs} = aeb_fate_abi:decode_calldata(FunName, Calldata),
FateArgs
end.
encode_decode(T, D) ->
?assertEqual(D, decode(T, encode(D))),
encode_decode(D) ->
?assertEqual(D, decode(encode(D))),
D.
encode(D) ->
aeb_heap:to_binary(D).
aeb_fate_encoding:serialize(D).
decode(T,B) ->
{ok, D} = aeb_heap:from_binary(T, B),
D.
decode(B) ->
aeb_fate_encoding:deserialize(B).
+1 -1
View File
@@ -108,7 +108,7 @@ aci_test_contract(Name) ->
{error, ErrorStringJ} when is_binary(ErrorStringJ) -> error(ErrorStringJ);
{error, ErrorJ} -> aeso_compiler_tests:print_and_throw(ErrorJ)
end,
case aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]) of
case aeso_compiler:from_string(String, [{aci, json} | Opts]) of
{ok, #{aci := JSON1}} ->
?assertEqual(JSON, JSON1),
io:format("JSON:\n~p\n", [JSON]),
+6 -29
View File
@@ -19,19 +19,9 @@ calldata_test_() ->
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
fun() ->
ContractString = aeso_test_utils:read_contract(ContractName),
AevmExprs =
case not lists:member(ContractName, not_yet_compilable(aevm)) of
true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]);
false -> undefined
end,
FateExprs =
case not lists:member(ContractName, not_yet_compilable(fate)) of
true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]);
false -> undefined
end,
FateExprs = ast_exprs(ContractString, Fun, Args),
ParsedExprs = parse_args(Fun, Args),
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
?assertEqual(ParsedExprs, FateExprs),
ok
end} || {ContractName, Fun, Args} <- compilable_contracts()].
@@ -42,19 +32,9 @@ calldata_aci_test_() ->
{ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString),
ContractACI = binary_to_list(ContractACIBin),
io:format("ACI:\n~s\n", [ContractACIBin]),
AevmExprs =
case not lists:member(ContractName, not_yet_compilable(aevm)) of
true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}]);
false -> undefined
end,
FateExprs =
case not lists:member(ContractName, not_yet_compilable(fate)) of
true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]);
false -> undefined
end,
FateExprs = ast_exprs(ContractACI, Fun, Args),
ParsedExprs = parse_args(Fun, Args),
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
?assertEqual(ParsedExprs, FateExprs),
ok
end} || {ContractName, Fun, Args} <- compilable_contracts()].
@@ -75,6 +55,8 @@ strip_ann1(L) when is_list(L) ->
lists:map(fun strip_ann/1, L);
strip_ann1(X) -> X.
ast_exprs(ContractString, Fun, Args) ->
ast_exprs(ContractString, Fun, Args, []).
ast_exprs(ContractString, Fun, Args, Opts) ->
{ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)),
{ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
@@ -159,8 +141,3 @@ compilable_contracts() ->
{"stub", "foo", ["-42"]},
{"payable", "foo", ["42"]}
].
not_yet_compilable(fate) ->
[];
not_yet_compilable(aevm) ->
["funargs", "strings"].
+39 -121
View File
@@ -24,20 +24,15 @@ run_test(Test) ->
%% are made on the output, just that it is a binary which indicates
%% that the compilation worked.
simple_compile_test_() ->
[ {"Testing the " ++ ContractName ++ " contract with the " ++ atom_to_list(Backend) ++ " backend",
[ {"Testing the " ++ ContractName ++ " contract",
fun() ->
case compile(Backend, ContractName) of
#{byte_code := ByteCode,
contract_source := _,
type_info := _} when Backend == aevm ->
?assertMatch(Code when is_binary(Code), ByteCode);
#{fate_code := Code} when Backend == fate ->
case compile(ContractName) of
#{fate_code := Code} ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
?assertMatch({X, X}, {Code1, Code});
Error -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
end
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
not lists:member(ContractName, not_compilable_on(Backend))] ++
end} || ContractName <- compilable_contracts()] ++
[ {"Test file not found error",
fun() ->
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
@@ -46,26 +41,16 @@ simple_compile_test_() ->
end} ] ++
[ {"Testing error messages of " ++ ContractName,
fun() ->
Errors = compile(aevm, ContractName, [warn_all, warn_error]),
Errors = compile(ContractName, [warn_all, warn_error]),
check_errors(ExpectedErrors, Errors)
end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
[ {"Testing " ++ atom_to_list(Backend) ++ " code generation error messages of " ++ ContractName,
[ {"Testing code generation error messages of " ++ ContractName,
fun() ->
Errors = compile(Backend, ContractName),
Expect =
case is_binary(ExpectedError) of
true -> [ExpectedError];
false ->
case proplists:get_value(Backend, ExpectedError, no_error) of
no_error -> no_error;
Err -> [Err]
end
end,
check_errors(Expect, Errors)
Errors = compile(ContractName),
check_errors([ExpectedError], Errors)
end} ||
{ContractName, ExpectedError} <- failing_code_gen_contracts(),
Backend <- [aevm, fate] ] ++
{ContractName, ExpectedError} <- failing_code_gen_contracts()] ++
[ {"Testing include with explicit files",
fun() ->
FileSystem = maps:from_list(
@@ -73,26 +58,25 @@ simple_compile_test_() ->
{ok, Bin} = file:read_file(filename:join([aeso_test_utils:contract_path(), File])),
{File, Bin}
end || File <- ["included.aes", "../contracts/included2.aes"] ]),
#{byte_code := Code1} = compile(aevm, "include", [{include, {explicit_files, FileSystem}}]),
#{byte_code := Code2} = compile(aevm, "include"),
#{byte_code := Code1} = compile("include", [{include, {explicit_files, FileSystem}}]),
#{byte_code := Code2} = compile("include"),
?assertMatch(true, Code1 == Code2)
end} ] ++
[ {"Testing deadcode elimination for " ++ atom_to_list(Backend),
[ {"Testing deadcode elimination",
fun() ->
#{ byte_code := NoDeadCode } = compile(Backend, "nodeadcode"),
#{ byte_code := DeadCode } = compile(Backend, "deadcode"),
#{ byte_code := NoDeadCode } = compile("nodeadcode"),
#{ byte_code := DeadCode } = compile("deadcode"),
SizeNoDeadCode = byte_size(NoDeadCode),
SizeDeadCode = byte_size(DeadCode),
Delta = if Backend == aevm -> 40;
Backend == fate -> 20 end,
Delta = 20,
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + Delta < SizeNoDeadCode}),
ok
end} || Backend <- [aevm, fate] ] ++
end} ] ++
[ {"Testing warning messages",
fun() ->
#{ warnings := Warnings } = compile(Backend, "warnings", [warn_all]),
#{ warnings := Warnings } = compile("warnings", [warn_all]),
check_warnings(warnings(), Warnings)
end} || Backend <- [aevm, fate] ] ++
end} ] ++
[].
%% Check if all modules in the standard library compile
@@ -101,7 +85,7 @@ stdlib_test_() ->
[ { "Testing " ++ File ++ " from the stdlib",
fun() ->
String = "include \"" ++ File ++ "\"\nmain contract Test =\n entrypoint f(x) = x",
Options = [{src_file, File}, {backend, fate}],
Options = [{src_file, File}],
case aeso_compiler:from_string(String, Options) of
{ok, #{fate_code := Code}} ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
@@ -133,18 +117,17 @@ check_warnings(Expect0, Actual0) ->
{Missing, Extra} -> ?assertEqual(Missing, Extra)
end.
compile(Backend, Name) ->
compile(Backend, Name,
[{include, {file_system, [aeso_test_utils:contract_path()]}}]).
compile(Name) ->
compile( Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]).
compile(Backend, Name, Options) ->
compile(Name, Options) ->
String = aeso_test_utils:read_contract(Name),
Options1 =
case lists:member(Name, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[ {src_file, Name ++ ".aes"}, {backend, Backend}
[ {src_file, Name ++ ".aes"}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
] ++ Options,
case aeso_compiler:from_string(String, Options1) of
@@ -222,9 +205,6 @@ compilable_contracts() ->
"test" % Custom general-purpose test file. Keep it last on the list.
].
not_compilable_on(fate) -> [];
not_compilable_on(aevm) -> compilable_contracts().
debug_mode_contracts() ->
["hermetization_turnoff"].
@@ -840,113 +820,51 @@ failing_contracts() ->
-define(Path(File), "code_errors/" ??File).
-define(Msg(File, Line, Col, Err), <<?Pos("Code generation", ?Path(File), Line, Col) Err>>).
-define(SAME(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}).
-define(AEVM(File, Line, Col, Err), {?Path(File), [{aevm, ?Msg(File, Line, Col, Err)}]}).
-define(FATE(File, Line, Col, Err), {?Path(File), [{fate, ?Msg(File, Line, Col, Err)}]}).
-define(BOTH(File, Line, Col, ErrAEVM, ErrFATE),
{?Path(File), [{aevm, ?Msg(File, Line, Col, ErrAEVM)},
{fate, ?Msg(File, Line, Col, ErrFATE)}]}).
-define(FATE_ERR(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}).
failing_code_gen_contracts() ->
[ ?SAME(missing_definition, 2, 14,
[ ?FATE_ERR(missing_definition, 2, 14,
"Missing definition of function 'foo'.")
, ?AEVM(polymorphic_entrypoint, 2, 17,
"The argument\n"
" x : 'a\n"
"of entrypoint 'id' has a polymorphic (contains type variables) type.\n"
"Use the FATE backend if you want polymorphic entrypoints.")
, ?AEVM(polymorphic_entrypoint_return, 2, 3,
"The return type\n"
" 'a\n"
"of entrypoint 'fail' is polymorphic (contains type variables).\n"
"Use the FATE backend if you want polymorphic entrypoints.")
, ?SAME(higher_order_entrypoint, 2, 20,
, ?FATE_ERR(higher_order_entrypoint, 2, 20,
"The argument\n"
" f : (int) => int\n"
"of entrypoint 'apply' has a higher-order (contains function types) type.")
, ?SAME(higher_order_entrypoint_return, 2, 3,
, ?FATE_ERR(higher_order_entrypoint_return, 2, 3,
"The return type\n"
" (int) => int\n"
"of entrypoint 'add' is higher-order (contains function types).")
, ?SAME(missing_init_function, 1, 10,
, ?FATE_ERR(missing_init_function, 1, 10,
"Missing init function for the contract 'MissingInitFunction'.\n"
"The 'init' function can only be omitted if the state type is 'unit'.")
, ?SAME(parameterised_state, 3, 8,
, ?FATE_ERR(parameterised_state, 3, 8,
"The state type cannot be parameterized.")
, ?SAME(parameterised_event, 3, 12,
, ?FATE_ERR(parameterised_event, 3, 12,
"The event type cannot be parameterized.")
, ?SAME(polymorphic_aens_resolve, 4, 5,
, ?FATE_ERR(polymorphic_aens_resolve, 4, 5,
"Invalid return type of AENS.resolve:\n"
" 'a\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?SAME(bad_aens_resolve, 6, 5,
, ?FATE_ERR(bad_aens_resolve, 6, 5,
"Invalid return type of AENS.resolve:\n"
" list(int)\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?AEVM(polymorphic_compare, 4, 5,
"Cannot compare values of type\n"
" 'a\n"
"The AEVM only supports '==' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"- type string\n"
"- tuple or record of word type\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(complex_compare, 4, 5,
"Cannot compare values of type\n"
" (string * int)\n"
"The AEVM only supports '!=' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"- type string\n"
"- tuple or record of word type\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(complex_compare_leq, 4, 5,
"Cannot compare values of type\n"
" (int * int)\n"
"The AEVM only supports '=<' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(higher_order_compare, 4, 5,
"Cannot compare values of type\n"
" (int) => int\n"
"The AEVM only supports '<' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(unapplied_contract_call, 6, 19,
"The AEVM does not support unapplied contract call to\n"
" r : Remote\n"
"Use FATE if you need this.")
, ?AEVM(unapplied_named_arg_builtin, 4, 15,
"The AEVM does not support unapplied use of Oracle.register.\n"
"Use FATE if you need this.")
, ?AEVM(polymorphic_map_keys, 4, 34,
"Invalid map key type\n"
" 'a\n"
"Map keys cannot be polymorphic in the AEVM. Use FATE if you need this.")
, ?AEVM(higher_order_map_keys, 4, 42,
"Invalid map key type\n"
" (int) => int\n"
"Map keys cannot be higher-order.")
, ?SAME(polymorphic_query_type, 3, 5,
, ?FATE_ERR(polymorphic_query_type, 3, 5,
"Invalid oracle type\n"
" oracle('a, 'b)\n"
"The query type must not be polymorphic (contain type variables).")
, ?SAME(polymorphic_response_type, 3, 5,
, ?FATE_ERR(polymorphic_response_type, 3, 5,
"Invalid oracle type\n"
" oracle(string, 'r)\n"
"The response type must not be polymorphic (contain type variables).")
, ?SAME(higher_order_query_type, 3, 5,
, ?FATE_ERR(higher_order_query_type, 3, 5,
"Invalid oracle type\n"
" oracle((int) => int, string)\n"
"The query type must not be higher-order (contain function types).")
, ?SAME(higher_order_response_type, 3, 5,
, ?FATE_ERR(higher_order_response_type, 3, 5,
"Invalid oracle type\n"
" oracle(string, (int) => int)\n"
"The response type must not be higher-order (contain function types).")
, ?AEVM(higher_order_state, 3, 3,
"Invalid state type\n"
" {f : (int) => int}\n"
"The state cannot contain functions in the AEVM. Use FATE if you need this.")
, ?FATE(child_with_decls, 2, 14,
, ?FATE_ERR(child_with_decls, 2, 14,
"Missing definition of function 'f'.")
].
@@ -985,7 +903,7 @@ validation_fails() ->
"Byte code contract is not payable, but source code contract is.">>]}].
validate(Contract1, Contract2) ->
case compile(fate, Contract1) of
case compile(Contract1) of
ByteCode = #{ fate_code := FCode } ->
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
Source = aeso_test_utils:read_contract(Contract2),
@@ -995,7 +913,7 @@ validate(Contract1, Contract2) ->
true -> [debug_mode];
false -> []
end ++
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]);
[{include, {file_system, [aeso_test_utils:contract_path()]}}]);
Error -> print_and_throw(Error)
end.
-15
View File
@@ -1,15 +0,0 @@
## Requires ocaml >= 4.02, < 4.06
## and reason-3.0.0 (opam install reason).
default : voting_test
%.ml : %.re
refmt -p ml $< > $@
voting_test : rte.ml voting.ml voting_test.ml
ocamlopt -o $@ $^
clean :
rm -f *.cmi *.cmx *.ml *.o voting_test
-31
View File
@@ -1,31 +0,0 @@
// A simple test of the abort built-in function.
contract AbortTest =
record state = { value : int }
public function init(v : int) =
{ value = v }
// Aborting
public function do_abort(v : int, s : string) : unit =
put_value(v)
revert_abort(s)
// Accessing the value
public function get_value() = state.value
public function put_value(v : int) = put(state{value = v})
public function get_values() : list(int) = [state.value]
public function put_values(v : int) = put(state{value = v})
// Some basic statistics
public function get_stats(acct : address) =
( Contract.balance, Chain.balance(acct) )
// Abort functions.
private function revert_abort(s : string) =
abort(s)
// This is still legal but will be stripped out.
// TODO: This function confuses the type inference, so it cannot be present.
//private function abort(s : string) = 42
-27
View File
@@ -1,27 +0,0 @@
contract Interface =
function do_abort : (int, string) => unit
function get_value : () => int
function put_value : (int) => unit
function get_values : () => list(int)
function put_values : (int) => unit
contract AbortTestInt =
record state = {r : Interface, value : int}
public function init(r : Interface, value : int) =
{r = r, value = value}
// Aborting
public function do_abort(v : int, s : string) =
put_value(v)
state.r.do_abort(v + 100, s)
// Accessing the value
public function put_value(v : int) = put(state{value = v})
public function get_value() = state.value
public function get_values() : list(int) =
state.value :: state.r.get_values()
public function put_values(v : int) =
put_value(v)
state.r.put_values(v + 1000)
-8
View File
@@ -1,8 +0,0 @@
contract ChannelEnv =
public function coinbase() : address = Chain.coinbase
public function timestamp() : int = Chain.timestamp
public function block_height() : int = Chain.block_height
public function difficulty() : int = Chain.difficulty
@@ -1,7 +0,0 @@
contract ChannelOnChainContractNameResolution =
public function can_resolve(name: string, key: string) : bool =
switch(AENS.resolve(name, key) : option(string))
None => false
Some(_address) => true
@@ -1,51 +0,0 @@
contract ChannelOnChainContractOracle =
type query_t = string
type answer_t = string
type oracle_id = oracle(query_t, answer_t)
type query_id = oracle_query(query_t, answer_t)
record state = { oracle : oracle_id,
question : string,
bets : map(string, address)
}
public function init(oracle: oracle_id, question: string) : state =
{ oracle = oracle,
question = question,
bets = {}
}
public stateful function place_bet(answer: string) =
switch(Map.lookup(answer, state.bets))
None =>
put(state{ bets = state.bets{[answer] = Call.caller}})
"ok"
Some(_value) =>
"bet_already_taken"
public function expiry() =
Oracle.expiry(state.oracle)
public function query_fee() =
Oracle.query_fee(state.oracle)
public function get_question(q: query_id) =
Oracle.get_question(state.oracle, q)
public stateful function resolve(q: query_id) =
switch(Oracle.get_answer(state.oracle, q))
None =>
"no response"
Some(result) =>
if(state.question == Oracle.get_question(state.oracle, q))
switch(Map.lookup(result, state.bets))
None =>
"no winning bet"
Some(winner) =>
Chain.spend(winner, Contract.balance)
"ok"
else
"different question"
@@ -1,9 +0,0 @@
contract Remote =
function get : () => int
function can_resolve : (string, string) => bool
contract RemoteCall =
function remote_resolve(r : Remote, name: string, key: string) : bool =
r.can_resolve(name, key)
-51
View File
@@ -1,51 +0,0 @@
contract Chess =
type board = map(int, map(int, string))
type state = board
private function get_row(r, m : board) =
Map.lookup_default(r, m, {})
private function set_piece(r, c, p, m : board) =
m { [r] = get_row(r, m) { [c] = p } }
private function get_piece(r, c, m : board) =
Map.lookup(c, get_row(r, m))
private function from_list(xs, m : board) =
switch(xs)
[] => m
(r, c, p) :: xs => from_list(xs, set_piece(r, c, p, m))
function init() =
from_list([ (2, 1, "white pawn"), (7, 1, "black pawn")
, (2, 2, "white pawn"), (7, 2, "black pawn")
, (2, 3, "white pawn"), (7, 3, "black pawn")
, (2, 4, "white pawn"), (7, 4, "black pawn")
, (2, 5, "white pawn"), (7, 5, "black pawn")
, (2, 6, "white pawn"), (7, 6, "black pawn")
, (2, 7, "white pawn"), (7, 7, "black pawn")
, (2, 8, "white pawn"), (7, 8, "black pawn")
, (1, 1, "white rook"), (8, 1, "black rook")
, (1, 2, "white knight"), (8, 2, "black knight")
, (1, 3, "white bishop"), (8, 3, "black bishop")
, (1, 4, "white queen"), (8, 4, "black queen")
, (1, 5, "white king"), (8, 5, "black king")
, (1, 6, "white bishop"), (8, 6, "black bishop")
, (1, 7, "white knight"), (8, 7, "black knight")
, (1, 8, "white rook"), (8, 8, "black rook")
], {})
function piece(r, c) = get_piece(r, c, state)
function move_piece(r, c, r1, c1) =
switch(piece(r, c))
Some(p) => put(set_piece(r1, c1, p, state))
function destroy_piece(r, c) =
put(state{ [r] = Map.delete(c, get_row(r, state)) })
function delete_row(r) =
put(Map.delete(r, state))
@@ -1,5 +1,4 @@
contract UnappliedNamedArgBuiltin =
// Allowed in FATE, but not AEVM
stateful entrypoint main_fun(s) =
let reg = Oracle.register
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)
-20
View File
@@ -1,20 +0,0 @@
contract OtherContract =
function multiply : (int, int) => int
contract ThisContract =
record state = { server : OtherContract, n : int }
function init(server : OtherContract) =
{ server = server, n = 2 }
function square() =
put(state{ n @ n = state.server.multiply(value = 100, n, n) })
function get_n() = state.n
function tip_server() =
Chain.spend(state.server.address, Call.value)
-87
View File
@@ -1,87 +0,0 @@
contract ERC20Token =
record state = {
totalSupply : int,
decimals : int,
name : string,
symbol : string,
balances : map(address, int),
allowed : map(address, map(address,int)),
// Logs, remove when native Events are there
transfer_log : list((address,address,int)),
approval_log : list((address,address,int))}
// init(100000000, 10, "Token Name", "TKN")
public stateful function init(_totalSupply : int, _decimals : int, _name : string, _symbol : string ) = {
totalSupply = _totalSupply,
decimals = _decimals,
name = _name,
symbol = _symbol,
balances = {[Call.caller] = _totalSupply }, // creator gets all Tokens
allowed = {},
// Logs, remove when native Events are there
transfer_log = [],
approval_log = []}
public stateful function totalSupply() : int = state.totalSupply
public stateful function decimals() : int = state.decimals
public stateful function name() : string = state.name
public stateful function symbol() : string = state.symbol
public stateful function balanceOf(tokenOwner : address ) : int =
Map.lookup_default(tokenOwner, state.balances, 0)
public stateful function transfer(to : address, tokens : int) =
put( state{balances[Call.caller] = sub(state.balances[Call.caller], tokens) })
put( state{balances[to] = add(Map.lookup_default(to, state.balances, 0), tokens) })
transferEvent(Call.caller, to, tokens)
true
public stateful function approve(spender : address, tokens : int) =
// allowed[Call.caller] field must have a value!
ensure_allowed(Call.caller)
put( state{allowed[Call.caller][spender] = tokens} )
approvalEvent(Call.caller, spender, tokens)
true
public stateful function transferFrom(from : address, to : address, tokens : int) =
put( state{ balances[from] = sub(state.balances[from], tokens) })
put( state{ allowed[from][Call.caller] = sub(state.allowed[from][Call.caller], tokens) })
put( state{ balances[to] = add(balanceOf(to), tokens) })
transferEvent(from, to, tokens)
true
public function allowance(_owner : address, _spender : address) : int =
state.allowed[_owner][_spender]
public stateful function getTransferLog() : list((address,address,int)) =
state.transfer_log
public stateful function getApprovalLog() : list((address,address,int)) =
state.approval_log
//
// Private Functions
//
private function ensure_allowed(key : address) =
switch(Map.lookup(key, state.allowed))
None => put(state{allowed[key] = {}})
Some(_) => ()
private function transferEvent(from : address, to : address, tokens : int) =
let e = (from, to, tokens)
put( state{transfer_log = e :: state.transfer_log })
e
private function approvalEvent(from : address, to : address, tokens : int) =
let e = (from, to, tokens)
put( state{approval_log = e :: state.approval_log })
e
private function sub(_a : int, _b : int) : int =
require(_b =< _a, "Error")
_a - _b
private function add(_a : int, _b : int) : int =
let c : int = _a + _b
require(c >= _a, "Error")
c
-6
View File
@@ -1,6 +0,0 @@
contract Exploits =
// We'll hack the bytecode of this changing the return type to string.
function pair(n : int) = (n, 0)
-9
View File
@@ -1,9 +0,0 @@
contract Remote =
function missing : (int) => int
contract Init_error =
record state = {value : int}
function init(r : Remote, x : int) =
{value = r.missing(x)}
-7
View File
@@ -1,7 +0,0 @@
contract Fail =
entrypoint tttt() : bool * int =
let f(x : 'a) : 'a = x
(f(true), f(1))
-36
View File
@@ -1,36 +0,0 @@
contract MapOfMaps =
type board = map(int, map(int, string))
type map2('a, 'b, 'c) = map('a, map('b, 'c))
record state = { big1 : map2(string, string, string),
big2 : map2(string, string, string),
small1 : map(string, string),
small2 : map(string, string) }
private function empty_state() =
{ big1 = {}, big2 = {},
small1 = {}, small2 = {} }
function init() = empty_state()
function setup_state() =
let small = {["key"] = "val"}
put({ big1 = {["one"] = small},
big2 = {["two"] = small},
small1 = small,
small2 = small })
// -- Garbage collection of inner map when outer map is garbage collected
function test1_setup() =
let inner = {["key"] = "val"}
put(empty_state() { big1 = {["one"] = inner} })
function test1_execute() =
put(state{ big1 = {} })
function test1_check() =
state.big1
-29
View File
@@ -1,29 +0,0 @@
contract MapUpdater =
function update_map : (int, string, map(int, string)) => map(int, string)
contract Benchmark =
record state = { updater : MapUpdater,
map : map(int, string) }
function init(u, m) = { updater = u, map = m }
function set_updater(u) = put(state{ updater = u })
function update_map(k : int, v : string, m) = m{ [k] = v }
function update(a : int, b : int, v : string) =
if (a > b) ()
else
put(state{ map[a] = v })
update(a + 1, b, v)
function get(k) = state.map[k]
function noop() = ()
function benchmark(k, v) =
let m = state.updater.update_map(k, v, state.map)
put(state{ map = m })
m
-6
View File
@@ -1,6 +0,0 @@
contract MinimalInit =
record state = {foo : int}
function init() =
{ foo = 0 }
-7
View File
@@ -1,7 +0,0 @@
contract MultiplicationServer =
function multiply(x : int, y : int) =
switch(Call.value >= 100)
true => x * y
-11
View File
@@ -1,11 +0,0 @@
contract OraclesErr =
public function unsafeCreateQueryThenErr(
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
let x = Oracle.query(o, q, qfee, qttl, rttl)
switch(0) 1 => ()
x // Never reached.
-22
View File
@@ -1,22 +0,0 @@
contract OraclesGas =
type fee = int
type question_t = string
type answer_t = int
public function happyPathWithAllBuiltinsAtSameHeight(
qfee : fee,
ottl : Chain.ttl,
ettl : Chain.ttl,
qttl : Chain.ttl,
rttl : Chain.ttl
) =
let question = "why"
let answer = 42
let o = Oracle.register(Contract.address, qfee, ottl) : oracle(question_t, answer_t)
Oracle.extend(o, ettl)
require(qfee =< Call.value, "insufficient value for qfee")
let q = Oracle.query(o, question, qfee, qttl, rttl)
Oracle.respond(o, q, answer)
()
-35
View File
@@ -1,35 +0,0 @@
contract Oracles =
type fee = int
type ttl = Chain.ttl
type query_t = string
type answer_t = string
type oracle_id = oracle(query_t, answer_t)
type query_id = oracle_query(query_t, answer_t)
function createQuery(o : oracle_id,
q : query_t,
qfee : fee,
qttl : ttl,
rttl : ttl) : query_id =
require(qfee =< Call.value, "insufficient value for qfee")
Oracle.query(o, q, qfee, qttl, rttl)
function respond(o : oracle_id,
q : query_id,
sign : signature,
r : answer_t) : unit =
Oracle.respond(o, q, signature = sign, r)
function getQuestion(o : oracle_id,
q : query_id) : query_t =
Oracle.get_question(o, q)
function getAnswer(o : oracle_id,
q : query_id) : option(answer_t) =
Oracle.get_answer(o, q)
-16
View File
@@ -1,16 +0,0 @@
contract Identity =
function zip_with(f, xs, ys) =
switch((xs, ys))
(x :: xs, y :: ys) => f(x, y) :: zip_with(f, xs, ys)
_ => []
// Check that we can use zip_with at different types
function foo() =
zip_with((x, y) => x + y, [1, 2, 3], [4, 5, 6, 7])
function bar() =
zip_with((x, y) => if(x) y else 0, [true, false, true, false], [1, 2, 3])
-57
View File
@@ -1,57 +0,0 @@
contract MapServer =
function insert : (string, string, map(string, string)) => map(string, string)
function delete : (string, map(string, string)) => map(string, string)
contract PrimitiveMaps =
record state = { remote : MapServer,
map : map(string, string),
map2 : map(string, string) }
function init(r) =
let m = {}
{ remote = r, map = m, map2 = m }
function set_remote(r) = put(state{ remote = r })
function insert(k, v, m) : map(string, string) = m{ [k] = v }
function delete(k, m) : map(string, string) = Map.delete(k, m)
function remote_insert(k, v, m) =
state.remote.insert(k, v, m)
function remote_delete(k, m) =
state.remote.delete(k, m)
function get_state_map() = state.map
function set_state_map(m) = put(state{ map = m })
function clone_state() = put(state{ map2 = state.map })
function insert_state(k, v) = put(state{ map @ m = m { [k] = v } })
function delete_state(k) = put(state{ map @ m = Map.delete(k, m) })
function lookup_state(k) = Map.lookup(k, state.map)
function double_insert_state(k, v1, v2) =
put(state{ map @ m = m { [k] = v1 },
map2 @ m = m { [k] = v2 } })
function test() =
let m = {} : map(string, string)
let m1 = m { ["foo"] = "value_of_foo",
["bla"] = "value_of_bla" }
let m2 = Map.delete("foo", m1)
let m3 = m2 { ["bla"] = "new_value_of_bla" }
[Map.lookup("foo", m), Map.lookup("bla", m),
Map.lookup("foo", m1), Map.lookup("bla", m1),
Map.lookup("foo", m2), Map.lookup("bla", m2),
Map.lookup("foo", m3), Map.lookup("bla", m3)]
function return_map() =
Map.delete("goo", {["foo"] = "bar", ["goo"] = "gaa"})
function argument_map(m : map(string, string)) =
m["foo"]
-20
View File
@@ -1,20 +0,0 @@
contract Remote1 =
function set : (int) => int
contract RemoteCall =
record state = { i : int }
function init(x) = { i = x }
function set( x : int) : int =
let old = state.i
put(state{ i = x })
old
function call(r : Remote1, x : int, g : int) : int =
r.set(gas = g, value = 10, x)
function get() = state.i
-23
View File
@@ -1,23 +0,0 @@
contract RemoteState =
record rstate = { i : int, s : string, m : map(int, int) }
function look_at(s : rstate) = ()
function return_s(big : bool) =
let x = "short"
let y = "______longer_string_at_least_32_bytes_long___________longer_string_at_least_32_bytes_long___________longer_string_at_least_32_bytes_long_____"
if(big) y else x
function return_m(big : bool) =
let x = { [1] = 2 }
let y = { [1] = 2, [3] = 4, [5] = 6 }
if(big) y else x
function get(s : rstate) = s
function get_i(s : rstate) = s.i
function get_s(s : rstate) = s.s
function get_m(s : rstate) = s.m
function fun_update_i(s : rstate, ni) = s{ i = ni }
function fun_update_s(s : rstate, ns) = s{ s = ns }
function fun_update_m(s : rstate, nm) = s{ m = nm }
function fun_update_mk(s : rstate, k, v) = s{ m = s.m{[k] = v} }
-22
View File
@@ -1,22 +0,0 @@
contract Remote =
function id : ('a) => 'a
function missing : ('a) => 'a
function wrong_type : (string) => string
contract Main =
function id(x : int) =
x
function wrong_type(x : int) =
x
function remote_id(r : Remote, x) =
r.id(x)
function remote_missing(r : Remote, x) =
r.missing(x)
function remote_wrong_type(r : Remote, x) =
r.wrong_type(x)
-21
View File
@@ -1,21 +0,0 @@
contract ValueOnErr =
function err : () => int
function ok : () => int
contract RemoteValueOnErr =
public function callErr(
r : ValueOnErr,
value : int) : int =
r.err(value = value)
public function callErrLimitGas(
r : ValueOnErr,
value : int,
gas : int) : int =
r.err(value = value, gas = gas)
public function callOk(
r : ValueOnErr,
value : int) : int =
r.ok(value = value)
+4 -12
View File
@@ -1,12 +1,4 @@
// This is a custom test file if you need to run a compiler without
// changing aeso_compiler_tests.erl
include "List.aes"
contract IntegerHolder =
type state = int
entrypoint init(x) = x
entrypoint get() = state
main contract Test =
stateful entrypoint f(c) = Chain.clone(ref=c, 123)
contract ShareTwo =
record state = {s1 : int, s2 : int}
entrypoint init() = {s1 = 0, s2 = 0}
stateful entrypoint buy() = ()
+1 -1
View File
@@ -1,4 +1,4 @@
// Builtins without named arguments can appear unapplied in both AEVM and FATE.
// Builtins without named arguments can appear unapplied.
// Named argument builtins are:
// Oracle.register
// Oracle.respond
-6
View File
@@ -1,6 +0,0 @@
contract UpfrontCharges =
record state = { b : int } // For enabling retrieval of sender balance observed inside init.
public function init() : state = { b = b() }
public function initialSenderBalance() : int = state.b
public function senderBalance() : int = b()
private function b() = Chain.balance(Call.origin)
-7
View File
@@ -1,7 +0,0 @@
contract ValueOnErr =
public function err() : int =
switch(0) 1 => 5
public function ok() : int =
11