From 126e04ae427d3f68e5745bca46ca48251db82674 Mon Sep 17 00:00:00 2001 From: skkw Date: Tue, 10 Sep 2019 15:33:02 +0200 Subject: [PATCH 01/13] support for AENS.update call --- rebar.config | 2 +- rebar.lock | 2 +- src/aeso_ast_infer_types.erl | 10 +++++++- src/aeso_ast_to_fcode.erl | 42 ++++++++++++++++++---------------- src/aeso_ast_to_icode.erl | 10 ++++++++ src/aeso_fcode_to_fate.erl | 5 +++- src/aeso_icode.erl | 44 ++++++++++++++++++++---------------- test/contracts/aens.aes | 17 +++++++++++++- 8 files changed, 88 insertions(+), 44 deletions(-) diff --git a/rebar.config b/rebar.config index e5ca552..87edb36 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,"4f4d6d3"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"38f4f0e"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index f5cf812..f4e93cb 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}}, + {ref,"38f4f0ecd75db592f596b9687c37945d4fc807d7"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 9376129..db5b39a 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -389,6 +389,8 @@ global_env() -> 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"]}, + Fee = Int, [A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]), @@ -402,6 +404,10 @@ global_env() -> %% TTL constructors {"RelativeTTL", Fun1(Int, TTL)}, {"FixedTTL", Fun1(Int, TTL)}, + %% AENS pointee constructors + {"AccountPointee", Fun1(Address, Pointee)}, + {"OraclePointee", Fun1(Address, Pointee)}, + {"ContractPointee", Fun1(Address, Pointee)}, %% Abort {"abort", Fun1(String, A)}, {"require", Fun([Bool, String], Unit)}]) @@ -462,7 +468,9 @@ global_env() -> {"preclaim", SignFun([Address, Hash], Unit)}, {"claim", SignFun([Address, String, Int, Int], Unit)}, {"transfer", SignFun([Address, Address, String], Unit)}, - {"revoke", SignFun([Address, String], Unit)}]) }, + {"revoke", SignFun([Address, String], Unit)}, + {"update", SignFun([Address, String, Option(TTL), Option(TTL), Option(Map(String, Pointee))], Unit)}]) + , types = MkDefs([{"pointee", 0}]) }, MapScope = #scope { funs = MkDefs( diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 35d5d06..6fa163a 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -164,10 +164,13 @@ init_env(Options) -> #{ type_env => init_type_env(), fun_env => #{}, builtins => builtins(), - con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, - ["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, - ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, - ["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] } + con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, + ["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, + ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, + ["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }, + ["AccountPointee"] => #con_tag{ tag = 0, arities = [1, 1, 1] }, + ["OraclePointee"] => #con_tag{ tag = 1, arities = [1, 1, 1] }, + ["ContractPointee"] => #con_tag{ tag = 2, arities = [1, 1, 1] } }, options => Options, functions => #{} }. @@ -188,7 +191,7 @@ builtins() -> {"respond", 4}, {"extend", 3}, {"get_answer", 2}, {"check", 1}, {"check_query", 2}]}, {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4}, - {"revoke", 3}]}, + {"revoke", 3}, {"update", 6}]}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, {["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3}, @@ -212,20 +215,21 @@ builtins() -> -spec init_type_env() -> type_env(). init_type_env() -> - #{ ["int"] => ?type(integer), - ["bool"] => ?type(boolean), - ["bits"] => ?type(bits), - ["char"] => ?type(integer), - ["string"] => ?type(string), - ["address"] => ?type(address), - ["hash"] => ?type(hash), - ["signature"] => ?type(signature), - ["oracle"] => ?type(Q, R, {oracle, Q, R}), - ["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate - ["list"] => ?type(T, {list, T}), - ["map"] => ?type(K, V, {map, K, V}), - ["option"] => ?type(T, {variant, [[], [T]]}), - ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}) + #{ ["int"] => ?type(integer), + ["bool"] => ?type(boolean), + ["bits"] => ?type(bits), + ["char"] => ?type(integer), + ["string"] => ?type(string), + ["address"] => ?type(address), + ["hash"] => ?type(hash), + ["signature"] => ?type(signature), + ["oracle"] => ?type(Q, R, {oracle, Q, R}), + ["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate + ["list"] => ?type(T, {list, T}), + ["map"] => ?type(K, V, {map, K, V}), + ["option"] => ?type(T, {variant, [[], [T]]}), + ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}), + ["AENS", "pointee"] => ?type({variant, [[address], [address], [address]]}) }. is_no_code(Env) -> diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index acf68ea..0dc28fa 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -463,6 +463,7 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) -> is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true; is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true; is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true; +is_builtin_fun({qid, _, ["AENS", "update"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true; @@ -618,6 +619,12 @@ builtin_code(_, {qid, _, ["AENS", "revoke"]}, Args, _, _, Icode) -> [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)], [word, word, sign_t()], {tuple, []}); +builtin_code(_, {qid, _, ["AENS", "update"]}, Args, _, _, Icode) -> + {Sign, [Addr, Name, TTL, ClientTTL, Pointers]} = get_signature_arg(Args), + prim_call(?PRIM_CALL_AENS_UPDATE, #integer{value = 0}, + [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(TTL, Icode), ast_body(ClientTTL, Icode), ast_body(Pointers, Icode), ast_body(Sign, Icode)], + [word, string, word, word, word, sign_t()], {tuple, []}); + %% -- Maps %% -- lookup functions builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) -> @@ -926,6 +933,9 @@ ast_typerep1({variant_t, Cons}, Icode) -> ttl_t(Icode) -> ast_typerep({qid, [], ["Chain", "ttl"]}, Icode). +%% pointee_t(Icode) -> +%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode). + sign_t() -> bytes_t(64). bytes_t(Len) when Len =< 32 -> word; bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}. diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 7c399b9..8c93490 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -572,6 +572,9 @@ builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) -> builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) -> call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a), tuple(0)], Args); +builtin_to_scode(Env, aens_update, [_Sign, _Account, _NameString, _TTL, _ClientTTL, _Pointers] = Args) -> + call_to_scode(Env, [aeb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a), + tuple(0)], Args); builtin_to_scode(_Env, auth_tx_hash, []) -> [aeb_fate_ops:auth_tx_hash(?a)]. @@ -896,7 +899,7 @@ attributes(I) -> {'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]); {'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]); {'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); - 'AENS_UPDATE' -> Impure(none, []);%% TODO + {'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); {'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]); {'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]); {'ABORT', A} -> Impure(pc, A); diff --git a/src/aeso_icode.erl b/src/aeso_icode.erl index e6d50f9..0b6357b 100644 --- a/src/aeso_icode.erl +++ b/src/aeso_icode.erl @@ -73,28 +73,33 @@ new(Options) -> builtin_types() -> Word = fun([]) -> word end, - #{ "bool" => Word - , "int" => Word - , "char" => Word - , "bits" => Word - , "string" => fun([]) -> string end - , "address" => Word - , "hash" => Word - , "unit" => fun([]) -> {tuple, []} end - , "signature" => fun([]) -> {tuple, [word, word]} end - , "oracle" => fun([_, _]) -> word end - , "oracle_query" => fun([_, _]) -> word end - , "list" => fun([A]) -> {list, A} end - , "option" => fun([A]) -> {variant, [[], [A]]} end - , "map" => fun([K, V]) -> map_typerep(K, V) end - , ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end + #{ "bool" => Word + , "int" => Word + , "char" => Word + , "bits" => Word + , "string" => fun([]) -> string end + , "address" => Word + , "hash" => Word + , "unit" => fun([]) -> {tuple, []} end + , "signature" => fun([]) -> {tuple, [word, word]} end + , "oracle" => fun([_, _]) -> word end + , "oracle_query" => fun([_, _]) -> word end + , "list" => fun([A]) -> {list, A} end + , "option" => fun([A]) -> {variant, [[], [A]]} end + , "map" => fun([K, V]) -> map_typerep(K, V) end + , ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end + , ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end }. builtin_constructors() -> - #{ ["RelativeTTL"] => 0 - , ["FixedTTL"] => 1 - , ["None"] => 0 - , ["Some"] => 1 }. + #{ ["RelativeTTL"] => 0 + , ["FixedTTL"] => 1 + , ["None"] => 0 + , ["Some"] => 1 + , ["AccountPointee"] => 0 + , ["OraclePointee"] => 1 + , ["ContractPointee"] => 2 + }. map_typerep(K, V) -> {map, K, V}. @@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) -> undefined -> error({undefined_constructor, Name}); Tag -> Tag end. - diff --git a/test/contracts/aens.aes b/test/contracts/aens.aes index ee1ba27..cb34e82 100644 --- a/test/contracts/aens.aes +++ b/test/contracts/aens.aes @@ -33,7 +33,22 @@ contract AENSTest = sign : signature) : unit = AENS.claim(addr, name, salt, name_fee, signature = sign) - // TODO: update() -- how to handle pointers? + + stateful entrypoint update(owner : address, + name : string, + ttl : option(Chain.ttl), + client_ttl : option(Chain.ttl), + pointers : option(map(string, AENS.pointee))) : unit = + AENS.update(owner, name, ttl, client_ttl, pointers) + + stateful entrypoint signedUpdate(owner : address, + name : string, + ttl : option(Chain.ttl), + client_ttl : option(Chain.ttl), + pointers : option(map(string, AENS.pointee)), + sign : signature) : unit = + AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign) + stateful entrypoint transfer(owner : address, new_owner : address, -- 2.30.2 From baf527b5fada91af4b694fc21e751cc76f5c1b4d Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Mon, 18 Nov 2019 11:35:08 +0100 Subject: [PATCH 02/13] Handle encoding/decoding bits Fixes GH-174 --- src/aeso_compiler.erl | 8 ++++++++ src/aeso_fcode_to_fate.erl | 12 +++++++++++ src/aeso_vm_decode.erl | 32 ++++++++++++++++++++++++----- test/aeso_calldata_tests.erl | 40 +++++++++++++++++++++++++----------- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index a2ec0bc..f6ecc13 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -507,6 +507,14 @@ icode_to_term(T = {map, KT, VT}, M) -> #{}; _ -> throw({todo, M}) end; +icode_to_term(word, {unop, 'bnot', A}) -> + bnot icode_to_term(word, A); +icode_to_term(word, {binop, 'bor', A, B}) -> + icode_to_term(word, A) bor icode_to_term(word, B); +icode_to_term(word, {binop, 'bsl', A, B}) -> + icode_to_term(word, B) bsl icode_to_term(word, A); +icode_to_term(word, {binop, 'band', A, B}) -> + icode_to_term(word, A) band icode_to_term(word, B); icode_to_term(typerep, _) -> throw({todo, typerep}); icode_to_term(T, V) -> diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 8c93490..00aa43d 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -261,6 +261,18 @@ term_to_fate({tuple, 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)); +term_to_fate({builtin, bits_all, []}) -> + aeb_fate_data:make_bits(-1); +term_to_fate({builtin, bits_none, []}) -> + aeb_fate_data:make_bits(0); +term_to_fate({op, bits_set, [B, I]}) -> + {bits, N} = term_to_fate(B), + J = term_to_fate(I), + {bits, N bor (1 bsl J)}; +term_to_fate({op, bits_clear, [B, I]}) -> + {bits, N} = term_to_fate(B), + J = term_to_fate(I), + {bits, N band bnot (1 bsl J)}; term_to_fate({builtin, map_empty, []}) -> aeb_fate_data:make_map(#{}); term_to_fate({'let', _, {builtin, map_empty, []}, Set}) -> diff --git a/src/aeso_vm_decode.erl b/src/aeso_vm_decode.erl index bf61a48..827aed4 100644 --- a/src/aeso_vm_decode.erl +++ b/src/aeso_vm_decode.erl @@ -18,9 +18,14 @@ from_aevm(word, {id, _, "address"}, N) -> address_literal(ac from_aevm(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N); from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N); from_aevm(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N); -from_aevm(word, {id, _, "int"}, N) -> <> = <>, {int, [], N1}; -from_aevm(word, {id, _, "bits"}, N) -> error({todo, bits, N}); -from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; +from_aevm(word, {id, _, "int"}, N0) -> + <> = <>, + if N < 0 -> {app, [], {'-', []}, [{int, [], -N}]}; + true -> {int, [], N} end; +from_aevm(word, {id, _, "bits"}, N0) -> + <> = <>, + make_bits(N); +from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; from_aevm(word, {bytes_t, _, Len}, Val) when Len =< 32 -> <> = <>, {bytes, [], <>}; @@ -55,6 +60,7 @@ from_aevm({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args}) VmTypes = lists:nth(Tag + 1, VmCons), ConType = lists:nth(Tag + 1, Cons), from_aevm(VmTypes, ConType, Args); +from_aevm([], {constr_t, _, Con, []}, []) -> Con; from_aevm(VmTypes, {constr_t, _, Con, Types}, Args) when length(VmTypes) == length(Types), length(VmTypes) == length(Args) -> @@ -70,8 +76,10 @@ from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin}; from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin}; -from_fate({id, _, "bits"}, ?FATE_BITS(Bin)) -> error({todo, bits, Bin}); -from_fate({id, _, "int"}, N) when is_integer(N) -> {int, [], N}; +from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N); +from_fate({id, _, "int"}, N) when is_integer(N) -> + if N < 0 -> {app, [], {'-', []}, [{int, [], -N}]}; + true -> {int, [], N} end; from_fate({id, _, "bool"}, B) when is_boolean(B) -> {bool, [], B}; from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S}; from_fate({app_t, _, {id, _, "list"}, [Type]}, List) when is_list(List) -> @@ -105,9 +113,23 @@ from_fate({variant_t, Cons}, {variant, Ar, Tag, Args}) from_fate(ConType, ArgList); _ -> throw(cannot_translate_to_sophia) end; +from_fate({constr_t, _, Con, []}, []) -> Con; from_fate({constr_t, _, Con, Types}, Args) when length(Types) == length(Args) -> {app, [], Con, [ from_fate(Type, Arg) || {Type, Arg} <- lists:zip(Types, Args) ]}; from_fate(_Type, _Data) -> throw(cannot_translate_to_sophia). + + +make_bits(N) -> + Id = fun(F) -> {qid, [], ["Bits", F]} end, + if N < 0 -> make_bits(Id("clear"), Id("all"), 0, bnot N); + true -> make_bits(Id("set"), Id("none"), 0, N) end. + +make_bits(_Set, Zero, _I, 0) -> Zero; +make_bits(Set, Zero, I, N) when 0 == N rem 2 -> + make_bits(Set, Zero, I + 1, N div 2); +make_bits(Set, Zero, I, N) -> + {app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}. + diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index 2f419cc..46e0be9 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -29,11 +29,10 @@ calldata_test_() -> true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); false -> undefined end, - case FateExprs == undefined orelse AevmExprs == undefined of - true -> ok; - false -> - ?assertEqual(FateExprs, AevmExprs) - end + ParsedExprs = parse_args(Fun, Args), + [ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ], + [ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ], + ok end} || {ContractName, Fun, Args} <- compilable_contracts()]. calldata_aci_test_() -> @@ -53,19 +52,34 @@ calldata_aci_test_() -> true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]); false -> undefined end, - case FateExprs == undefined orelse AevmExprs == undefined of - true -> ok; - false -> - ?assertEqual(FateExprs, AevmExprs) - end + ParsedExprs = parse_args(Fun, Args), + [ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ], + [ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ], + ok end} || {ContractName, Fun, Args} <- compilable_contracts()]. +parse_args(Fun, Args) -> + [{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] = + aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"), + strip_ann(AST). + +strip_ann(T) when is_tuple(T) -> + strip_ann1(setelement(2, T, [])); +strip_ann(X) -> strip_ann1(X). + +strip_ann1({map, [], KVs}) -> + {map, [], [{strip_ann(K), strip_ann(V)} || {K, V} <- KVs]}; +strip_ann1(T) when is_tuple(T) -> + list_to_tuple(strip_ann1(tuple_to_list(T))); +strip_ann1(L) when is_list(L) -> + lists:map(fun strip_ann/1, L); +strip_ann1(X) -> X. ast_exprs(ContractString, Fun, Args, Opts) -> {ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)), {ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)), ?assert(is_list(Exprs)), - Exprs. + strip_ann(Exprs). check_errors(Expect, ErrorString) -> %% This removes the final single \n as well. @@ -85,7 +99,9 @@ compilable_contracts() -> {"maps", "init", []}, {"funargs", "menot", ["false"]}, {"funargs", "append", ["[\"false\", \" is\", \" not\", \" true\"]"]}, - %% TODO {"funargs", "bitsum", ["Bits.all"]}, + {"funargs", "bitsum", ["Bits.all"]}, + {"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test + {"funargs", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]}, {"funargs", "read", ["{label = \"question 1\", result = 4}"]}, {"funargs", "sjutton", ["#0011012003100011012003100011012003"]}, {"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940" -- 2.30.2 From 13b7bde44ba0eb8d27de5b3ccb1381f6764b951e Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Mon, 18 Nov 2019 11:55:04 +0100 Subject: [PATCH 03/13] Prefix format annotation for negative numbers --- src/aeso_vm_decode.erl | 4 ++-- test/aeso_abi_tests.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aeso_vm_decode.erl b/src/aeso_vm_decode.erl index 827aed4..d25512e 100644 --- a/src/aeso_vm_decode.erl +++ b/src/aeso_vm_decode.erl @@ -20,7 +20,7 @@ from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(or from_aevm(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N); from_aevm(word, {id, _, "int"}, N0) -> <> = <>, - if N < 0 -> {app, [], {'-', []}, [{int, [], -N}]}; + if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]}; true -> {int, [], N} end; from_aevm(word, {id, _, "bits"}, N0) -> <> = <>, @@ -78,7 +78,7 @@ from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin}; from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N); from_fate({id, _, "int"}, N) when is_integer(N) -> - if N < 0 -> {app, [], {'-', []}, [{int, [], -N}]}; + if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]}; true -> {int, [], N} end; from_fate({id, _, "bool"}, B) when is_boolean(B) -> {bool, [], B}; from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S}; diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index ce04b33..6f1fb02 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -62,7 +62,7 @@ encode_decode_sophia_test() -> Other -> Other end end, ok = Check("int", "42"), - ok = Check("int", "-42"), + ok = Check("int", "- 42"), ok = Check("bool", "true"), ok = Check("bool", "false"), ok = Check("string", "\"Hello\""), -- 2.30.2 From 5c5d3c60ef754612abdeb149d2488e62c252223b Mon Sep 17 00:00:00 2001 From: skkw Date: Mon, 18 Nov 2019 16:25:47 +0100 Subject: [PATCH 04/13] changed type of client ttl to int option --- rebar.config | 2 +- rebar.lock | 2 +- src/aeso_ast_infer_types.erl | 2 +- test/contracts/aens.aes | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index 87edb36..9342440 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,"38f4f0e"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"b040dcc"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index f4e93cb..0e0b07b 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"38f4f0ecd75db592f596b9687c37945d4fc807d7"}}, + {ref,"b040dccdefb0713198986264bc2d19d3fb84398d"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index db5b39a..e585ac5 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -469,7 +469,7 @@ global_env() -> {"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(TTL), Option(Map(String, Pointee))], Unit)}]) + {"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)}]) , types = MkDefs([{"pointee", 0}]) }, MapScope = #scope diff --git a/test/contracts/aens.aes b/test/contracts/aens.aes index cb34e82..c342170 100644 --- a/test/contracts/aens.aes +++ b/test/contracts/aens.aes @@ -37,14 +37,14 @@ contract AENSTest = stateful entrypoint update(owner : address, name : string, ttl : option(Chain.ttl), - client_ttl : option(Chain.ttl), + client_ttl : option(int), pointers : option(map(string, AENS.pointee))) : unit = AENS.update(owner, name, ttl, client_ttl, pointers) stateful entrypoint signedUpdate(owner : address, name : string, ttl : option(Chain.ttl), - client_ttl : option(Chain.ttl), + client_ttl : option(int), pointers : option(map(string, AENS.pointee)), sign : signature) : unit = AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign) -- 2.30.2 From 04445e4dee7099a187770335e7aa755da430d9d1 Mon Sep 17 00:00:00 2001 From: Tino Breddin Date: Tue, 26 Nov 2019 17:24:02 +0100 Subject: [PATCH 05/13] Add CI check to verify rebar.lock --- .circleci/config.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c192e4e..0549670 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,15 @@ executors: working_directory: ~/aesophia jobs: + verify_rebar_lock: + executor: aebuilder + steps: + - checkout + - run: + name: Ensure lock file is up-to-date + command: | + ./rebar3 upgrade + git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1) build: executor: aebuilder steps: -- 2.30.2 From f5df2c1a5fbbe7f378a7bb545cb8286a2977c46b Mon Sep 17 00:00:00 2001 From: Tino Breddin Date: Tue, 26 Nov 2019 17:27:26 +0100 Subject: [PATCH 06/13] Add CI workflow --- .circleci/config.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0549670..88ad945 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,3 +44,10 @@ jobs: - _build/default/rebar3_20.3.8_plt - store_artifacts: path: _build/test/logs + +workflows: + version: 2 + build_test: + jobs: + - build + - verify_rebar_lock -- 2.30.2 From 1c24a700dc6617f921a7940a5049a8ea74b33ae9 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Wed, 27 Nov 2019 10:41:14 +0100 Subject: [PATCH 07/13] Add BLS12-381 operations (to FATE) --- priv/stdlib/BLS12_381.aes | 68 +++++++++++++++++++++++++++++++ rebar.config | 2 +- rebar.lock | 2 +- src/aeso_ast_infer_types.erl | 45 +++++++++++++++++++- src/aeso_ast_to_fcode.erl | 27 ++++++++++-- src/aeso_fcode_to_fate.erl | 50 ++++++++++++++++++++++- test/aeso_compiler_tests.erl | 5 ++- test/contracts/pairing_crypto.aes | 30 ++++++++++++++ 8 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 priv/stdlib/BLS12_381.aes create mode 100644 test/contracts/pairing_crypto.aes diff --git a/priv/stdlib/BLS12_381.aes b/priv/stdlib/BLS12_381.aes new file mode 100644 index 0000000..4bc27ff --- /dev/null +++ b/priv/stdlib/BLS12_381.aes @@ -0,0 +1,68 @@ +namespace BLS12_381 = + type fr = MCL_BLS12_381.fr + type fp = MCL_BLS12_381.fp + record fp2 = { x1 : fp, x2 : fp } + record g1 = { x : fp, y : fp, z : fp } + record g2 = { x : fp2, y : fp2, z : fp2 } + record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, + x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp } + + function pairing_check(xs : list(g1), ys : list(g2)) = + switch((xs, ys)) + ([], []) => true + (x :: xs, y :: ys) => pairing_check_(pairing(x, y), xs, ys) + + function pairing_check_(acc : gt, xs : list(g1), ys : list(g2)) = + switch((xs, ys)) + ([], []) => gt_is_one(acc) + (x :: xs, y :: ys) => + pairing_check_(gt_mul(acc, pairing(x, y)), xs, ys) + + function int_to_fr(x : int) = MCL_BLS12_381.int_to_fr(x) + function int_to_fp(x : int) = MCL_BLS12_381.int_to_fp(x) + function fr_to_int(x : fr) = MCL_BLS12_381.fr_to_int(x) + function fp_to_int(x : fp) = MCL_BLS12_381.fp_to_int(x) + + function mk_g1(x : int, y : int, z : int) : g1 = + { x = int_to_fp(x), y = int_to_fp(y), z = int_to_fp(z) } + + function mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 = + { x = {x1 = int_to_fp(x1), x2 = int_to_fp(x2)}, + y = {x1 = int_to_fp(y1), x2 = int_to_fp(y2)}, + z = {x1 = int_to_fp(z1), x2 = int_to_fp(z2)} } + + function pack_g1(t) = switch(t) + (x, y, z) => {x = x, y = y, z = z} : g1 + function pack_g2(t) = switch(t) + ((x1, x2), (y1, y2), (z1, z2)) => + {x = {x1 = x1, x2 = x2}, y = {x1 = y1, x2 = y2}, z = {x1 = z1, x2 = z2}} : g2 + function pack_gt(t) = switch(t) + (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) => + {x1 = x1, x2 = x2, x3 = x3, x4 = x4, x5 = x5, x6 = x6, + x7 = x7, x8 = x8, x9 = x9, x10 = x10, x11 = x11, x12 = x12} : gt + + function g1_neg(p : g1) = pack_g1(MCL_BLS12_381.g1_neg((p.x, p.y, p.z))) + function g1_norm(p : g1) = pack_g1(MCL_BLS12_381.g1_norm((p.x, p.y, p.z))) + function g1_valid(p : g1) = MCL_BLS12_381.g1_valid((p.x, p.y, p.z)) + function g1_is_zero(p : g1) = MCL_BLS12_381.g1_is_zero((p.x, p.y, p.z)) + function g1_add(p : g1, q : g1) = pack_g1(MCL_BLS12_381.g1_add((p.x, p.y, p.z), (q.x, q.y, q.z))) + function g1_mul(k : fr, p : g1) = pack_g1(MCL_BLS12_381.g1_mul(k, (p.x, p.y, p.z))) + + function g2_neg(p : g2) = pack_g2(MCL_BLS12_381.g2_neg(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))) + function g2_norm(p : g2) = pack_g2(MCL_BLS12_381.g2_norm(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))) + function g2_valid(p : g2) = MCL_BLS12_381.g2_valid(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))) + function g2_is_zero(p : g2) = MCL_BLS12_381.g2_is_zero(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))) + function g2_add(p : g2, q : g2) = pack_g2(MCL_BLS12_381.g2_add(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)), + ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2)))) + function g2_mul(k : fr, p : g2) = pack_g2(MCL_BLS12_381.g2_mul(k, ((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))) + + function gt_inv(p : gt) = pack_gt(MCL_BLS12_381.gt_inv((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12))) + function gt_add(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_add((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), + (q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12))) + function gt_mul(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_mul((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), + (q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12))) + function gt_pow(p : gt, k : fr) = pack_gt(MCL_BLS12_381.gt_pow((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), k)) + function gt_is_one(p : gt) = MCL_BLS12_381.gt_is_one((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)) + function pairing(p : g1, q : g2) = pack_gt(MCL_BLS12_381.pairing((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2)))) + function miller_loop(p : g1, q : g2) = pack_gt(MCL_BLS12_381.miller_loop((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2)))) + function final_exp(p : gt) = pack_gt(MCL_BLS12_381.final_exp((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12))) diff --git a/rebar.config b/rebar.config index 5360196..aa6dcc1 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,"b040dcc"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"e4b09d7"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index 0e0b07b..0395056 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"b040dccdefb0713198986264bc2d19d3fb84398d"}}, + {ref,"e4b09d7c5c85030636b3b88df7dd9f89f3d1e2cb"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index e585ac5..d290f25 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -390,6 +390,12 @@ global_env() -> SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end, TTL = {qid, Ann, ["Chain", "ttl"]}, Pointee = {qid, Ann, ["AENS", "pointee"]}, + 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)}, Fee = Int, [A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]), @@ -493,6 +499,40 @@ global_env() -> {"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( @@ -547,12 +587,15 @@ global_env() -> , ["Map"] => MapScope , ["Auth"] => AuthScope , ["Crypto"] => CryptoScope + , ["MCL_BLS12_381"] => MCL_BLS12_381_Scope , ["String"] => StringScope , ["Bits"] => BitsScope , ["Bytes"] => BytesScope , ["Int"] => IntScope , ["Address"] => AddressScope - } }. + } + }. + option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}. map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}. diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 6fa163a..9b28e78 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -34,7 +34,14 @@ bits_intersection | bits_union | bits_difference | contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 | crypto_sha3 | crypto_sha256 | crypto_blake2b | - crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1. + crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 | + mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid | + mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul | + mcl_bls12_381_g2_neg | mcl_bls12_381_g2_norm | mcl_bls12_381_g2_valid | + mcl_bls12_381_g2_is_zero | mcl_bls12_381_g2_add | mcl_bls12_381_g2_mul | + mcl_bls12_381_gt_inv | mcl_bls12_381_gt_add | mcl_bls12_381_gt_mul | mcl_bls12_381_gt_pow | + mcl_bls12_381_gt_is_one | mcl_bls12_381_pairing | mcl_bls12_381_miller_loop | mcl_bls12_381_final_exp | + mcl_bls12_381_int_to_fr | mcl_bls12_381_int_to_fp | mcl_bls12_381_fr_to_int | mcl_bls12_381_fp_to_int. -type flit() :: {int, integer()} | {string, binary()} @@ -197,6 +204,11 @@ builtins() -> {["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3}, {"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, + {["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2}, + {"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2}, + {"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1}, + {"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1}, + {"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]}, {["Auth"], [{"tx_hash", none}]}, {["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, {["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2}, @@ -229,7 +241,9 @@ init_type_env() -> ["map"] => ?type(K, V, {map, K, V}), ["option"] => ?type(T, {variant, [[], [T]]}), ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}), - ["AENS", "pointee"] => ?type({variant, [[address], [address], [address]]}) + ["AENS", "pointee"] => ?type({variant, [[address], [address], [address]]}), + ["MCL_BLS12_381", "fr"] => ?type({bytes, 32}), + ["MCL_BLS12_381", "fp"] => ?type({bytes, 48}) }. is_no_code(Env) -> @@ -910,7 +924,14 @@ op_builtins() -> bits_difference, int_to_str, address_to_str, crypto_verify_sig, address_to_contract, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, - crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1 + crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1, + mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid, + mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul, + mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid, + mcl_bls12_381_g2_is_zero, mcl_bls12_381_g2_add, mcl_bls12_381_g2_mul, + mcl_bls12_381_gt_inv, mcl_bls12_381_gt_add, mcl_bls12_381_gt_mul, mcl_bls12_381_gt_pow, + mcl_bls12_381_gt_is_one, mcl_bls12_381_pairing, mcl_bls12_381_miller_loop, mcl_bls12_381_final_exp, + mcl_bls12_381_int_to_fr, mcl_bls12_381_int_to_fp, mcl_bls12_381_fr_to_int, mcl_bls12_381_fp_to_int ]. builtin_to_fcode(require, [Cond, Msg]) -> diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 411f26b..46f827d 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -570,7 +570,31 @@ op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a); -op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a). +op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); +op_to_scode(mcl_bls12_381_g1_neg) -> aeb_fate_ops:bls12_381_g1_neg(?a, ?a); +op_to_scode(mcl_bls12_381_g1_norm) -> aeb_fate_ops:bls12_381_g1_norm(?a, ?a); +op_to_scode(mcl_bls12_381_g1_valid) -> aeb_fate_ops:bls12_381_g1_valid(?a, ?a); +op_to_scode(mcl_bls12_381_g1_is_zero) -> aeb_fate_ops:bls12_381_g1_is_zero(?a, ?a); +op_to_scode(mcl_bls12_381_g1_add) -> aeb_fate_ops:bls12_381_g1_add(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_g1_mul) -> aeb_fate_ops:bls12_381_g1_mul(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_g2_neg) -> aeb_fate_ops:bls12_381_g2_neg(?a, ?a); +op_to_scode(mcl_bls12_381_g2_norm) -> aeb_fate_ops:bls12_381_g2_norm(?a, ?a); +op_to_scode(mcl_bls12_381_g2_valid) -> aeb_fate_ops:bls12_381_g2_valid(?a, ?a); +op_to_scode(mcl_bls12_381_g2_is_zero) -> aeb_fate_ops:bls12_381_g2_is_zero(?a, ?a); +op_to_scode(mcl_bls12_381_g2_add) -> aeb_fate_ops:bls12_381_g2_add(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_g2_mul) -> aeb_fate_ops:bls12_381_g2_mul(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_gt_inv) -> aeb_fate_ops:bls12_381_gt_inv(?a, ?a); +op_to_scode(mcl_bls12_381_gt_add) -> aeb_fate_ops:bls12_381_gt_add(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_gt_mul) -> aeb_fate_ops:bls12_381_gt_mul(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_gt_pow) -> aeb_fate_ops:bls12_381_gt_pow(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_gt_is_one) -> aeb_fate_ops:bls12_381_gt_is_one(?a, ?a); +op_to_scode(mcl_bls12_381_pairing) -> aeb_fate_ops:bls12_381_pairing(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_miller_loop) -> aeb_fate_ops:bls12_381_miller_loop(?a, ?a, ?a); +op_to_scode(mcl_bls12_381_final_exp) -> aeb_fate_ops:bls12_381_final_exp(?a, ?a); +op_to_scode(mcl_bls12_381_int_to_fr) -> aeb_fate_ops:bls12_381_int_to_fr(?a, ?a); +op_to_scode(mcl_bls12_381_int_to_fp) -> aeb_fate_ops:bls12_381_int_to_fp(?a, ?a); +op_to_scode(mcl_bls12_381_fr_to_int) -> aeb_fate_ops:bls12_381_fr_to_int(?a, ?a); +op_to_scode(mcl_bls12_381_fp_to_int) -> aeb_fate_ops:bls12_381_fp_to_int(?a, ?a). %% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% easier, and specialize to PUSH (which is cheaper) at the end. @@ -843,6 +867,30 @@ attributes(I) -> {'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); {'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]); {'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]); + {'BLS12_381_G1_NEG', A, B} -> Pure(A, [B]); + {'BLS12_381_G1_NORM', A, B} -> Pure(A, [B]); + {'BLS12_381_G1_VALID', A, B} -> Pure(A, [B]); + {'BLS12_381_G1_IS_ZERO', A, B} -> Pure(A, [B]); + {'BLS12_381_G1_ADD', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_G1_MUL', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_G2_NEG', A, B} -> Pure(A, [B]); + {'BLS12_381_G2_NORM', A, B} -> Pure(A, [B]); + {'BLS12_381_G2_VALID', A, B} -> Pure(A, [B]); + {'BLS12_381_G2_IS_ZERO', A, B} -> Pure(A, [B]); + {'BLS12_381_G2_ADD', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_G2_MUL', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_GT_INV', A, B} -> Pure(A, [B]); + {'BLS12_381_GT_ADD', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_GT_MUL', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_GT_POW', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_GT_IS_ONE', A, B} -> Pure(A, [B]); + {'BLS12_381_PAIRING', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_MILLER_LOOP', A, B, C} -> Pure(A, [B, C]); + {'BLS12_381_FINAL_EXP', A, B} -> Pure(A, [B]); + {'BLS12_381_INT_TO_FR', A, B} -> Pure(A, [B]); + {'BLS12_381_INT_TO_FP', A, B} -> Pure(A, [B]); + {'BLS12_381_FR_TO_INT', A, B} -> Pure(A, [B]); + {'BLS12_381_FP_TO_INT', A, B} -> Pure(A, [B]); {'ABORT', A} -> Impure(pc, A); {'EXIT', A} -> Impure(pc, A); 'NOP' -> Pure(none, []) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 50130e6..5506da9 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -161,11 +161,12 @@ compilable_contracts() -> "list_comp", "payable", "unapplied_builtins", - "underscore_number_literals" + "underscore_number_literals", + "pairing_crypto" ]. not_yet_compilable(fate) -> []; -not_yet_compilable(aevm) -> []. +not_yet_compilable(aevm) -> ["pairing_crypto"]. %% Contracts that should produce type errors diff --git a/test/contracts/pairing_crypto.aes b/test/contracts/pairing_crypto.aes new file mode 100644 index 0000000..93f4a24 --- /dev/null +++ b/test/contracts/pairing_crypto.aes @@ -0,0 +1,30 @@ +include "BLS12_381.aes" + +contract GrothVerify = + type fr = BLS12_381.fr + type g1 = BLS12_381.g1 + type g2 = BLS12_381.g2 + + record proof = { a : g1, b : g2, c : g1 } + record verify_key = { a : g1, b : g2, c : g2, d : g2, ic : list(g1) } + + record state = { vk : verify_key } + + entrypoint init(vk0 : verify_key) = {vk = vk0} + + entrypoint verify_proof(p : proof, input : list(fr)) = + let vk = state.vk + let vk_x = calc_vk_x(vk.ic, input) + + BLS12_381.pairing_check([BLS12_381.g1_neg(p.a), vk.a, vk_x, p.c], + [p.b, vk.b, vk.c, vk.d]) + + function calc_vk_x(ics : list(g1), xs : list(fr)) = + switch(ics) + (ic :: ics) => calc_vk_x_(ic, ics, xs) + + function calc_vk_x_(vk_x : g1, ics : list(g1), xs : list(fr)) = + switch((ics, xs)) + ([], []) => vk_x + (ic :: ics, x :: xs) => calc_vk_x_(BLS12_381.g1_add(vk_x, BLS12_381.g1_mul(x, ic)), ics, xs) + -- 2.30.2 From 760d2841d1bb5664ce75d9757ca18682901d00eb Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Mon, 3 Feb 2020 12:22:34 +0100 Subject: [PATCH 08/13] From Iris (with AENS.update) AENS.resolve is not Pure --- src/aeso_fcode_to_fate.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index ddb15eb..66c2a1a 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -880,7 +880,7 @@ attributes(I) -> {'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]); {'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]); {'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]); - {'AENS_RESOLVE', A, B, C, D} -> Pure(A, [B, C, D]); + {'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]); {'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]); {'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); {'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); -- 2.30.2 From db01e237c1893fa237b7d2cc265b79707a014ad0 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Mon, 3 Feb 2020 12:24:27 +0100 Subject: [PATCH 09/13] Add Oracle.expiry --- src/aeso_ast_infer_types.erl | 1 + src/aeso_ast_to_fcode.erl | 2 +- src/aeso_fcode_to_fate.erl | 3 +++ test/contracts/channel_on_chain_contract_oracle.aes | 7 +++++-- test/contracts/unapplied_builtins.aes | 1 + 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index c2d1498..5ee5641 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -459,6 +459,7 @@ global_env() -> 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)}, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 3f360d0..bd4c539 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -204,7 +204,7 @@ builtins() -> {["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}, + {["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2}, {"respond", 4}, {"extend", 3}, {"get_answer", 2}, {"check", 1}, {"check_query", 2}]}, {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4}, diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 66c2a1a..181c945 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -498,6 +498,8 @@ builtin_to_scode(_Env, call_gas_left, []) -> [aeb_fate_ops:gas(?a)]; builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) -> call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args); +builtin_to_scode(Env, oracle_expiry, [_Oracle] = Args) -> + call_to_scode(Env, aeb_fate_ops:oracle_expiry(?a, ?a), Args); builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) -> call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args); builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) -> @@ -880,6 +882,7 @@ attributes(I) -> {'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]); {'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]); {'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]); + {'ORACLE_EXPIRY', A, B} -> Impure(A, [B]); {'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]); {'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]); {'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); diff --git a/test/contracts/channel_on_chain_contract_oracle.aes b/test/contracts/channel_on_chain_contract_oracle.aes index d62ab41..de681aa 100644 --- a/test/contracts/channel_on_chain_contract_oracle.aes +++ b/test/contracts/channel_on_chain_contract_oracle.aes @@ -17,7 +17,7 @@ contract ChannelOnChainContractOracle = bets = {} } - public stateful function place_bet(answer: string) = + public stateful function place_bet(answer: string) = switch(Map.lookup(answer, state.bets)) None => put(state{ bets = state.bets{[answer] = Call.caller}}) @@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle = Some(_value) => "bet_already_taken" + public function expiry() = + Oracle.expiry(state.oracle) + public function query_fee() = Oracle.query_fee(state.oracle) @@ -35,7 +38,7 @@ contract ChannelOnChainContractOracle = switch(Oracle.get_answer(state.oracle, q)) None => "no response" - Some(result) => + Some(result) => if(state.question == Oracle.get_question(state.oracle, q)) switch(Map.lookup(result, state.bets)) None => diff --git a/test/contracts/unapplied_builtins.aes b/test/contracts/unapplied_builtins.aes index 6055a87..3d3bb39 100644 --- a/test/contracts/unapplied_builtins.aes +++ b/test/contracts/unapplied_builtins.aes @@ -21,6 +21,7 @@ contract UnappliedBuiltins = function b_abort() = abort function b_require() = require function oracle_query_fee() = Oracle.query_fee + function oracle_expiry() = Oracle.expiry stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _ function oracle_get_question() = Oracle.get_question : (o, _) => _ function oracle_get_answer() = Oracle.get_answer : (o, _) => _ -- 2.30.2 From c3426f0e65aa91d5f3f9b3fe0ea2f82895cfeca4 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Mon, 3 Feb 2020 12:52:00 +0100 Subject: [PATCH 10/13] Add AENS.lookup Also move Pointee-constructors inside AENS namespace. --- src/aeso_ast_infer_types.erl | 18 ++++++++++++------ src/aeso_ast_to_fcode.erl | 21 ++++++++++++--------- src/aeso_fcode_to_fate.erl | 3 +++ test/aeso_compiler_tests.erl | 3 ++- test/contracts/aens_update.aes | 17 +++++++++++++++++ 5 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 test/contracts/aens_update.aes diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 5ee5641..802614c 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -390,6 +390,7 @@ global_env() -> 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]}, @@ -410,10 +411,6 @@ global_env() -> %% TTL constructors {"RelativeTTL", Fun1(Int, TTL)}, {"FixedTTL", Fun1(Int, TTL)}, - %% AENS pointee constructors - {"AccountPointee", Fun1(Address, Pointee)}, - {"OraclePointee", Fun1(Address, Pointee)}, - {"ContractPointee", Fun1(Address, Pointee)}, %% Abort {"abort", Fun1(String, A)}, {"require", Fun([Bool, String], Unit)}]) @@ -476,8 +473,17 @@ global_env() -> {"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)}]) - , types = MkDefs([{"pointee", 0}]) }, + {"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( diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index bd4c539..d4c1f0d 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -181,13 +181,15 @@ init_env(Options) -> #{ type_env => init_type_env(), fun_env => #{}, builtins => builtins(), - con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, - ["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, - ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, - ["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }, - ["AccountPointee"] => #con_tag{ tag = 0, arities = [1, 1, 1] }, - ["OraclePointee"] => #con_tag{ tag = 1, arities = [1, 1, 1] }, - ["ContractPointee"] => #con_tag{ tag = 2, arities = [1, 1, 1] } + con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, + ["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, + ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, + ["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }, + ["AENS", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1] }, + ["AENS", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1] }, + ["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] }, + ["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] }, + ["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] } }, options => Options, functions => #{} }. @@ -208,7 +210,7 @@ builtins() -> {"respond", 4}, {"extend", 3}, {"get_answer", 2}, {"check", 1}, {"check_query", 2}]}, {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4}, - {"revoke", 3}, {"update", 6}]}, + {"revoke", 3}, {"update", 6}, {"lookup", 1}]}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, {["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3}, @@ -253,7 +255,8 @@ init_type_env() -> ["map"] => ?type(K, V, {map, K, V}), ["option"] => ?type(T, {variant, [[], [T]]}), ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}), - ["AENS", "pointee"] => ?type({variant, [[address], [address], [address]]}), + ["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}), + ["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}), ["MCL_BLS12_381", "fr"] => ?type({bytes, 32}), ["MCL_BLS12_381", "fp"] => ?type({bytes, 48}) }. diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 181c945..70cac15 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -541,6 +541,8 @@ builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) -> builtin_to_scode(Env, aens_update, [_Sign, _Account, _NameString, _TTL, _ClientTTL, _Pointers] = Args) -> call_to_scode(Env, [aeb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a), tuple(0)], Args); +builtin_to_scode(Env, aens_lookup, [_Name] = Args) -> + call_to_scode(Env, aeb_fate_ops:aens_lookup(?a, ?a), Args); builtin_to_scode(_Env, auth_tx_hash, []) -> [aeb_fate_ops:auth_tx_hash(?a)]. @@ -889,6 +891,7 @@ attributes(I) -> {'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); {'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]); {'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]); + {'AENS_LOOKUP', A, B} -> Impure(A, [B]); {'BLS12_381_G1_NEG', A, B} -> Pure(A, [B]); {'BLS12_381_G1_NORM', A, B} -> Pure(A, [B]); {'BLS12_381_G1_VALID', A, B} -> Pure(A, [B]); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index ea27c67..2e610b0 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -154,6 +154,7 @@ compilable_contracts() -> "bytes_to_x", "bytes_concat", "aens", + "aens_update", "tuple_match", "cyclic_include", "stdlib_include", @@ -170,7 +171,7 @@ compilable_contracts() -> ]. not_yet_compilable(fate) -> []; -not_yet_compilable(aevm) -> ["pairing_crypto"]. +not_yet_compilable(aevm) -> ["pairing_crypto", "aens_update"]. %% Contracts that should produce type errors diff --git a/test/contracts/aens_update.aes b/test/contracts/aens_update.aes new file mode 100644 index 0000000..5d65229 --- /dev/null +++ b/test/contracts/aens_update.aes @@ -0,0 +1,17 @@ +contract AENSUpdate = + stateful entrypoint update_name(owner : address, name : string) = + let p1 : AENS.pointee = AENS.AccountPt(Call.caller) + let p2 : AENS.pointee = AENS.OraclePt(Call.caller) + let p3 : AENS.pointee = AENS.ContractPt(Call.caller) + let p4 : AENS.pointee = AENS.ChannelPt(Call.caller) + AENS.update(owner, name, None, None, + Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2, + ["contract_pubkey"] = p3, ["misc"] = p4 })) + + entrypoint get_ttl(name : string) = + switch(AENS.lookup(name)) + Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl + + entrypoint expiry(o : oracle(int, int)) : int = + Oracle.expiry(o) + -- 2.30.2 From bb79e7dd89c3cd0c05bae33185967c7579a1891c Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Mon, 3 Feb 2020 13:32:16 +0100 Subject: [PATCH 11/13] Update aebytecode reference --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index ba09c46..eb173a0 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,"e4b09d7"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"8a9c9de"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index 0395056..9b84603 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"e4b09d7c5c85030636b3b88df7dd9f89f3d1e2cb"}}, + {ref,"8a9c9dec956b1d2322b38490b2759e53a91affab"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", -- 2.30.2 From 13bc8212117d2cf3812eeff7df8a349f5990dc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Fri, 7 Feb 2020 19:51:12 +0100 Subject: [PATCH 12/13] Optimize stdlib (#215) --- priv/stdlib/List.aes | 155 +++++++++++++++++++++-------------------- priv/stdlib/Option.aes | 20 +++--- 2 files changed, 91 insertions(+), 84 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 64c4fd5..98fd4fe 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -19,18 +19,28 @@ namespace List = [x] => Some(x) _::t => last(t) + function drop_last(l : list('a)) : option(list('a)) = switch(l) + [] => None + _ => Some(drop_last_unsafe(l)) + + function drop_last_unsafe(l : list('a)) : list('a) = switch(l) + [_] => [] + h::t => h::drop_last_unsafe(t) + [] => abort("drop_last_unsafe: list empty") + function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) [] => None h::t => if(p(h)) Some(h) else find(p, t) - function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) + function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0) private function find_indices_( p : 'a => bool , l : list('a) , n : int - , acc : list(int) ) : list(int) = switch(l) - [] => reverse(acc) - h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) + [] => [] + h::t => + let rest = find_indices_(p, t, n+1) + if(p(h)) n::rest else rest function nth(n : int, l : list('a)) : option('a) = switch(l) @@ -52,39 +62,40 @@ namespace List = function from_to(a : int, b : int) : list(int) = [a..b] - function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) - private function from_to_step_(a, b, s, acc) = - if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc) + function from_to_step(a : int, b : int, s : int) : list(int) = + from_to_step_(a, b - (b-a) mod s, s, []) + private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) = + if(b < a) acc + else from_to_step_(a, b - s, s, b::acc) + /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ function replace_at(n : int, e : 'a, l : list('a)) : list('a) = - if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, []) - private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = + if(n<0) abort("insert_at underflow") else replace_at_(n, e, l) + private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) = switch(l) [] => abort("replace_at overflow") - h::t => if (n == 0) reverse(e::acc) ++ t - else replace_at_(n-1, e, t, h::acc) + h::t => if (n == 0) e::t + else h::replace_at_(n-1, e, t) /* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ function insert_at(n : int, e : 'a, l : list('a)) : list('a) = - if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, []) - private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = - if (n == 0) reverse(e::acc) ++ l + if(n<0) abort("insert_at underflow") else insert_at_(n, e, l) + private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) = + if (n == 0) e::l else switch(l) [] => abort("insert_at overflow") - h::t => insert_at_(n-1, e, t, h::acc) + h::t => h::insert_at_(n-1, e, t) function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = - insert_by_(cmp, x, l, []) - private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(x::acc) + [] => [x] h::t => if(cmp(x, h)) // x < h - reverse(acc) ++ (x::l) + x::l else - insert_by_(cmp, x, t, h::acc) + h::insert_by(cmp, x, t) function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) @@ -102,43 +113,46 @@ namespace List = f(e) foreach(l', f) - function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) + function reverse(l : list('a)) : list('a) = reverse_(l, []) + private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l) + [] => acc + h::t => reverse_(t, h::acc) - function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, []) - private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) - h::t => map_(f, t, f(h)::acc) + function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l) + [] => [] + h::t => f(h)::map(f, t) function flat_map(f : 'a => list('b), l : list('a)) : list('b) = ListInternal.flat_map(f, l) - function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) - private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => filter_(p, t, if(p(h)) h::acc else acc) + function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l) + [] => [] + h::t => + let rest = filter(p, t) + if(p(h)) h::rest else rest - /* Take `n` first elements */ + /* Take up to `n` first elements */ function take(n : int, l : list('a)) : list('a) = - if(n < 0) abort("Take negative number of elements") else take_(n, l, []) - private function take_(n : int, l : list('a), acc : list('a)) : list('a) = - if(n == 0) reverse(acc) + if(n < 0) abort("Take negative number of elements") else take_(n, l) + private function take_(n : int, l : list('a)) : list('a) = + if(n == 0) [] else switch(l) - [] => reverse(acc) - h::t => take_(n-1, t, h::acc) + [] => [] + h::t => h::take_(n-1, t) - /* Drop `n` first elements */ + /* Drop up to `n` first elements */ function drop(n : int, l : list('a)) : list('a) = - if(n < 0) abort("Drop negative number of elements") - elif (n == 0) l + if(n < 0) abort("Drop negative number of elements") else drop_(n, l) + private function drop_(n : int, l : list('a)) : list('a) = + if (n == 0) l else switch(l) [] => [] - h::t => drop(n-1, t) + h::t => drop_(n-1, t) /* Get the longest prefix of a list in which every element matches predicate `p` */ - function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) - private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) + function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) + [] => [] + h::t => if(p(h)) h::take_while(p, t) else [] /* Drop elements from `l` until `p` holds */ function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) @@ -146,17 +160,15 @@ namespace List = h::t => if(p(h)) drop_while(p, t) else l /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ - function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) - private function partition_( p : 'a => bool - , l : list('a) - , acc_t : list('a) - , acc_f : list('a) - ) : (list('a) * list('a)) = switch(l) - [] => (reverse(acc_t), reverse(acc_f)) - h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l) + [] => ([], []) + h::t => + let (l, r) = partition(p, t) + if(p(h)) (h::l, r) else (l, h::r) - - function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) + function flatten(l : list(list('a))) : list('a) = switch(l) + [] => [] + h::t => h ++ flatten(t) function all(p : 'a => bool, l : list('a)) : bool = switch(l) [] => true @@ -172,25 +184,21 @@ namespace List = /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ - function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) - private function zip_with_( f : ('a, 'b) => 'c + private function zip_with( f : ('a, 'b) => 'c , l1 : list('a) , l2 : list('b) - , acc : list('c) ) : list('c) = switch ((l1, l2)) - (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) - _ => reverse(acc) + (h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2) + _ => [] /* Zips two lists into list of pairs. Drops longer tail. */ function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) - function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) - private function unzip_( l : list('a * 'b) - , acc_l : list('a) - , acc_r : list('b) - ) : (list('a) * list('b)) = switch(l) - [] => (reverse(acc_l), reverse(acc_r)) - (left, right)::t => unzip_(t, left::acc_l, right::acc_r) + function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l) + [] => ([], []) + (h1, h2)::t => + let (t1, t2) = unzip(t) + (h1::t1, h2::t2) // TODO: Improve? @@ -200,15 +208,14 @@ namespace List = (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) - function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) - private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - [e] => reverse(e::acc) - h::t => intersperse_(delim, t, delim::h::acc) + function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l) + [] => [] + [e] => [e] + h::t => h::delim::intersperse(delim, t) - function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) - private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) - [] => reverse(acc) - h::t => enumerate_(t, n + 1, (n, h)::acc) + function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0) + private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l) + [] => [] + h::t => (n, h)::enumerate_(t, n + 1) diff --git a/priv/stdlib/Option.aes b/priv/stdlib/Option.aes index 6ebf98c..9647230 100644 --- a/priv/stdlib/Option.aes +++ b/priv/stdlib/Option.aes @@ -53,17 +53,17 @@ namespace Option = None => [] Some(x) => [x] - function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) - private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) - [] => List.reverse(acc) - None::t => filter_options_(t, acc) - Some(x)::t => filter_options_(t, x::acc) + function filter_options(l : list(option('a))) : list('a) = switch(l) + [] => [] + None::t => filter_options(t) + Some(x)::t => x::filter_options(t) - function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) - private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) - [] => Some(List.reverse(acc)) - None::t => None - Some(x)::t => seq_options_(t, x::acc) + function seq_options(l : list (option('a))) : option (list('a)) = switch(l) + [] => Some([]) + None::_ => None + Some(x)::t => switch(seq_options(t)) + None => None + Some(st) => Some(x::st) function choose(o1 : option('a), o2 : option('a)) : option('a) = -- 2.30.2 From b2c4e8e4a6d5526bc4adaf42e77da202d51dcae2 Mon Sep 17 00:00:00 2001 From: radrow Date: Fri, 7 Feb 2020 21:55:59 +0100 Subject: [PATCH 13/13] Add Frac Fix bugs in Frac Added optimizer --- priv/stdlib/Frac.aes | 171 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 priv/stdlib/Frac.aes diff --git a/priv/stdlib/Frac.aes b/priv/stdlib/Frac.aes new file mode 100644 index 0000000..27ad531 --- /dev/null +++ b/priv/stdlib/Frac.aes @@ -0,0 +1,171 @@ +namespace Frac = + + private function gcd(a : int, b : int) = + if (b == 0) a else gcd(b, a mod b) + + private function abs_int(a : int) = if (a < 0) -a else a + + datatype frac = Pos(int, int) | Zero | Neg(int, int) + + // Checks if internal representation is correct. Numerator and denominator must be positive. + function is_sane(f : frac) : bool = switch(f) + Pos(n, d) => n > 0 && d > 0 + Zero => true + Neg(n, d) => n > 0 && d > 0 + + function num(f : frac) : int = switch(f) + Pos(n, _) => n + Neg(n, _) => -n + Zero => 0 + + function den(f : frac) : int = switch(f) + Pos(_, d) => d + Neg(_, d) => d + Zero => 1 + + function to_pair(f : frac) : int * int = switch(f) + Pos(n, d) => (n, d) + Neg(n, d) => (-n, d) + Zero => (0, 1) + + function sign(f : frac) : int = switch(f) + Pos(_, _) => 1 + Neg(_, _) => -1 + Zero => 0 + + function to_str(f : frac) : string = switch(f) + Pos(n, d) => String.concat(Int.to_str(n), if (d == 1) "" else String.concat("/", Int.to_str(d))) + Neg(n, d) => String.concat("-", to_str(Pos(n, d))) + Zero => "0" + + // Reduce fraction to normal form + function simplify(f : frac) : frac = + switch(f) + Neg(n, d) => + let cd = gcd(n, d) + Neg(n / cd, d / cd) + Zero => Zero + Pos(n, d) => + let cd = gcd(n, d) + Pos(n / cd, d / cd) + + function make_frac(n : int, d : int) : frac = + if (d == 0) abort("Division by zero") + elif (n == 0) Zero + elif ((n < 0) == (d < 0)) simplify(Pos(abs_int(n), abs_int(d))) + else simplify(Neg(abs_int(n), abs_int(d))) + + function eq(a : frac, b : frac) : bool = + let na = num(a) + let nb = num(b) + let da = den(a) + let db = den(b) + (na == nb && da == db) || na * db == nb * da // they are more likely to be normalized + + function neq(a : frac, b : frac) : bool = + let na = num(a) + let nb = num(b) + let da = den(a) + let db = den(b) + (na != nb || da != db) && na * db != nb * da + + function geq(a : frac, b : frac) : bool = num(a) * den(b) >= num(b) * den(a) + + function leq(a : frac, b : frac) : bool = num(a) * den(b) =< num(b) * den(a) + + function gt(a : frac, b : frac) : bool = num(a) * den(b) > num(b) * den(a) + + function lt(a : frac, b : frac) : bool = num(a) * den(b) < num(b) * den(a) + + function min(a : frac, b : frac) : frac = if (leq(a, b)) a else b + + function max(a : frac, b : frac) : frac = if (geq(a, b)) a else b + + function abs(f : frac) : frac = switch(f) + Pos(n, d) => Pos(n, d) + Zero => Zero + Neg(n, d) => Pos(n, d) + + function from_int(n : int) : frac = + if (n > 0) Pos(n, 1) + elif (n < 0) Neg(-n, 1) + else Zero + + function floor(f : frac) : int = switch(f) + Pos(n, d) => n / d + Zero => 0 + Neg(n, d) => -(n + d - 1) / d + + function ceil(f : frac) : int = switch(f) + Pos(n, d) => (n + d - 1) / d + Zero => 0 + Neg(n, d) => -n / d + + function round_to_zero(f : frac) : int = switch(f) + Pos(n, d) => n / d + Zero => 0 + Neg(n, d) => -n / d + + function round_from_zero(f : frac) : int = switch(f) + Pos(n, d) => (n + d - 1) / d + Zero => 0 + Neg(n, d) => -(n + d - 1) / d + + // Round towards nearest integer. If two integers are in the same distance, choose the even one. + function round(f : frac) : int = + let fl = floor(f) + let cl = ceil(f) + let dif_fl = abs(sub(f, from_int(fl))) + let dif_cl = abs(sub(f, from_int(cl))) + if (gt(dif_fl, dif_cl)) cl + elif (gt(dif_cl, dif_fl)) fl + elif (fl mod 2 == 0) fl + else cl + + function add(a : frac, b : frac) : frac = + let na = num(a) + let nb = num(b) + let da = den(a) + let db = den(b) + if (da == db) make_frac(na + nb, da) + else make_frac(na * db + nb * da, da * db) + + function neg(a : frac) : frac = switch(a) + Neg(n, d) => Pos(n, d) + Zero => Zero + Pos(n, d) => Neg(n, d) + + function sub(a : frac, b : frac) : frac = add(a, neg(b)) + + function inv(a : frac) : frac = switch(a) + Neg(n, d) => Neg(d, n) + Zero => abort("Inversion of zero") + Pos(n, d) => Pos(d, n) + + function mul(a : frac, b : frac) : frac = make_frac(num(a) * num(b), den(a) * den(b)) + + function div(a : frac, b : frac) : frac = mul(a, inv(b)) + + function int_exp(b : frac, e : int) : frac = + if (sign(b) == 0 && e == 0) abort("Zero to the zero exponentation") + elif (e < 0) inv(int_exp_(b, -e)) + else int_exp_(b, e) + private function int_exp_(b : frac, e : int) = + if (e == 0) from_int(1) + elif (e == 1) b + else + let half = int_exp_(b, e / 2) + if (e mod 2 == 1) mul(mul(half, half), b) + else mul(half, half) + + // Reduces the fraction's in-memory size by dividing its components by two until the + // the error is bigger than `loss` value + function optimize(f : frac, loss : frac) : frac = + require(geq(loss, Zero), "negative loss optimize") + let s = sign(f) + mul(from_int(s), run_optimize(abs(f), loss)) + private function run_optimize(f : frac, loss : frac) : frac = + let t = make_frac((num(f) + 1) / 2, (den(f) + 1)/2) + if(gt(abs(sub(t, f)), loss)) f + elif (eq(t, f)) f + else run_optimize(t, loss) -- 2.30.2