From a3efaf71a75b351647bf84a102a444c9f07fb936 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 11:05:29 +0200 Subject: [PATCH 1/6] Compile oracle check functions in FATE backend --- src/aeso_ast_to_fcode.erl | 34 +++++++++++++++++++++++----------- src/aeso_fcode_to_fate.erl | 20 ++++++++++++++++++++ test/aeso_compiler_tests.erl | 5 +---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 3eedc24..ecb00a5 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -175,11 +175,12 @@ builtins() -> {["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none}, {"timestamp", none}, {"block_height", none}, {"difficulty", none}, {"gas_limit", none}]}, - {["Contract"], [{"address", none}, {"balance", none}]}, + {["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]}, {["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none}, {"gas_left", 0}]}, {["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2}, - {"respond", 4}, {"extend", 3}, {"get_answer", 2}]}, + {"respond", 4}, {"extend", 3}, {"get_answer", 2}, + {"check", 1}, {"check_query", 2}]}, {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 4}, {"transfer", 4}, {"revoke", 3}]}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, @@ -192,7 +193,7 @@ builtins() -> {"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]}, {["Bytes"], [{"to_int", 1}, {"to_str", 1}]}, {["Int"], [{"to_str", 1}]}, - {["Address"], [{"to_str", 1}]} + {["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 1}]} ], maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}} || {NS, Funs} <- Scopes, @@ -475,7 +476,9 @@ expr_to_fcode(Env, Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT, B =:= oracle_get_question; B =:= oracle_get_answer; B =:= oracle_respond; - B =:= oracle_register -> + B =:= oracle_register; + B =:= oracle_check; + B =:= oracle_check_query -> %% Get the type of the oracle from the args or the expression itself OType = get_oracle_type(B, Type, Args1), {oracle, QType, RType} = type_to_fcode(Env, OType), @@ -547,10 +550,12 @@ make_if(Cond, Then, Else) -> get_oracle_type(oracle_register, OType, _Args) -> OType; -get_oracle_type(oracle_query, _Type, [{typed, _,_Expr, OType}|_]) -> OType; -get_oracle_type(oracle_get_question, _Type, [{typed, _,_Expr, OType}|_]) -> OType; -get_oracle_type(oracle_get_answer, _Type, [{typed, _,_Expr, OType}|_]) -> OType; -get_oracle_type(oracle_respond, _Type, [_,{typed, _,_Expr, OType}|_]) -> OType. +get_oracle_type(oracle_query, _Type, [{typed, _, _Expr, OType} | _]) -> OType; +get_oracle_type(oracle_get_question, _Type, [{typed, _, _Expr, OType} | _]) -> OType; +get_oracle_type(oracle_get_answer, _Type, [{typed, _, _Expr, OType} | _]) -> OType; +get_oracle_type(oracle_check, _Type, [{typed, _, _Expr, OType}]) -> OType; +get_oracle_type(oracle_check_query, _Type, [{typed, _, _Expr, OType} | _]) -> OType; +get_oracle_type(oracle_respond, _Type, [_, {typed, _,_Expr, OType} | _]) -> OType. %% -- Pattern matching -- @@ -1261,6 +1266,8 @@ pp_punctuate(Sep, [X | Xs]) -> [pp_beside(X, Sep) | pp_punctuate(Sep, Xs)]. pp_par([]) -> prettypr:empty(); pp_par(Xs) -> prettypr:par(Xs). +pp_fexpr({lit, {typerep, T}}) -> + pp_ftype(T); pp_fexpr({lit, {Tag, Lit}}) -> aeso_pretty:expr({Tag, [], Lit}); pp_fexpr(nil) -> @@ -1321,18 +1328,23 @@ pp_fexpr({switch, Split}) -> pp_split(Split). pp_call(Fun, Args) -> pp_beside(Fun, pp_fexpr({tuple, Args})). +pp_call_t(Fun, Args) -> + pp_beside(pp_text(Fun), pp_ftype({tuple, Args})). + +-spec pp_ftype(ftype()) -> prettypr:doc(). pp_ftype(T) when is_atom(T) -> pp_text(T); pp_ftype(any) -> pp_text("_"); pp_ftype({tvar, X}) -> pp_text(X); -pp_ftype({bytes, N}) -> pp_text("bytes(" ++ integer_to_list(N) ++ ")"); +pp_ftype({bytes, N}) -> pp_call(pp_text("bytes"), [{lit, {int, N}}]); +pp_ftype({oracle, Q, R}) -> pp_call_t("oracle", [Q, R]); pp_ftype({tuple, Ts}) -> pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts]))); pp_ftype({list, T}) -> - pp_beside([pp_text("list("), pp_ftype(T), pp_text(")")]); + pp_call_t("list", [T]); pp_ftype({function, Args, Res}) -> pp_par([pp_ftype({tuple, Args}), pp_text("=>"), pp_ftype(Res)]); pp_ftype({map, Key, Val}) -> - pp_beside([pp_text("map"), pp_ftype({tuple, [Key, Val]})]); + pp_call_t("map", [Key, Val]); pp_ftype({variant, Cons}) -> pp_par( pp_punctuate(pp_text(" |"), diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index c79459e..a86ec74 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -104,6 +104,11 @@ Op =:= 'AUTH_TX_HASH' orelse Op =:= 'BYTES_TO_INT' orelse Op =:= 'BYTES_TO_STR' orelse + Op =:= 'ORACLE_CHECK' orelse + Op =:= 'ORACLE_CHECK_QUERY' orelse + Op =:= 'IS_ORACLE' orelse + Op =:= 'IS_CONTRACT' orelse + Op =:= 'CREATOR' orelse false)). -record(env, { contract, vars = [], locals = [], tailpos = true }). @@ -499,6 +504,8 @@ builtin_to_scode(_Env, contract_balance, []) -> [aeb_fate_ops:balance(?a)]; builtin_to_scode(_Env, contract_address, []) -> [aeb_fate_ops:address(?a)]; +builtin_to_scode(_Env, contract_creator, []) -> + [aeb_fate_ops:contract_creator(?a)]; builtin_to_scode(_Env, call_origin, []) -> [aeb_fate_ops:origin(?a)]; builtin_to_scode(_Env, call_caller, []) -> @@ -525,6 +532,14 @@ builtin_to_scode(Env, oracle_extend, [_Sign, _Oracle, _TTL] = Args) -> tuple(0)], Args); builtin_to_scode(Env, oracle_get_answer, [_Oracle, _QueryId, _QType, _RType] = Args) -> call_to_scode(Env, aeb_fate_ops:oracle_get_answer(?a, ?a, ?a, ?a, ?a), Args); +builtin_to_scode(Env, oracle_check, [_Oracle, _QType, _RType] = Args) -> + call_to_scode(Env, aeb_fate_ops:oracle_check(?a, ?a, ?a, ?a), Args); +builtin_to_scode(Env, oracle_check_query, [_Oracle, _Query, _QType, _RType] = Args) -> + call_to_scode(Env, aeb_fate_ops:oracle_check_query(?a, ?a, ?a, ?a, ?a), Args); +builtin_to_scode(Env, address_is_oracle, [_] = Args) -> + call_to_scode(Env, aeb_fate_ops:is_oracle(?a, ?a), Args); +builtin_to_scode(Env, address_is_contract, [_] = Args) -> + call_to_scode(Env, aeb_fate_ops:is_contract(?a, ?a), Args); builtin_to_scode(_Env, aens_resolve, [_, _] = _Args) -> ?TODO(fate_aens_resolve_instruction); builtin_to_scode(_Env, aens_preclaim, [_, _, _] = _Args) -> @@ -810,6 +825,11 @@ attributes(I) -> {'AUTH_TX_HASH', A} -> Pure(A, []); {'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]); + {'ORACLE_CHECK', A, B, C, D} -> Impure(A, [B, C, D]); + {'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Impure(A, [B, C, D, E]); + {'IS_ORACLE', A, B} -> Impure(A, [B]); + {'IS_CONTRACT', A, B} -> Impure(A, [B]); + {'CREATOR', A} -> Pure(A, []); {'ADDRESS', A} -> Pure(A, []); {'BALANCE', A} -> Impure(A, []); {'BALANCE_OTHER', A, B} -> Impure(A, [B]); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f2095df..f8b7d2f 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -120,10 +120,7 @@ compilable_contracts() -> ]. not_yet_compilable(fate) -> - ["events", %% events - "address_literals", %% oracle_query_id literals - "address_chain" %% Oracle.check_query - ]; + ["events"]; not_yet_compilable(aevm) -> []. %% Contracts that should produce type errors From 0cf6a52b268ece57db36c014e9f6087ba5819739 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 14:54:01 +0200 Subject: [PATCH 2/6] Compile events to FATE --- src/aeso_ast_infer_types.erl | 2 ++ src/aeso_ast_to_fcode.erl | 66 ++++++++++++++++++++++++++++-------- src/aeso_fcode_to_fate.erl | 16 +++++---- src/aeso_pretty.erl | 1 + 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 3d9a760..8df2359 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -772,11 +772,13 @@ is_word_type({id, _, Name}) -> lists:member(Name, ["int", "address", "hash", "bits", "bool"]); is_word_type({app_t, _, {id, _, Name}, [_, _]}) -> lists:member(Name, ["oracle", "oracle_query"]); +is_word_type({bytes_t, _, [N]}) -> N =< 32; is_word_type({con, _, _}) -> true; is_word_type({qcon, _, _}) -> true; is_word_type(_) -> false. is_string_type({id, _, "string"}) -> true; +is_string_type({bytes_t, _, _}) -> true; is_string_type(_) -> false. -spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return(). diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index ecb00a5..1d2262f 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -16,11 +16,11 @@ -type option() :: term(). --type attribute() :: stateful | pure. +-type attribute() :: stateful | pure | private. -type fun_name() :: {entrypoint, binary()} | {local_fun, [string()]} - | init. + | init | event. -type var_name() :: string(). -type sophia_name() :: [string()]. @@ -127,14 +127,15 @@ | {namespace, string()} | {abstract_contract, string()}. --type env() :: #{ type_env := type_env(), - fun_env := fun_env(), - con_env := con_env(), - builtins := builtins(), - options := [option()], - context => context(), - vars => [var_name()], - functions := #{ fun_name() => fun_def() } }. +-type env() :: #{ type_env := type_env(), + fun_env := fun_env(), + con_env := con_env(), + event_type := aeso_syntax:typedef(), + builtins := builtins(), + options := [option()], + context => context(), + vars => [var_name()], + functions := #{ fun_name() => fun_def() } }. %% -- Entrypoint ------------------------------------------------------------- @@ -229,7 +230,7 @@ to_fcode(Env, [{contract, _, {con, _, Main}, Decls}]) -> MainEnv = Env#{ context => {main_contract, Main}, builtins => Builtins#{[Main, "state"] => {get_state, none}, [Main, "put"] => {set_state, 1}, - [Main, "Chain", "event"] => {event, 1}} }, + [Main, "Chain", "event"] => {chain_event, 1}} }, #{ functions := Funs } = Env1 = decls_to_fcode(MainEnv, Decls), StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}), @@ -237,7 +238,7 @@ to_fcode(Env, [{contract, _, {con, _, Main}, Decls}]) -> #{ contract_name => Main, state_type => StateType, event_type => EventType, - functions => Funs }; + functions => add_event_function(Env1, EventType, Funs) }; to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) -> Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls), to_fcode(Env1, Code); @@ -303,7 +304,11 @@ typedef_to_fcode(Env, {id, _, Name}, Xs, Def) -> _ -> #{} end, Env1 = bind_constructors(Env, Constructors), - bind_type(Env1, Q, FDef). + Env2 = case Name of + "event" -> Env1#{ event_type => Def }; + _ -> Env1 + end, + bind_type(Env2, Q, FDef). -spec type_to_fcode(env(), aeso_syntax:type()) -> ftype(). type_to_fcode(Env, Type) -> @@ -788,6 +793,8 @@ op_builtins() -> builtin_to_fcode(require, [Cond, Msg]) -> make_if(Cond, {tuple, []}, {builtin, abort, [Msg]}); +builtin_to_fcode(chain_event, [Event]) -> + {def, event, [Event]}; builtin_to_fcode(map_delete, [Key, Map]) -> {op, map_delete, [Map, Key]}; builtin_to_fcode(map_member, [Key, Map]) -> @@ -806,6 +813,36 @@ builtin_to_fcode(Builtin, Args) -> false -> {builtin, Builtin, Args} end. +%% -- Event function -- + +add_event_function(_Env, none, Funs) -> Funs; +add_event_function(Env, EventFType, Funs) -> + Funs#{ event => event_function(Env, EventFType) }. + +event_function(_Env = #{event_type := {variant_t, EventCons}}, EventType = {variant, FCons}) -> + Cons = [ {Name, I - 1, proplists:get_value(indices, Ann)} + || {I, {constr_t, Ann, {con, _, Name}, _}} <- indexed(EventCons) ], + Arities = [length(Ts) || Ts <- FCons], + io:format("Cons = ~p\nEventFType = ~p\n", [Cons, EventType]), + Case = fun({Name, Tag, Ixs}) -> + %% TODO: precompute (needs dependency) + Hash = {op, crypto_sha3, [{lit, {string, list_to_binary(Name)}}]}, + Vars = [ "arg" ++ integer_to_list(I) || I <- lists:seq(1, length(Ixs)) ], + IVars = lists:zip(Ixs, Vars), + Payload = + case [ V || {notindexed, V} <- IVars ] of + [] -> {lit, {string, <<>>}}; + [V] -> {var, V} + end, + Indices = [ {var, V} || {indexed, V} <- IVars ], + Body = {builtin, chain_event, [Payload, Hash | Indices]}, + {'case', {con, Arities, Tag, Vars}, {nosplit, Body}} + end, + #{ attrs => [stateful, private], + args => [{"e", EventType}], + return => {tuple, []}, + body => {switch, {split, EventType, "e", lists:map(Case, Cons)}} }. + %% -- Lambda lifting --------------------------------------------------------- %% The expr_to_fcode compiler lambda expressions to {lam, Xs, Body}, but in %% FATE we can only call top-level functions, so we need to lift the lambda to @@ -1237,7 +1274,8 @@ pp_fun(Name, #{ args := Args, return := Return, body := Body }) -> pp_text(" : "), pp_ftype(Return), pp_text(" =")]), prettypr:nest(2, pp_fexpr(Body))). -pp_fun_name(init) -> pp_text("init"); +pp_fun_name(init) -> pp_text(init); +pp_fun_name(event) -> pp_text(event); pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E)); pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")). diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index a86ec74..f6c776f 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -140,6 +140,7 @@ make_function_id(X) -> aeb_fate_code:symbol_identifier(make_function_name(X)). make_function_name(init) -> <<"init">>; +make_function_name(event) -> <<"Chain.event">>; make_function_name({entrypoint, Name}) -> Name; make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). @@ -469,8 +470,9 @@ builtin_to_scode(_Env, get_state, []) -> builtin_to_scode(Env, set_state, [_] = Args) -> call_to_scode(Env, [aeb_fate_ops:store(?s, ?a), tuple(0)], Args); -builtin_to_scode(_Env, event, [_] = _Args) -> - ?TODO(fate_event_instruction); +builtin_to_scode(Env, chain_event, Args) -> + call_to_scode(Env, [erlang:apply(aeb_fate_ops, log, lists:duplicate(length(Args), ?a)), + tuple(0)], Args); builtin_to_scode(_Env, map_empty, []) -> [aeb_fate_ops:map_empty(?a)]; builtin_to_scode(_Env, bits_none, []) -> @@ -844,11 +846,11 @@ attributes(I) -> {'DIFFICULTY', A} -> Pure(A, []); {'GASLIMIT', A} -> Pure(A, []); {'GAS', A} -> Impure(?a, A); - {'LOG0', A, B} -> Impure(none, [A, B]); - {'LOG1', A, B, C} -> Impure(none, [A, B, C]); - {'LOG2', A, B, C, D} -> Impure(none, [A, B, C, D]); - {'LOG3', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); - {'LOG4', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); + {'LOG0', A} -> Impure(none, [A]); + {'LOG1', A, B} -> Impure(none, [A, B]); + {'LOG2', A, B, C} -> Impure(none, [A, B, C]); + {'LOG3', A, B, C, D} -> Impure(none, [A, B, C, D]); + {'LOG4', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); 'DEACTIVATE' -> Impure(none, []); {'SPEND', A, B} -> Impure(none, [A, B]); diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 8762cf7..5e6314f 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -330,6 +330,7 @@ expr_p(_, {Type, _, Bin}) Type == oracle_pubkey; Type == oracle_query_id -> text(binary_to_list(aeser_api_encoder:encode(Type, Bin))); +expr_p(_, {string, _, <<>>}) -> text("\"\""); expr_p(_, {string, _, S}) -> term(binary_to_list(S)); expr_p(_, {char, _, C}) -> case C of From 961f5572154418dd8d9dc92d0a41dcd8447da3c3 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 15:01:42 +0200 Subject: [PATCH 3/6] Events now compile to FATE --- src/aeso_ast_to_fcode.erl | 7 +++---- test/aeso_compiler_tests.erl | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 1d2262f..b545f5b 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -130,7 +130,7 @@ -type env() :: #{ type_env := type_env(), fun_env := fun_env(), con_env := con_env(), - event_type := aeso_syntax:typedef(), + event_type => aeso_syntax:typedef(), builtins := builtins(), options := [option()], context => context(), @@ -823,7 +823,6 @@ event_function(_Env = #{event_type := {variant_t, EventCons}}, EventType = {vari Cons = [ {Name, I - 1, proplists:get_value(indices, Ann)} || {I, {constr_t, Ann, {con, _, Name}, _}} <- indexed(EventCons) ], Arities = [length(Ts) || Ts <- FCons], - io:format("Cons = ~p\nEventFType = ~p\n", [Cons, EventType]), Case = fun({Name, Tag, Ixs}) -> %% TODO: precompute (needs dependency) Hash = {op, crypto_sha3, [{lit, {string, list_to_binary(Name)}}]}, @@ -838,7 +837,7 @@ event_function(_Env = #{event_type := {variant_t, EventCons}}, EventType = {vari Body = {builtin, chain_event, [Payload, Hash | Indices]}, {'case', {con, Arities, Tag, Vars}, {nosplit, Body}} end, - #{ attrs => [stateful, private], + #{ attrs => [private], args => [{"e", EventType}], return => {tuple, []}, body => {switch, {split, EventType, "e", lists:map(Case, Cons)}} }. @@ -1369,7 +1368,7 @@ pp_call(Fun, Args) -> pp_call_t(Fun, Args) -> pp_beside(pp_text(Fun), pp_ftype({tuple, Args})). --spec pp_ftype(ftype()) -> prettypr:doc(). +-spec pp_ftype(ftype()) -> any(). pp_ftype(T) when is_atom(T) -> pp_text(T); pp_ftype(any) -> pp_text("_"); pp_ftype({tvar, X}) -> pp_text(X); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f8b7d2f..2dbb9fd 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -119,8 +119,7 @@ compilable_contracts() -> "bytes_to_x" ]. -not_yet_compilable(fate) -> - ["events"]; +not_yet_compilable(fate) -> []; not_yet_compilable(aevm) -> []. %% Contracts that should produce type errors From 523d6b03a9f4779f4c15ff153a2563c1d0a3b068 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 16:26:50 +0200 Subject: [PATCH 4/6] Allow bytes(N) as indices if N =< 32 and payload if N > 32 --- src/aeso_ast_infer_types.erl | 4 ++-- src/aeso_builtins.erl | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 8df2359..b1caf16 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -772,13 +772,13 @@ is_word_type({id, _, Name}) -> lists:member(Name, ["int", "address", "hash", "bits", "bool"]); is_word_type({app_t, _, {id, _, Name}, [_, _]}) -> lists:member(Name, ["oracle", "oracle_query"]); -is_word_type({bytes_t, _, [N]}) -> N =< 32; +is_word_type({bytes_t, _, N}) -> N =< 32; is_word_type({con, _, _}) -> true; is_word_type({qcon, _, _}) -> true; is_word_type(_) -> false. is_string_type({id, _, "string"}) -> true; -is_string_type({bytes_t, _, _}) -> true; +is_string_type({bytes_t, _, N}) -> N > 32; is_string_type(_) -> false. -spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return(). diff --git a/src/aeso_builtins.erl b/src/aeso_builtins.erl index 4736e2f..daf638f 100644 --- a/src/aeso_builtins.erl +++ b/src/aeso_builtins.erl @@ -119,11 +119,12 @@ check_event_type(EvtName, Ix, Type, Icode) -> catch _:_ -> error({EvtName, could_not_resolve_type, Type}) end, - case {Ix, VMType} of - {indexed, word} -> ok; - {notindexed, string} -> ok; - {indexed, _} -> error({EvtName, indexed_field_should_be_word, is, VMType}); - {notindexed, _} -> error({EvtName, payload_should_be_string, is, VMType}) + case {Ix, VMType, Type} of + {indexed, word, _} -> ok; + {notindexed, string, _} -> ok; + {notindexed, _, {bytes_t, _, N}} when N > 32 -> ok; + {indexed, _, _} -> error({EvtName, indexed_field_should_be_word, is, VMType}); + {notindexed, _, _} -> error({EvtName, payload_should_be_string, is, VMType}) end. bfun(B, {IArgs, IExpr, IRet}) -> @@ -177,16 +178,22 @@ builtin_event(EventT) -> VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end, ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end, Payload = %% Should put data ptr, length on stack. - fun([]) -> {inline_asm, [A(?PUSH1), 0, A(?PUSH1), 0]}; - ([V]) -> {seq, [V, {inline_asm, [A(?DUP1), A(?MLOAD), %% length, ptr - A(?SWAP1), A(?PUSH1), 32, A(?ADD)]}]} %% ptr+32, length + fun([]) -> {inline_asm, [A(?PUSH1), 0, A(?PUSH1), 0]}; + ([{{id, _, "string"}, V}]) -> + {seq, [V, {inline_asm, [A(?DUP1), A(?MLOAD), %% length, ptr + A(?SWAP1), A(?PUSH1), 32, A(?ADD)]}]}; %% ptr+32, length + ([{{bytes_t, _, N}, V}]) -> {seq, [V, {integer, N}, {inline_asm, A(?SWAP1)}]} end, + Ix = + fun({bytes_t, _, N}, V) when N < 32 -> ?BSR(V, 32 - N); + (_, V) -> V end, Clause = fun(_Tag, {con, _, Con}, IxTypes) -> Types = [ T || {_Ix, T} <- IxTypes ], - Indexed = [ Var || {Var, {indexed, _Type}} <- lists:zip(ArgPats(Types), IxTypes) ], + Indexed = [ Ix(Type, Var) || {Var, {indexed, Type}} <- lists:zip(ArgPats(Types), IxTypes) ], + Data = [ {Type, Var} || {Var, {notindexed, Type}} <- lists:zip(ArgPats(Types), IxTypes) ], EvtIndex = {unop, 'sha3', str_to_icode(Con)}, - {event, lists:reverse(Indexed) ++ [EvtIndex], Payload(ArgPats(Types) -- Indexed)} + {event, lists:reverse(Indexed) ++ [EvtIndex], Payload(Data)} end, Pat = fun(Tag, Types) -> {tuple, [{integer, Tag} | ArgPats(Types)]} end, From 3c8d9561a03794b03963e90d988646e968dfde75 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 16:27:16 +0200 Subject: [PATCH 5/6] More thorough test for different event types --- test/contracts/events.aes | 50 ++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/test/contracts/events.aes b/test/contracts/events.aes index cb2c4b0..3746621 100644 --- a/test/contracts/events.aes +++ b/test/contracts/events.aes @@ -1,22 +1,40 @@ +contract Remote = + function dummy : () => () + contract Events = - type alias_int = int - type alias_address = address - type alias_string = string - datatype event = - Event1(indexed alias_int, indexed int, string) - | Event2(alias_string, indexed alias_address) - // | BadEvent1(indexed string, string) - // | BadEvent2(indexed int, int) + // Valid index types + type ix1 = int + type ix2 = bool + type ix3 = bits + type ix4 = bytes(12) + type ix5 = hash // bytes(32) + type ix6 = address + type ix7 = Remote + type ix8 = oracle(int, int) + type ix9 = oracle_query(int, int) - function f1(x : int, y : string) = - Chain.event(Event1(x, x+1, y)) + // Valid payload types + type data1 = string + type data2 = signature // bytes(64) + type data3 = bytes(65) - function f2(s : string) = - Chain.event(Event2(s, Call.caller)) + datatype event + = Nodata0 + | Nodata1(ix1) + | Nodata2(ix2, ix3) + | Nodata3(ix4, ix5, ix6) + | Data0(data1) + | Data1(data2, ix7) + | Data2(ix8, data3, ix9) + | Data3(ix1, ix2, ix5, data1) - function f3(x : int) = - Chain.event(Event1(x, x + 2, Int.to_str(x + 7))) + function nodata0() = Chain.event(Nodata0) + function nodata1(ix1) = Chain.event(Nodata1(ix1)) + function nodata2(ix2, ix3) = Chain.event(Nodata2(ix2, ix3)) + function nodata3(ix4, ix5, ix6) = Chain.event(Nodata3(ix4, ix5, ix6)) + function data0(data1) = Chain.event(Data0(data1)) + function data1(data2, ix7) = Chain.event(Data1(data2, ix7)) + function data2(ix8, data3, ix9) = Chain.event(Data2(ix8, data3, ix9)) + function data3(ix1, ix2, ix5, data1) = Chain.event(Data3(ix1, ix2, ix5, data1)) - function i2s(i : int) = Int.to_str(i) - function a2s(a : address) = Address.to_str(a) From 20085301ef049cd95813dee8d6e5cdd229798ec6 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 25 Jun 2019 11:41:05 +0200 Subject: [PATCH 6/6] aebytecode commit --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 1d7b511..b92aaa1 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"bf05e14"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"677712b"}}} , {getopt, "1.0.1"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}} diff --git a/rebar.lock b/rebar.lock index 4c2c833..ba48b19 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"bf05e14661ae25905bd020bfc2dcbc074f8ad66b"}}, + {ref,"677712b0b857da6a747b674b4efb1e030937f5f3"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git",