Avoid hash collisions in calldata creation

This commit is contained in:
Thomas Arts 2019-06-12 16:35:19 +02:00
parent 3a6337d8ca
commit 95c41b8eee
3 changed files with 39 additions and 25 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)), #{args := CallArgs} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
{ok, FunName, CallArgs} {ok, FunName, CallArgs}
end end
catch catch
@ -233,17 +233,26 @@ check_call1(ContractString0, FunName, Args, Options) ->
fun (E) -> io_lib:format("~p", [E]) end)} fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
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

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