Merge pull request #87 from aeternity/PT-166636909-avoid-calldata-collision

Pt 166636909 avoid calldata collision
This commit is contained in:
Thomas Arts 2019-06-14 13:09:25 +02:00 committed by GitHub
commit e81439779c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 153 additions and 35 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
.rebar3 .rebar3
_* _[^_]*
.eunit .eunit
*.o *.o
*.beam *.beam

View File

@ -186,21 +186,21 @@ check_call1(ContractString0, FunName, Args, Options) ->
try try
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
%% First check the contract without the __call function %% First check the contract without the __call function
#{} = string_to_icode(ContractString0, Options), #{} = string_to_icode(ContractString0, Options),
ContractString = insert_call_function(ContractString0, FunName, Args, Options), ContractString = insert_call_function(ContractString0, ?CALL_NAME, FunName, Args, Options),
#{typed_ast := TypedAst, #{typed_ast := TypedAst,
icode := Icode} = string_to_icode(ContractString, Options), icode := Icode} = string_to_icode(ContractString, Options),
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
RetVMType = case RetType of RetVMType = case RetType of
{id, _, "_"} -> any; {id, _, "_"} -> any;
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode) _ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
end, end,
#{ functions := Funs } = Icode, #{ functions := Funs } = Icode,
ArgIcode = get_arg_icode(Funs), ArgIcode = get_arg_icode(Funs),
ArgTerms = [ icode_to_term(T, Arg) || ArgTerms = [ icode_to_term(T, Arg) ||
{T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ], {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
RetVMType1 = RetVMType1 =
case FunName of case FunName of
"init" -> {tuple, [typerep, RetVMType]}; "init" -> {tuple, [typerep, RetVMType]};
@ -211,13 +211,13 @@ check_call1(ContractString0, FunName, Args, Options) ->
%% First check the contract without the __call function %% First check the contract without the __call function
#{fcode := OrgFcode} = string_to_fcode(ContractString0, Options), #{fcode := OrgFcode} = string_to_fcode(ContractString0, Options),
FateCode = aeso_fcode_to_fate:compile(OrgFcode, []), FateCode = aeso_fcode_to_fate:compile(OrgFcode, []),
_SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), %% collect all hashes and compute the first name without hash collision to
%% TODO collect all hashes and compute the first name without hash collision to SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
%% be used as __callX CallName = first_none_match(?CALL_NAME, SymbolHashes,
%% case aeb_fate_code:symbol_identifier(<<"__call">>) of lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
ContractString = insert_call_function(ContractString0, FunName, Args, Options), ContractString = insert_call_function(ContractString0, CallName, FunName, Args, Options),
#{fcode := Fcode} = string_to_fcode(ContractString, Options), #{fcode := Fcode} = string_to_fcode(ContractString, Options),
#{args := CallArgs} = maps:get({entrypoint, <<"__call">>}, maps:get(functions, Fcode)), CallArgs = arguments_of_body(CallName, FunName, Fcode),
{ok, FunName, CallArgs} {ok, FunName, CallArgs}
end end
catch catch
@ -233,17 +233,32 @@ check_call1(ContractString0, FunName, Args, Options) ->
fun (E) -> io_lib:format("~p", [E]) end)} fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
arguments_of_body(CallName, _FunName, Fcode) ->
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
{def, _FName, Args} = Body,
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
first_none_match(_CallName, _Hashes, []) ->
error(unable_to_find_unique_call_name);
first_none_match(CallName, Hashes, [Char|Chars]) ->
case not lists:member(aeb_fate_code:symbol_identifier(list_to_binary(CallName)), Hashes) of
true ->
CallName;
false ->
first_none_match(?CALL_NAME++[Char], Hashes, Chars)
end.
%% Add the __call function to a contract. %% Add the __call function to a contract.
-spec insert_call_function(string(), string(), [string()], options()) -> string(). -spec insert_call_function(string(), string(), string(), [string()], options()) -> string().
insert_call_function(Code, FunName, Args, Options) -> insert_call_function(Code, Call, FunName, Args, Options) ->
Ast = parse(Code, Options), Ast = parse(Code, Options),
Ind = last_contract_indent(Ast), Ind = last_contract_indent(Ast),
lists:flatten( lists:flatten(
[ Code, [ Code,
"\n\n", "\n\n",
lists:duplicate(Ind, " "), lists:duplicate(Ind, " "),
"stateful function __call() = ", FunName, "(", string:join(Args, ","), ")\n" "stateful function ", Call,"() = ", FunName, "(", string:join(Args, ","), ")\n"
]). ]).
-spec insert_init_function(string(), options()) -> string(). -spec insert_init_function(string(), options()) -> string().

View File

@ -9,7 +9,7 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeso_fcode_to_fate). -module(aeso_fcode_to_fate).
-export([compile/2]). -export([compile/2, term_to_fate/1]).
%% -- Preamble --------------------------------------------------------------- %% -- Preamble ---------------------------------------------------------------
@ -231,16 +231,36 @@ lookup_var(#env{vars = Vars}, X) ->
%% -- The compiler -- %% -- The compiler --
to_scode(_Env, {lit, L}) -> lit_to_fate(L) ->
case L of case L of
{int, N} -> [push(?i(N))]; {int, N} -> aeb_fate_data:make_integer(N);
{string, S} -> [push(?i(aeb_fate_data:make_string(S)))]; {string, S} -> aeb_fate_data:make_string(S);
{bool, B} -> [push(?i(B))]; {bool, B} -> aeb_fate_data:make_boolean(B);
{account_pubkey, K} -> [push(?i(aeb_fate_data:make_address(K)))]; {account_pubkey, K} -> aeb_fate_data:make_address(K);
{contract_pubkey, K} -> [push(?i(aeb_fate_data:make_contract(K)))]; {contract_pubkey, K} -> aeb_fate_data:make_contract(K);
{oracle_pubkey, K} -> [push(?i(aeb_fate_data:make_oracle(K)))]; {oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
{oracle_query_id, _} -> ?TODO(fate_oracle_query_id_value) {oracle_query_id, _} -> ?TODO(fate_oracle_query_id_value)
end; end.
term_to_fate({lit, L}) ->
lit_to_fate(L);
%% negative literals are parsed as 0 - N
term_to_fate({op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
aeb_fate_data:make_integer(-N);
term_to_fate(nil) ->
aeb_fate_data:make_list([]);
term_to_fate({op, '::', [Hd, Tl]}) ->
%% The Tl will translate into a list, because FATE lists are just lists
[term_to_fate(Hd) | term_to_fate(Tl)];
term_to_fate({tuple, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(A) || A<-As]));
term_to_fate({con, Ar, I, As}) ->
FateAs = [ term_to_fate(A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)).
to_scode(_Env, {lit, L}) ->
[push(?i(lit_to_fate(L)))];
to_scode(_Env, nil) -> to_scode(_Env, nil) ->
[aeb_fate_ops:nil(?a)]; [aeb_fate_ops:nil(?a)];

View File

@ -0,0 +1,78 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc Test Sophia language compiler.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_calldata_tests).
-compile([export_all, nowarn_export_all]).
-include_lib("eunit/include/eunit.hrl").
%% Very simply test compile the given contracts. Only basic checks
%% are made on the output, just that it is a binary which indicates
%% that the compilation worked.
calldata_test_() ->
[ {"Testing the " ++ ContractName ++ " contract with the " ++ atom_to_list(Backend) ++ " backend",
fun() ->
ContractString = aeso_test_utils:read_contract(ContractName),
Res = aeso_compiler:create_calldata(ContractString, Fun, Args, [{backend, Backend}]),
case Backend of
aevm ->
?assertMatch({ok, _, _, _}, Res);
fate ->
?assertMatch({ok, _}, Res)
end
end} || {ContractName, Fun, Args} <- compilable_contracts(), Backend <- [aevm, fate],
not lists:member(ContractName, not_yet_compilable(Backend))].
check_errors(Expect, ErrorString) ->
%% This removes the final single \n as well.
Actual = binary:split(<<ErrorString/binary,$\n>>, <<"\n\n">>, [global,trim]),
case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
{Missing, Extra} -> ?assertEqual(Missing, Extra)
end.
%% compilable_contracts() -> [ContractName].
%% The currently compilable contracts.
compilable_contracts() ->
[
{"identity", "init", []},
{"maps", "init", []},
{"oracles", "init", []},
{"variant_types", "init", []},
{"basic_auth", "init", []},
{"address_literals", "init", []},
{"bytes_equality", "init", []},
{"address_chain", "init", []},
{"counter", "init",
["-3334353637383940202122232425262728293031323334353637"]},
{"dutch_auction", "init",
["ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt", "200000", "1000"]},
{"maps", "fromlist_i",
["[(1, {x = 1, y = 2}), (2, {x = 3, y = 4}), (3, {x = 4, y = 4})]"]},
{"strings", "str_concat", ["\"test\"","\"me\""]},
{"complex_types", "filter_some", ["[Some(1), Some(2), None]"]},
{"complex_types", "init", ["ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ"]},
{"__call" "init", []},
{"bitcoin_auth", "init",
["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132"
"33343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]}
].
not_yet_compilable(fate) ->
["oracles", %% Oracle.register
"events",
"basic_auth", %% auth_tx_hash instruction
"bitcoin_auth", %% fate_auth_tx_hash_instruction
"address_literals", %% oracle_query_id literals
"address_chain" %% Oracle.check_query
];
not_yet_compilable(aevm) ->
["__call"].

View File

@ -0,0 +1,5 @@
contract Identity =
function main (x:int) = x
function __call() = 12