Skip to content

Commit

Permalink
[Ceres]: Extend FATE with arbitrary size byte arrays (#4142)
Browse files Browse the repository at this point in the history
* extend check_type...

* fate: implement new bytes functions

* fix: compiler warnings

* Add tests for bytes_any

* Bump aesophia to current ceres branch

* Add ceres release note
  • Loading branch information
hanssv committed Jul 7, 2023
1 parent 4843612 commit c7c5810
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 43 deletions.
75 changes: 71 additions & 4 deletions apps/aecontract/test/aecontract_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
, sophia_namespaces/1
, sophia_too_little_gas_for_mem/1
, sophia_bytes/1
, sophia_bytes_any/1
, sophia_bytes_remote/1
, sophia_bytes_to_x/1
, sophia_bytes_concat/1
Expand Down Expand Up @@ -471,6 +472,7 @@ groups() ->
sophia_heap_to_heap_bug,
sophia_namespaces,
sophia_bytes,
sophia_bytes_any,
sophia_bytes_remote,
sophia_bytes_to_x,
sophia_bytes_concat,
Expand Down Expand Up @@ -4372,7 +4374,7 @@ sophia_all_signatures_aens(Cfg) ->
_ -> sophia_all_signatures_aens_(Cfg)
end.

sophia_all_signatures_aens_(Cfg) ->
sophia_all_signatures_aens_(_Cfg) ->
init_new_state(),
Acc = ?call(new_account, 40000000000000 * aec_test_utils:min_gas_price()),
NameAcc = ?call(new_account, 40000000000000 * aec_test_utils:min_gas_price()),
Expand All @@ -4381,12 +4383,9 @@ sophia_all_signatures_aens_(Cfg) ->
Salt1 = rand:uniform(10000),
{ok, NameAscii} = aens_utils:to_ascii(Name1),
CHash = ?hsh(aens_hash:commitment_hash(NameAscii, Salt1)),
NHash = aens_hash:name_hash(NameAscii),
NameArg = Name1,
NameAccSigAll = sign(<<NameAcc/binary, "AENS"/utf8, Ct/binary>>, NameAcc),
AccSigAll = sign(<<Acc/binary, "AENS"/utf8, Ct/binary>>, Acc),
APubkey = 1,
OPubkey = 2,

%% PreClaim
Res1 = ?call(call_contract, Acc, Ct, signedPreclaim, {tuple, []}, {NameAcc, CHash, NameAccSigAll}, #{ height => 10 }),
Expand Down Expand Up @@ -6054,6 +6053,74 @@ sophia_bytes_remote(_Cfg) ->

ok.

split_any({bytes, B}, N) when byte_size(B) < abs(N) ->
none;
split_any({bytes, B}, N) ->
N1 = if N > 0 -> N; true -> byte_size(B) - abs(N) end,
<<B1:N1/binary, B2/binary>> = B,
{some, {{bytes, B1}, {bytes, B2}}}.

concat({bytes, B1}, {bytes, B2}) -> {bytes, <<B1/binary, B2/binary>>}.

sophia_bytes_any(_Cfg) ->
?skipRest(vm_version() < ?VM_FATE_SOPHIA_3, bytes_any_from_ceres_only),
init_new_state(),
Acc = ?call(new_account, 1000000000 * aec_test_utils:min_gas_price()),
C = ?call(create_contract, Acc, bytes_any, {}),
CR = ?call(create_contract, Acc, bytes_any, {}),

B = fun(B) -> {bytes, B} end,

Bs = B(<<"0123456789">>),
SpRes1 = ?call(call_contract, Acc, C, split_any, {option, {tuple, [bytes, bytes]}}, {Bs, 4}),
?assertEqual(split_any(Bs, 4), SpRes1),

SpRes2 = ?call(call_contract, Acc, C, split_any, {option, {tuple, [bytes, bytes]}}, {Bs, -6}),
?assertEqual(split_any(Bs, -6), SpRes2),

SpRes3 = ?call(call_contract, Acc, C, split_any, {option, {tuple, [bytes, bytes]}}, {Bs, 42}),
?assertEqual(split_any(Bs, 42), SpRes3),

SpRes4 = ?call(call_contract, Acc, C, r_split_any, {option, {tuple, [bytes, bytes]}}, {?cid(CR), Bs, 4}),
?assertEqual(split_any(Bs, 4), SpRes4),

CcRes1 = ?call(call_contract, Acc, C, concat, bytes, {Bs, Bs}),
?assertEqual(concat(Bs, Bs), CcRes1),

CcRes2 = ?call(call_contract, Acc, C, r_concat, bytes, {?cid(CR), Bs, Bs}),
?assertEqual(concat(Bs, Bs), CcRes2),

ToRes1 = ?call(call_contract, Acc, C, to_fixed, {option, bytes}, {B(<<"1234">>)}),
?assertEqual({some, B(<<"1234">>)}, ToRes1),

ToRes2 = ?call(call_contract, Acc, C, to_fixed, {option, bytes}, {Bs}),
?assertEqual(none, ToRes2),

ToRes3 = ?call(call_contract, Acc, C, to_any, bytes, {B(<<"1234">>)}),
?assertEqual(B(<<"1234">>), ToRes3),

ToRes4 = ?call(call_contract, Acc, C, to_any, bytes, {Bs}),
?assertEqual({error, <<"Type error on call: [{bytes,<<\"0123456789\">>}] is not of type [{bytes,4}]">>}, ToRes4),

IBRes1 = ?call(call_contract, Acc, C, int_to_bytes, bytes, {42, 4}),
?assertEqual(B(<<0, 0, 0, 42>>), IBRes1),

IBRes2 = ?call(call_contract, Acc, C, int_to_bytes, bytes, {1024, 1}),
?assertEqual(B(<<0>>), IBRes2),

SBRes1 = ?call(call_contract, Acc, C, string_to_bytes, bytes, {<<"abcdef">>}),
?assertEqual(B(<<"abcdef">>), SBRes1),

SRes1 = ?call(call_contract, Acc, C, size, word, {B(<<"123456">>)}),
?assertEqual(6, SRes1),

RtRes1 = ?call(call_contract, Acc, C, r_test1, {list, bytes}, {?cid(CR)}),
?assertEqual({error,<<"Type of remote function does not match expected type">>}, RtRes1),

RtRes2 = ?call(call_contract, Acc, C, r_test2, {list, bytes}, {?cid(CR)}),
?assertEqual({bytes, <<17,34,51,68>>}, RtRes2),

ok.

sophia_bytes_to_x(_Cfg) ->
?skipRest(sophia_version() =< ?SOPHIA_MINERVA, bytes_to_x_not_in_minerva),
Expand Down
51 changes: 31 additions & 20 deletions apps/aefate/src/aefa_fate.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
, ensure_contract_store/2
, unfold_store_maps/3
, unfold_store_maps_in_args/2
, check_type/2
, check_type/3
, get_function_signature/2
, push_gas_cap/2
, push_continuation/2
Expand Down Expand Up @@ -71,6 +71,7 @@

-include_lib("aebytecode/include/aeb_fate_data.hrl").
-include_lib("aecontract/include/hard_forks.hrl").
-include_lib("aecontract/include/aecontract.hrl").

-ifdef(TEST).
-define(trace(I,S), aefa_engine_state:add_trace(I, S)).
Expand Down Expand Up @@ -484,7 +485,8 @@ check_return_type(ES) ->
check_return_type(RetType, TVars, ES) ->
Acc = aefa_engine_state:accumulator(ES),
ES1 = aefa_engine_state:spend_gas_for_traversal(Acc, simple, ES),
case check_type(RetType, Acc) of
VMVersion = aefa_engine_state:vm_version(ES),
case check_type(RetType, Acc, VMVersion) of
false -> abort({bad_return_type, Acc, RetType}, ES1);
Inst ->
case merge_match(Inst, TVars) of
Expand All @@ -496,7 +498,8 @@ check_return_type(RetType, TVars, ES) ->
check_signature(Args, {ArgTypes, _RetSignature}, ES) when length(ArgTypes) /= length(Args) ->
abort({function_arity_mismatch, length(Args), length(ArgTypes)}, ES);
check_signature(Args, {ArgTypes, _RetSignature}, ES) ->
case check_arg_types(ArgTypes, Args) of
VMVersion = aefa_engine_state:vm_version(ES),
case check_arg_types(ArgTypes, Args, VMVersion) of
{ok, Inst} ->
aefa_engine_state:set_current_tvars(Inst, ES);
{error, T, V} ->
Expand All @@ -521,9 +524,9 @@ pop_args(N, ES) ->
bind_args(Args, ES) ->
bind_args(0, Args, #{}, ES).

check_arg_types(Ts, As0) ->
check_arg_types(Ts, As0, VMVersion) ->
As = lists:sublist(As0, length(Ts)),
case check_type({tuple, Ts}, {tuple, list_to_tuple(As)}) of
case check_type({tuple, Ts}, {tuple, list_to_tuple(As)}, VMVersion) of
false -> {error, Ts, As};
Inst -> {ok, Inst}
end.
Expand All @@ -533,9 +536,10 @@ bind_args(_, [], Mem, EngineState) ->
bind_args(N, [Arg|Args], Mem, EngineState) ->
bind_args(N + 1, Args, Mem#{{arg, N} => {val, Arg}}, EngineState).

check_type(T, V) ->
check_type(T, V, VMVersion) ->
io:format("Check type: ~100p =?= ~140p\n", [T, V]),
try
match_type(T, infer_type(V))
match_type(T, infer_type(V), VMVersion)
catch throw:_Err ->
false
end.
Expand Down Expand Up @@ -600,22 +604,28 @@ intersect_types({variant, Ss}, {variant, Ts}) when length(Ss) == length(Ts) ->
{variant, lists:zipwith(Isect, Ss, Ts)};
intersect_types(S, T) -> throw({not_compatible, S, T}).

match_type(T, T) -> yes_match();
match_type(any, _) -> yes_match();
match_type(_, any) -> yes_match();
match_type({tvar, X}, T) -> #{ X => T };
match_type({list, T}, {list, S}) -> match_type(T, S);
match_type({tuple, Ts}, {tuple, Ss}) when length(Ts) == length(Ss) ->
merge_match(lists:zipwith(fun match_type/2, Ts, Ss));
match_type({map, TK, TV}, {map, SK, SV}) ->
merge_match(match_type(TK, SK), match_type(TV, SV));
match_type({variant, Ts}, {variant, Ss}) when length(Ts) == length(Ss) ->
match_type(T1, T2, V) ->
io:format("Match type: ~100p =?= ~140p\n", [T1, T2]),
match_type_(T1, T2, V).

match_type_(T, T, _) -> yes_match();
match_type_(any, _, _) -> yes_match();
match_type_(_, any, _) -> yes_match();
match_type_({bytes, any}, {bytes, _}, V) when V >= ?VM_FATE_SOPHIA_3-> yes_match();
match_type_({bytes, any}, _, _) -> no_match();
match_type_({tvar, X}, T, _) -> #{ X => T };
match_type_({list, T}, {list, S}, V) -> match_type_(T, S, V);
match_type_({tuple, Ts}, {tuple, Ss}, V) when length(Ts) == length(Ss) ->
merge_match(lists:zipwith(fun(T, S) -> match_type_(T, S, V) end, Ts, Ss));
match_type_({map, TK, TV}, {map, SK, SV}, V) ->
merge_match(match_type_(TK, SK, V), match_type_(TV, SV, V));
match_type_({variant, Ts}, {variant, Ss}, V) when length(Ts) == length(Ss) ->
%% Second argument can be partial variant type
Match = fun({tuple, Us}, N) when length(Us) == N -> yes_match();
(T = {tuple, _}, S = {tuple, _}) -> match_type(T, S);
(T = {tuple, _}, S = {tuple, _}) -> match_type_(T, S, V);
(_, _) -> no_match() end,
merge_match(lists:zipwith(Match, Ts, Ss));
match_type(_, _) -> no_match().
match_type_(_, _, _) -> no_match().

yes_match() -> #{}.
no_match() -> false.
Expand Down Expand Up @@ -680,7 +690,8 @@ push_return_type_check({CalleeArgs, CalleeRet}, {CallerArgs, CallerRet}, CalleeT
%% unify CalleeSig and CallerSig to get the instantiation for the Caller
%% tvars.
InstCalleeSig = instantiate_type(CalleeTVars, CalleeSig),
case match_type(CallerSig, InstCalleeSig) of
VMVersion = aefa_engine_state:vm_version(ES),
case match_type(CallerSig, InstCalleeSig, VMVersion) of
false -> abort(remote_type_mismatch, ES);
CallerTVars -> aefa_engine_state:push_return_type_check(CallerRet, CallerTVars, Protected, ES)
end.
Expand Down

0 comments on commit c7c5810

Please sign in to comment.