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:
+53
-88
@@ -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).
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)}
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
contract Fail =
|
||||
|
||||
entrypoint tttt() : bool * int =
|
||||
let f(x : 'a) : 'a = x
|
||||
(f(true), f(1))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
contract MinimalInit =
|
||||
|
||||
record state = {foo : int}
|
||||
|
||||
function init() =
|
||||
{ foo = 0 }
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
contract MultiplicationServer =
|
||||
|
||||
function multiply(x : int, y : int) =
|
||||
switch(Call.value >= 100)
|
||||
true => x * y
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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} }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
@@ -1,7 +0,0 @@
|
||||
contract ValueOnErr =
|
||||
|
||||
public function err() : int =
|
||||
switch(0) 1 => 5
|
||||
|
||||
public function ok() : int =
|
||||
11
|
||||
Reference in New Issue
Block a user