diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 61b26ab..b516e7f 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -20,26 +20,6 @@ -include("aeso_utils.hrl"). --type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()] | var_args, utype()} - | {app_t, aeso_syntax:ann(), utype(), [utype()]} - | {tuple_t, aeso_syntax:ann(), [utype()]} - | aeso_syntax:id() | aeso_syntax:qid() - | aeso_syntax:con() | aeso_syntax:qcon() %% contracts - | aeso_syntax:tvar() - | {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected) - | uvar(). - --type uvar() :: {uvar, aeso_syntax:ann(), reference()}. - --type named_args_t() :: uvar() | [{named_arg_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), aeso_syntax:expr()}]. - --type type_id() :: aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon(). - --define(is_type_id(T), element(1, T) =:= id orelse - element(1, T) =:= qid orelse - element(1, T) =:= con orelse - element(1, T) =:= qcon). - -type why_record() :: aeso_syntax:field(aeso_syntax:expr()) | {var_args, aeso_syntax:ann(), aeso_syntax:expr()} | {proj, aeso_syntax:ann(), aeso_syntax:expr(), aeso_syntax:id()}. @@ -87,714 +67,14 @@ -type byte_constraint() :: {is_bytes, utype()} | {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}. --type constraint() :: named_argument_constraint() | field_constraint() | byte_constraint(). - --record(field_info, - { ann :: aeso_syntax:ann() - , field_t :: utype() - , record_t :: utype() - , kind :: contract | record }). - --type field_info() :: #field_info{}. - --type access() :: public | private | internal. - --type typedef() :: {[aeso_syntax:tvar()], aeso_syntax:typedef() | {contract_t, [aeso_syntax:field_t()]}} - | {builtin, non_neg_integer()}. - --type type() :: aeso_syntax:type(). --type name() :: string(). --type qname() :: [string()]. --type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. - --type namespace_alias() :: none | name(). --type namespace_parts() :: none | {for, [name()]} | {hiding, [name()]}. --type used_namespaces() :: [{qname(), namespace_alias(), namespace_parts()}]. - --type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash. - -type variance() :: invariant | covariant | contravariant | bivariant. --type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. --type type_info() :: {aeso_syntax:ann(), typedef()}. --type var_info() :: {aeso_syntax:ann(), utype()}. - --type fun_env() :: [{name(), fun_info()}]. --type type_env() :: [{name(), type_info()}]. - --record(scope, { funs = [] :: fun_env() - , types = [] :: type_env() - , access = public :: access() - , kind = namespace :: namespace | contract - , ann = [{origin, system}] :: aeso_syntax:ann() - }). - --type scope() :: #scope{}. - --record(env, - { scopes = #{ [] => #scope{}} :: #{ qname() => scope() } - , vars = [] :: [{name(), var_info()}] - , typevars = unrestricted :: unrestricted | [name()] - , fields = #{} :: #{ name() => [field_info()] } %% fields are global - , contract_parents = #{} :: #{ name() => [name()] } - , namespace = [] :: qname() - , used_namespaces = [] :: used_namespaces() - , in_pattern = false :: boolean() - , in_guard = false :: boolean() - , stateful = false :: boolean() - , unify_throws = true :: boolean() - , current_function = none :: none | aeso_syntax:id() - , what = top :: top | namespace | contract | contract_interface - }). - --type env() :: #env{}. +-type constraint() :: named_argument_constraint() | field_constraint() | byte_constraint(). -define(PRINT_TYPES(Fmt, Args), when_option(pp_types, fun () -> io:format(Fmt, Args) end)). -define(CONSTRUCTOR_MOCK_NAME, "#__constructor__#"). -%% -- Environment manipulation ----------------------------------------------- - --spec push_scope(namespace | contract, aeso_syntax:con(), env()) -> env(). -push_scope(Kind, Con, Env) -> - Ann = aeso_syntax:get_ann(Con), - Name = name(Con), - New = Env#env.namespace ++ [Name], - Env#env{ namespace = New, scopes = (Env#env.scopes)#{ New => #scope{ kind = Kind, ann = Ann } } }. - --spec pop_scope(env()) -> env(). -pop_scope(Env) -> - Env#env{ namespace = lists:droplast(Env#env.namespace) }. - --spec get_scope(env(), qname()) -> false | scope(). -get_scope(#env{ scopes = Scopes }, Name) -> - maps:get(Name, Scopes, false). - --spec on_current_scope(env(), fun((scope()) -> scope())) -> env(). -on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) -> - Scope = maps:get(NS, Scopes), - Env#env{ scopes = Scopes#{ NS => Fun(Scope) } }. - --spec on_scopes(env(), fun((scope()) -> scope())) -> env(). -on_scopes(Env = #env{ scopes = Scopes }, Fun) -> - Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }. - --spec bind_var(aeso_syntax:id(), utype(), env()) -> env(). -bind_var({id, Ann, X}, T, Env = #env{ vars = Vars }) -> - when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Ann, X, Vars) end), - Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }. - --spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env(). -bind_vars([], Env) -> Env; -bind_vars([{X, T} | Vars], Env) -> - bind_vars(Vars, bind_var(X, T, Env)). - --spec bind_tvars([aeso_syntax:tvar()], env()) -> env(). -bind_tvars(Xs, Env) -> - Env#env{ typevars = [X || {tvar, _, X} <- Xs] }. - --spec check_tvar(env(), aeso_syntax:tvar()) -> aeso_syntax:tvar() | no_return(). -check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) -> - case TVars == unrestricted orelse lists:member(X, TVars) of - true -> ok; - false -> type_error({unbound_type, T}) - end, - T. - --spec bind_fun(name(), type() | typesig(), env()) -> env(). -bind_fun(X, Type, Env) -> - case lookup_env(Env, term, [], [X]) of - false -> force_bind_fun(X, Type, Env); - {_QId, {Ann1, _}} -> - type_error({duplicate_definition, X, [Ann1, aeso_syntax:get_ann(Type)]}), - Env - end. - --spec force_bind_fun(name(), type() | typesig(), env()) -> env(). -force_bind_fun(X, Type, Env = #env{ what = What }) -> - Ann = aeso_syntax:get_ann(Type), - NoCode = get_option(no_code, false), - Entry = if X == "init", What == contract, not NoCode -> - {reserved_init, Ann, Type}; - What == contract_interface -> {contract_fun, Ann, Type}; - true -> {Ann, Type} - end, - on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> - Scope#scope{ funs = [{X, Entry} | Funs] } - end). - --spec bind_funs([{name(), type() | typesig()}], env()) -> env(). -bind_funs([], Env) -> Env; -bind_funs([{Id, Type} | Rest], Env) -> - bind_funs(Rest, bind_fun(Id, Type, Env)). - --spec bind_type(name(), aeso_syntax:ann(), typedef(), env()) -> env(). -bind_type(X, Ann, Def, Env) -> - on_current_scope(Env, fun(Scope = #scope{ types = Types }) -> - Scope#scope{ types = [{X, {Ann, Def}} | Types] } - end). - -%% Bind state primitives --spec bind_state(env()) -> env(). -bind_state(Env) -> - Ann = [{origin, system}], - Unit = {tuple_t, Ann, []}, - State = - case lookup_type(Env, {id, Ann, "state"}) of - {S, _} -> {qid, Ann, S}; - false -> Unit - end, - Env1 = bind_funs([{"state", State}, - {"put", {type_sig, [stateful | Ann], none, [], [State], Unit}}], Env), - - case lookup_type(Env, {id, Ann, "event"}) of - {E, _} -> - %% We bind Chain.event in a local 'Chain' namespace. - Event = {qid, Ann, E}, - pop_scope( - bind_fun("event", {fun_t, Ann, [], [Event], Unit}, - push_scope(namespace, {con, Ann, "Chain"}, Env1))); - false -> Env1 - end. - --spec bind_field(name(), field_info(), env()) -> env(). -bind_field(X, Info, Env = #env{ fields = Fields }) -> - Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields), - Env#env{ fields = Fields1 }. - --spec bind_fields([{name(), field_info()}], env()) -> env(). -bind_fields([], Env) -> Env; -bind_fields([{Id, Info} | Rest], Env) -> - bind_fields(Rest, bind_field(Id, Info, Env)). - -%% Contract entrypoints take three named arguments -%% gas : int = Call.gas_left() -%% value : int = 0 -%% protected : bool = false -contract_call_type({fun_t, Ann, [], Args, Ret}) -> - Id = fun(X) -> {id, Ann, X} end, - Int = Id("int"), - Typed = fun(E, T) -> {typed, Ann, E, T} end, - Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end, - {fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]}, - {fun_t, Ann, [], [], Int}), - []}, Int)), - Named("value", Typed({int, Ann, 0}, Int)), - Named("protected", Typed({bool, Ann, false}, Id("bool")))], - Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}. - --spec bind_contract(aeso_syntax:decl(), env()) -> env(). -bind_contract({Contract, Ann, Id, _Impls, Contents}, Env) - when ?IS_CONTRACT_HEAD(Contract) -> - Key = name(Id), - Sys = [{origin, system}], - Fields = - [ {field_t, AnnF, Entrypoint, contract_call_type(Type)} - || {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++ - [ {field_t, AnnF, Entrypoint, - contract_call_type( - {fun_t, AnnF, [], [ArgT || {typed, _, _, ArgT} <- Args], RetT}) - } - || {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], {typed, _, _, RetT}}]} <- Contents, - Name =/= "init" - ] ++ - %% Predefined fields - [ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++ - [ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME}, - contract_call_type( - case [ [ArgT || {typed, _, _, ArgT} <- Args] - || {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents, - aeso_syntax:get_ann(entrypoint, AnnF, false)] - ++ [ Args - || {fun_decl, AnnF, {id, _, "init"}, {fun_t, _, _, Args, _}} <- Contents, - aeso_syntax:get_ann(entrypoint, AnnF, false)] - ++ [ Args - || {fun_decl, AnnF, {id, _, "init"}, {type_sig, _, _, _, Args, _}} <- Contents, - aeso_syntax:get_ann(entrypoint, AnnF, false)] - of - [] -> {fun_t, [stateful,payable|Sys], [], [], {id, Sys, "void"}}; - [Args] -> {fun_t, [stateful,payable|Sys], [], Args, {id, Sys, "void"}} - end - ) - } - ], - FieldInfo = [ {Entrypoint, #field_info{ ann = FieldAnn, - kind = contract, - field_t = Type, - record_t = Id }} - || {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ], - bind_type(Key, Ann, {[], {contract_t, Fields}}, - bind_fields(FieldInfo, Env)). - -%% What scopes could a given name come from? --spec possible_scopes(env(), qname()) -> [qname()]. -possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, Name) -> - Qual = lists:droplast(Name), - NewQuals = case lists:filter(fun(X) -> element(2, X) == Qual end, UsedNamespaces) of - [] -> - [Qual]; - Namespaces -> - lists:map(fun(X) -> element(1, X) end, Namespaces) - end, - Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ], - Ret2 = [ Namespace ++ Q || {Namespace, none, _} <- UsedNamespaces, Q <- NewQuals ], - lists:usort(Ret1 ++ Ret2). - --spec visible_in_used_namespaces(used_namespaces(), qname()) -> boolean(). -visible_in_used_namespaces(UsedNamespaces, QName) -> - Qual = lists:droplast(QName), - Name = lists:last(QName), - case lists:filter(fun({Ns, _, _}) -> Qual == Ns end, UsedNamespaces) of - [] -> - true; - Namespaces -> - IsVisible = fun(Namespace) -> - case Namespace of - {_, _, {for, Names}} -> - lists:member(Name, Names); - {_, _, {hiding, Names}} -> - not lists:member(Name, Names); - _ -> - true - end - end, - lists:any(IsVisible, Namespaces) - end. - --spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. -lookup_type(Env, Id) -> - lookup_env(Env, type, aeso_syntax:get_ann(Id), qname(Id)). - --spec lookup_env(env(), term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}; - (env(), type, aeso_syntax:ann(), qname()) -> false | {qname(), type_info()}. -lookup_env(Env, Kind, Ann, Name) -> - Var = case Name of - [X] when Kind == term -> proplists:get_value(X, Env#env.vars, false); - _ -> false - end, - case Var of - {Ann1, Type} -> {Name, {Ann1, Type}}; - false -> - Names = [ Qual ++ [lists:last(Name)] || Qual <- possible_scopes(Env, Name) ], - case [ Res || QName <- Names, Res <- [lookup_env1(Env, Kind, Ann, QName)], Res /= false] of - [] -> false; - [Res = {_, {AnnR, _}}] -> - when_warning(warn_unused_includes, - fun() -> - %% If a file is used from a different file, we - %% can then mark it as used - F1 = proplists:get_value(file, Ann, no_file), - F2 = proplists:get_value(file, AnnR, no_file), - if - F1 /= F2 -> - used_include(AnnR); - true -> - ok - end - end), - Res; - Many -> - type_error({ambiguous_name, qid(Ann, Name), [{qid, A, Q} || {Q, {A, _}} <- Many]}), - false - end - end. - --spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}. -lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) -> - Qual = lists:droplast(QName), - Name = lists:last(QName), - AllowPrivate = lists:prefix(Qual, Current), - %% Get the scope - case maps:get(Qual, Scopes, false) of - false -> false; %% TODO: return reason for not in scope - #scope{ funs = Funs, types = Types } -> - Defs = case Kind of - type -> Types; - term -> Funs - end, - %% Look up the unqualified name - case proplists:get_value(Name, Defs, false) of - false -> false; - {reserved_init, Ann1, Type} -> - type_error({cannot_call_init_function, Ann}), - {QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error - {contract_fun, Ann1, Type} -> - type_error({contract_treated_as_namespace, Ann, QName}), - {QName, {Ann1, Type}}; - {Ann1, _} = E -> - %% Check that it's not private (or we can see private funs) - case not is_private(Ann1) orelse AllowPrivate of - true -> - case visible_in_used_namespaces(UsedNamespaces, QName) of - true -> {QName, E}; - false -> false - end; - false -> false - end - end - end. - --spec lookup_record_field(env(), name()) -> [field_info()]. -lookup_record_field(Env, FieldName) -> - maps:get(FieldName, Env#env.fields, []). - -%% For 'create' or 'update' constraints we don't consider contract types. --spec lookup_record_field(env(), name(), create | project | update) -> [field_info()]. -lookup_record_field(Env, FieldName, Kind) -> - [ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName), - Kind == project orelse K /= contract ]. - -%% -- Name manipulation ------------------------------------------------------ - --spec qname(type_id()) -> qname(). -qname({id, _, X}) -> [X]; -qname({qid, _, Xs}) -> Xs; -qname({con, _, X}) -> [X]; -qname({qcon, _, Xs}) -> Xs. - --spec name(aeso_syntax:id() | aeso_syntax:con()) -> name(). -name({_, _, X}) -> X. - --spec qid(aeso_syntax:ann(), qname()) -> aeso_syntax:id() | aeso_syntax:qid(). -qid(Ann, [X]) -> {id, Ann, X}; -qid(Ann, Xs) -> {qid, Ann, Xs}. - --spec qcon(aeso_syntax:ann(), qname()) -> aeso_syntax:con() | aeso_syntax:qcon(). -qcon(Ann, [X]) -> {con, Ann, X}; -qcon(Ann, Xs) -> {qcon, Ann, Xs}. - --spec set_qname(qname(), type_id()) -> type_id(). -set_qname(Xs, {id, Ann, _}) -> qid(Ann, Xs); -set_qname(Xs, {qid, Ann, _}) -> qid(Ann, Xs); -set_qname(Xs, {con, Ann, _}) -> qcon(Ann, Xs); -set_qname(Xs, {qcon, Ann, _}) -> qcon(Ann, Xs). - -is_private(Ann) -> proplists:get_value(private, Ann, false). - -%% -- The rest --------------------------------------------------------------- - -%% Environment containing language primitives --spec global_env() -> env(). -global_env() -> - Ann = [{origin, system}], - Int = {id, Ann, "int"}, - Char = {id, Ann, "char"}, - Bool = {id, Ann, "bool"}, - String = {id, Ann, "string"}, - Address = {id, Ann, "address"}, - Hash = {id, Ann, "hash"}, - Bits = {id, Ann, "bits"}, - Bytes = fun(Len) -> {bytes_t, Ann, Len} end, - Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, - Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, - Unit = {tuple_t, Ann, []}, - List = fun(T) -> {app_t, Ann, {id, Ann, "list"}, [T]} end, - Option = fun(T) -> {app_t, Ann, {id, Ann, "option"}, [T]} end, - Map = fun(A, B) -> {app_t, Ann, {id, Ann, "map"}, [A, B]} end, - Pair = fun(A, B) -> {tuple_t, Ann, [A, B]} end, - FunC = fun(C, Ts, T) -> {type_sig, Ann, C, [], Ts, T} end, - FunC1 = fun(C, S, T) -> {type_sig, Ann, C, [], [S], T} end, - Fun = fun(Ts, T) -> FunC(none, Ts, T) end, - Fun1 = fun(S, T) -> Fun([S], T) end, - FunCN = fun(C, Named, Normal, Ret) -> {type_sig, Ann, C, Named, Normal, Ret} end, - FunN = fun(Named, Normal, Ret) -> FunCN(none, Named, Normal, Ret) end, - %% Lambda = fun(Ts, T) -> {fun_t, Ann, [], Ts, T} end, - %% Lambda1 = fun(S, T) -> Lambda([S], T) end, - StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [], Ts, T} end, - TVar = fun(X) -> {tvar, Ann, "'" ++ X} end, - SignId = {id, Ann, "signature"}, - SignDef = {bytes, Ann, <<0:64/unit:8>>}, - Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}}, - SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end, - TTL = {qid, Ann, ["Chain", "ttl"]}, - Pointee = {qid, Ann, ["AENS", "pointee"]}, - AENSName = {qid, Ann, ["AENS", "name"]}, - Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]}, - Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]}, - Fp2 = {tuple_t, Ann, [Fp, Fp]}, - G1 = {tuple_t, Ann, [Fp, Fp, Fp]}, - G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]}, - GT = {tuple_t, Ann, lists:duplicate(12, Fp)}, - Tx = {qid, Ann, ["Chain", "tx"]}, - GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]}, - BaseTx = {qid, Ann, ["Chain", "base_tx"]}, - PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]}, - - FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end, - TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)}, - {"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}], - TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]}, - Stateful = fun(T) -> setelement(2, T, [stateful|element(2, T)]) end, - - Fee = Int, - [A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]), - - MkDefs = fun(Defs) -> [{X, {Ann, if is_integer(T) -> {builtin, T}; true -> T end}} || {X, T} <- Defs] end, - - TopScope = #scope - { funs = MkDefs( - %% Option constructors - [{"None", Option(A)}, - {"Some", Fun1(A, Option(A))}, - %% TTL constructors - {"RelativeTTL", Fun1(Int, TTL)}, - {"FixedTTL", Fun1(Int, TTL)}, - %% Abort - {"abort", Fun1(String, A)}, - {"require", Fun([Bool, String], Unit)}]) - , types = MkDefs( - [{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0}, - {"void", 0}, - {"unit", {[], {alias_t, Unit}}}, - {"hash", {[], {alias_t, Bytes(32)}}}, - {"signature", {[], {alias_t, Bytes(64)}}}, - {"bits", 0}, - {"option", 1}, {"list", 1}, {"map", 2}, - {"oracle", 2}, {"oracle_query", 2} - ]) }, - - ChainScope = #scope - { funs = MkDefs( - %% Spend transaction. - [{"spend", StateFun([Address, Int], Unit)}, - %% Chain environment - {"balance", Fun1(Address, Int)}, - {"block_hash", Fun1(Int, Option(Hash))}, - {"coinbase", Address}, - {"timestamp", Int}, - {"block_height", Int}, - {"difficulty", Int}, - {"gas_limit", Int}, - {"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))}, - {"create", Stateful( - FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}} - ], var_args, A))}, - {"clone", Stateful( - FunN([ {named_arg_t, Ann, {id, Ann, "gas"}, Int, - {typed, Ann, - {app, Ann, - {typed, Ann, {qid, Ann, ["Call","gas_left"]}, - typesig_to_fun_t(Fun([], Int)) - }, - []}, Int - }} - , {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}} - , {named_arg_t, Ann, {id, Ann, "protected"}, Bool, {typed, Ann, {bool, Ann, false}, Bool}} - , {named_arg_t, Ann, {id, Ann, "ref"}, A, undefined} - ], var_args, A))}, - %% Tx constructors - {"GAMetaTx", Fun([Address, Int], GAMetaTx)}, - {"PayingForTx", Fun([Address, Int], PayForTx)}, - {"SpendTx", Fun([Address, Int, String], BaseTx)}, - {"OracleRegisterTx", BaseTx}, - {"OracleQueryTx", BaseTx}, - {"OracleResponseTx", BaseTx}, - {"OracleExtendTx", BaseTx}, - {"NamePreclaimTx", BaseTx}, - {"NameClaimTx", Fun([String], BaseTx)}, - {"NameUpdateTx", Fun([Hash], BaseTx)}, - {"NameRevokeTx", Fun([Hash], BaseTx)}, - {"NameTransferTx", Fun([Address, Hash], BaseTx)}, - {"ChannelCreateTx", Fun([Address], BaseTx)}, - {"ChannelDepositTx", Fun([Address, Int], BaseTx)}, - {"ChannelWithdrawTx", Fun([Address, Int], BaseTx)}, - {"ChannelForceProgressTx", Fun([Address], BaseTx)}, - {"ChannelCloseMutualTx", Fun([Address], BaseTx)}, - {"ChannelCloseSoloTx", Fun([Address], BaseTx)}, - {"ChannelSlashTx", Fun([Address], BaseTx)}, - {"ChannelSettleTx", Fun([Address], BaseTx)}, - {"ChannelSnapshotSoloTx", Fun([Address], BaseTx)}, - {"ContractCreateTx", Fun([Int], BaseTx)}, - {"ContractCallTx", Fun([Address, Int], BaseTx)}, - {"GAAttachTx", BaseTx} - ]) - , types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}}, - {"base_tx", 0}, - {"paying_for_tx", 0}, {"ga_meta_tx", 0}]) }, - - ContractScope = #scope - { funs = MkDefs( - [{"address", Address}, - {"creator", Address}, - {"balance", Int}]) }, - - CallScope = #scope - { funs = MkDefs( - [{"origin", Address}, - {"caller", Address}, - {"value", Int}, - {"gas_price", Int}, - {"fee", Int}, - {"gas_left", Fun([], Int)}]) - }, - - OracleScope = #scope - { funs = MkDefs( - [{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))}, - {"expiry", Fun([Oracle(Q, R)], Fee)}, - {"query_fee", Fun([Oracle(Q, R)], Fee)}, - {"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))}, - {"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)}, - {"respond", SignFun([Oracle(Q, R), Query(Q, R), R], Unit)}, - {"extend", SignFun([Oracle(Q, R), TTL], Unit)}, - {"get_answer", Fun([Oracle(Q, R), Query(Q, R)], option_t(Ann, R))}, - {"check", Fun([Oracle(Q, R)], Bool)}, - {"check_query", Fun([Oracle(Q,R), Query(Q, R)], Bool)}]) }, - - AENSScope = #scope - { funs = MkDefs( - [{"resolve", Fun([String, String], option_t(Ann, A))}, - {"preclaim", SignFun([Address, Hash], Unit)}, - {"claim", SignFun([Address, String, Int, Int], Unit)}, - {"transfer", SignFun([Address, Address, String], Unit)}, - {"revoke", SignFun([Address, String], Unit)}, - {"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)}, - {"lookup", Fun([String], option_t(Ann, AENSName))}, - %% AENS pointee constructors - {"AccountPt", Fun1(Address, Pointee)}, - {"OraclePt", Fun1(Address, Pointee)}, - {"ContractPt", Fun1(Address, Pointee)}, - {"ChannelPt", Fun1(Address, Pointee)}, - %% Name object constructor - {"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)} - ]) - , types = MkDefs([{"pointee", 0}, {"name", 0}]) }, - - MapScope = #scope - { funs = MkDefs( - [{"from_list", Fun1(List(Pair(K, V)), Map(K, V))}, - {"to_list", Fun1(Map(K, V), List(Pair(K, V)))}, - {"lookup", Fun([K, Map(K, V)], Option(V))}, - {"lookup_default", Fun([K, Map(K, V), V], V)}, - {"delete", Fun([K, Map(K, V)], Map(K, V))}, - {"member", Fun([K, Map(K, V)], Bool)}, - {"size", Fun1(Map(K, V), Int)}]) }, - - %% Crypto/Curve operations - CryptoScope = #scope - { funs = MkDefs( - [{"verify_sig", Fun([Hash, Address, SignId], Bool)}, - {"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)}, - {"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)}, - {"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))}, - {"sha3", Fun1(A, Hash)}, - {"sha256", Fun1(A, Hash)}, - {"blake2b", Fun1(A, Hash)}]) }, - - %% Fancy BLS12-381 crypto operations - MCL_BLS12_381_Scope = #scope - { funs = MkDefs( - [{"g1_neg", Fun1(G1, G1)}, - {"g1_norm", Fun1(G1, G1)}, - {"g1_valid", Fun1(G1, Bool)}, - {"g1_is_zero", Fun1(G1, Bool)}, - {"g1_add", Fun ([G1, G1], G1)}, - {"g1_mul", Fun ([Fr, G1], G1)}, - - {"g2_neg", Fun1(G2, G2)}, - {"g2_norm", Fun1(G2, G2)}, - {"g2_valid", Fun1(G2, Bool)}, - {"g2_is_zero", Fun1(G2, Bool)}, - {"g2_add", Fun ([G2, G2], G2)}, - {"g2_mul", Fun ([Fr, G2], G2)}, - - {"gt_inv", Fun1(GT, GT)}, - {"gt_add", Fun ([GT, GT], GT)}, - {"gt_mul", Fun ([GT, GT], GT)}, - {"gt_pow", Fun ([GT, Fr], GT)}, - {"gt_is_one", Fun1(GT, Bool)}, - {"pairing", Fun ([G1, G2], GT)}, - {"miller_loop", Fun ([G1, G2], GT)}, - {"final_exp", Fun1(GT, GT)}, - - {"int_to_fr", Fun1(Int, Fr)}, - {"int_to_fp", Fun1(Int, Fp)}, - {"fr_to_int", Fun1(Fr, Int)}, - {"fp_to_int", Fun1(Fp, Int)} - ]), - types = MkDefs( - [{"fr", 0}, {"fp", 0}]) }, - - %% Authentication - AuthScope = #scope - { funs = MkDefs( - [{"tx_hash", Option(Hash)}, - {"tx", Option(Tx)} ]) }, - - %% Strings - StringScope = #scope - { funs = MkDefs( - [{"length", Fun1(String, Int)}, - {"concat", Fun([String, String], String)}, - {"to_list", Fun1(String, List(Char))}, - {"from_list", Fun1(List(Char), String)}, - {"to_upper", Fun1(String, String)}, - {"to_lower", Fun1(String, String)}, - {"sha3", Fun1(String, Hash)}, - {"sha256", Fun1(String, Hash)}, - {"blake2b", Fun1(String, Hash)} - ]) }, - - %% Chars - CharScope = #scope - { funs = MkDefs( - [{"to_int", Fun1(Char, Int)}, - {"from_int", Fun1(Int, Option(Char))}]) }, - - %% Bits - BitsScope = #scope - { funs = MkDefs( - [{"set", Fun([Bits, Int], Bits)}, - {"clear", Fun([Bits, Int], Bits)}, - {"test", Fun([Bits, Int], Bool)}, - {"sum", Fun1(Bits, Int)}, - {"intersection", Fun([Bits, Bits], Bits)}, - {"union", Fun([Bits, Bits], Bits)}, - {"difference", Fun([Bits, Bits], Bits)}, - {"none", Bits}, - {"all", Bits}]) }, - - %% Bytes - BytesScope = #scope - { funs = MkDefs( - [{"to_int", Fun1(Bytes(any), Int)}, - {"to_str", Fun1(Bytes(any), String)}, - {"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))}, - {"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))} - ]) }, - - %% Conversion - IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) }, - AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, - {"to_contract", FunC(address_to_contract, [Address], A)}, - {"is_oracle", Fun1(Address, Bool)}, - {"is_contract", Fun1(Address, Bool)}, - {"is_payable", Fun1(Address, Bool)}]) }, - - - #env{ scopes = - #{ [] => TopScope - , ["Chain"] => ChainScope - , ["Contract"] => ContractScope - , ["Call"] => CallScope - , ["Oracle"] => OracleScope - , ["AENS"] => AENSScope - , ["Map"] => MapScope - , ["Auth"] => AuthScope - , ["Crypto"] => CryptoScope - , ["MCL_BLS12_381"] => MCL_BLS12_381_Scope - , ["StringInternal"] => StringScope - , ["Char"] => CharScope - , ["Bits"] => BitsScope - , ["Bytes"] => BytesScope - , ["Int"] => IntScope - , ["Address"] => AddressScope - } - , fields = - maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]} - || {N, T} <- TxFlds ]) - }. - option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}. map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.