Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd6dac2257 | |||
| b2a0c9d8be | |||
| 078af45c92 | |||
| 3271d6fba4 | |||
| 30fbcc50c5 | |||
| 27bc5474cb | |||
| efeb391805 | |||
| 15ca37342c | |||
| 8b7e4db490 | |||
| d89fd134b5 | |||
| 37dfbf78ac | |||
| 188916c61f | |||
| f395649419 | |||
| 36395b597a | |||
| 177e32c117 | |||
| 4d61ee65df | |||
| a089af555f | |||
| cd116b23d7 | |||
| fba6609c3a | |||
| e7c477d4de | |||
| a44b787735 | |||
| b6a789bbbc | |||
| ad34363673 | |||
| 257de08100 | |||
| 7ae4a98360 | |||
| 5e6e607fa4 | |||
| a69056c35e | |||
| 8cfa611b20 | |||
| e9bdd59def | |||
| 0da7376d11 | |||
| af4f2ad795 | |||
| abae4a7602 | |||
| 1cfd4c6f24 | |||
| f07a49c91d | |||
| e33e4cf2cd | |||
| 85a014958d | |||
| fadf3378b4 | |||
| 54edba3164 | |||
| fc64ca572d | |||
| 53b85ce6f4 | |||
| a7af62c089 | |||
| abc70ba288 | |||
| 6342cd6a08 | |||
| 123d1d2fa2 | |||
| b7b54b38a8 | |||
| ae3f292f03 | |||
| bcdf311096 |
@@ -19,16 +19,16 @@ jobs:
|
||||
- dialyzer-cache-v2-
|
||||
- run:
|
||||
name: Build
|
||||
command: rebar3 compile
|
||||
command: ./rebar3 compile
|
||||
- run:
|
||||
name: Static Analysis
|
||||
command: rebar3 dialyzer
|
||||
command: ./rebar3 dialyzer
|
||||
- run:
|
||||
name: Eunit
|
||||
command: rebar3 eunit
|
||||
command: ./rebar3 eunit
|
||||
- run:
|
||||
name: Common Tests
|
||||
command: rebar3 ct
|
||||
command: ./rebar3 ct
|
||||
- save_cache:
|
||||
key: dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||
paths:
|
||||
|
||||
+10
-2
@@ -3,7 +3,7 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"720510a"}}}
|
||||
{ref, "9041423"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
{tag, "2.8.0"}}}
|
||||
@@ -12,7 +12,7 @@
|
||||
{escript_incl_apps, [aesophia, aebytecode, getopt]}.
|
||||
{escript_main_app, aesophia}.
|
||||
{escript_name, aesophia}.
|
||||
{escript_emu_args, "%%! +sbtu +A0\n"}.
|
||||
{escript_emu_args, "%%! \n"}.
|
||||
{provider_hooks, [{post, [{compile, escriptize}]}]}.
|
||||
|
||||
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
|
||||
@@ -29,3 +29,11 @@
|
||||
{plt_apps, all_deps},
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "2.0.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
{extended_start_script, true}]}.
|
||||
|
||||
+11
-1
@@ -1,8 +1,17 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"720510a24de32c9bad6486f34ca7babde124bf1e"}},
|
||||
{ref,"9041423906247a7267a5a94530307b19c4490e8c"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
|
||||
1},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
|
||||
2},
|
||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},1},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
@@ -10,5 +19,6 @@
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
||||
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
|
||||
].
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
|
||||
, aeso_heap:to_binary(OutType)
|
||||
]),
|
||||
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
|
||||
{ok, Hash} = aeso_blake2:blake2b(?HASH_SIZE, Bin),
|
||||
{ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin),
|
||||
Hash.
|
||||
|
||||
-spec arg_typerep_from_function(function_name(), type_info()) ->
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
%% the code easier but don't seem to exist elsewhere.
|
||||
|
||||
-record(contract, {ann,con,decls}).
|
||||
-record(namespace, {ann,con,decls}).
|
||||
%% -record(namespace, {ann,con,decls}).
|
||||
-record(letfun, {ann,id,args,type,body}).
|
||||
-record(type_def, {ann,id,vars,typedef}).
|
||||
|
||||
|
||||
@@ -443,8 +443,13 @@ global_env() ->
|
||||
{"sha256", Fun1(A, Hash)},
|
||||
{"blake2b", Fun1(A, Hash)}]) },
|
||||
|
||||
%% Strings
|
||||
StringScope = #scope
|
||||
%% Authentication
|
||||
AuthScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"tx_hash", Option(Hash)}]) },
|
||||
|
||||
%% Strings
|
||||
StringScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
@@ -452,8 +457,8 @@ global_env() ->
|
||||
{"sha256", Fun1(String, Hash)},
|
||||
{"blake2b", Fun1(String, Hash)}]) },
|
||||
|
||||
%% Bits
|
||||
BitsScope = #scope
|
||||
%% Bits
|
||||
BitsScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"set", Fun([Bits, Int], Bits)},
|
||||
{"clear", Fun([Bits, Int], Bits)},
|
||||
@@ -465,9 +470,9 @@ global_env() ->
|
||||
{"none", Bits},
|
||||
{"all", Bits}]) },
|
||||
|
||||
%% Conversion
|
||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}]) },
|
||||
%% Conversion
|
||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}]) },
|
||||
|
||||
#env{ scopes =
|
||||
#{ [] => TopScope
|
||||
@@ -477,6 +482,7 @@ global_env() ->
|
||||
, ["Oracle"] => OracleScope
|
||||
, ["AENS"] => AENSScope
|
||||
, ["Map"] => MapScope
|
||||
, ["Auth"] => AuthScope
|
||||
, ["Crypto"] => CryptoScope
|
||||
, ["String"] => StringScope
|
||||
, ["Bits"] => BitsScope
|
||||
|
||||
@@ -182,6 +182,11 @@ ast_body(?id_app("abort", [String], _, _), Icode) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, abort} },
|
||||
args = [ast_body(String, Icode)] };
|
||||
|
||||
%% Authentication
|
||||
ast_body({qid, _, ["Auth", "tx_hash"]}, _Icode) ->
|
||||
prim_call(?PRIM_CALL_AUTH_TX_HASH, #integer{value = 0},
|
||||
[], [], aeso_icode:option_typerep(word));
|
||||
|
||||
%% Oracles
|
||||
ast_body(?qid_app(["Oracle", "register"], Args, _, ?oracle_t(QType, RType)), Icode) ->
|
||||
{Sign, [Acct, QFee, TTL]} = get_signature_arg(Args),
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
%%%=============================================================================
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net
|
||||
%%% @end
|
||||
%%%=============================================================================
|
||||
|
||||
-module(aeso_blake2).
|
||||
|
||||
-export([ blake2b/2
|
||||
, blake2b/3
|
||||
]).
|
||||
|
||||
-define(MAX_64BIT, 16#ffffffffffffffff).
|
||||
|
||||
-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}.
|
||||
blake2b(HashLen, Msg) ->
|
||||
blake2b(HashLen, Msg, <<>>).
|
||||
|
||||
-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}.
|
||||
blake2b(HashLen, Msg0, Key) ->
|
||||
%% If message should be keyed, prepend message with padded key.
|
||||
Msg = <<(pad(128, Key))/binary, Msg0/binary>>,
|
||||
|
||||
%% Set up the initial state
|
||||
Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen),
|
||||
<<H0:64, H1_7/binary>> = blake_iv(),
|
||||
H = <<(H0 bxor Init):64, H1_7/binary>>,
|
||||
|
||||
%% Perform the compression - message will be chopped into 128-byte chunks.
|
||||
State = blake2b_compress(H, Msg, 0),
|
||||
|
||||
%% Just return the requested part of the hash
|
||||
{ok, binary_part(to_little_endian(State), {0, HashLen})}.
|
||||
|
||||
blake2b_compress(H, <<Chunk:(128*8), Rest/binary>>, BCompr) when Rest /= <<>> ->
|
||||
H1 = blake2b_compress(H, <<Chunk:(128*8)>>, BCompr + 128, false),
|
||||
blake2b_compress(H1, Rest, BCompr + 128);
|
||||
blake2b_compress(H, SmallChunk, BCompr) ->
|
||||
Size = byte_size(SmallChunk),
|
||||
FillSize = (128 - Size) * 8,
|
||||
blake2b_compress(H, <<SmallChunk/binary, 0:FillSize>>, BCompr + Size, true).
|
||||
|
||||
blake2b_compress(H, Chunk0, BCompr, Last) ->
|
||||
Chunk = to_big_endian(Chunk0),
|
||||
<<V0_11:(12*64), V12:64, V13:64, V14:64, V15:64>> = <<H/binary, (blake_iv())/binary>>,
|
||||
V12_ = V12 bxor (BCompr band ?MAX_64BIT),
|
||||
V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT),
|
||||
V14_ = case Last of
|
||||
false -> V14;
|
||||
true -> V14 bxor ?MAX_64BIT
|
||||
end,
|
||||
V = <<V0_11:(12*64), V12_:64, V13_:64, V14_:64, V15:64>>,
|
||||
|
||||
<<VLow:(8*64), VHigh:(8*64)>> =
|
||||
lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)),
|
||||
|
||||
<<HInt:(8*64)>> = H,
|
||||
<<((HInt bxor VLow) bxor VHigh):(8*64)>>.
|
||||
|
||||
blake2b_mix(Rnd, Chunk, V) ->
|
||||
<<V0:64, V1:64, V2:64, V3:64, V4:64, V5:64, V6:64, V7:64, V8:64,
|
||||
V9:64, V10:64, V11:64, V12:64, V13:64, V14:64, V15:64>> = V,
|
||||
<<M0:64, M1:64, M2:64, M3:64, M4:64, M5:64, M6:64, M7:64, M8:64,
|
||||
M9:64, M10:64, M11:64, M12:64, M13:64, M14:64, M15:64>> = Chunk,
|
||||
Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15},
|
||||
M = fun(Ix) -> element(Ix+1, Ms) end,
|
||||
|
||||
[S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10),
|
||||
|
||||
{Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)),
|
||||
{Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)),
|
||||
{Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)),
|
||||
{Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)),
|
||||
|
||||
{Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)),
|
||||
{Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)),
|
||||
{Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)),
|
||||
{Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)),
|
||||
|
||||
<<Vy0:64, Vy1:64, Vy2:64, Vy3:64, Vy4:64, Vy5:64, Vy6:64, Vy7:64, Vy8:64,
|
||||
Vy9:64, Vy10:64, Vy11:64, Vy12:64, Vy13:64, Vy14:64, Vy15:64>>.
|
||||
|
||||
blake2b_mix(Va, Vb, Vc, Vd, X, Y) ->
|
||||
Va1 = (Va + Vb + X) band ?MAX_64BIT,
|
||||
Vd1 = rotr64(32, Vd bxor Va1),
|
||||
|
||||
Vc1 = (Vc + Vd1) band ?MAX_64BIT,
|
||||
Vb1 = rotr64(24, Vb bxor Vc1),
|
||||
|
||||
Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT,
|
||||
Vd2 = rotr64(16, Va2 bxor Vd1),
|
||||
|
||||
Vc2 = (Vc1 + Vd2) band ?MAX_64BIT,
|
||||
Vb2 = rotr64(63, Vb1 bxor Vc2),
|
||||
|
||||
{Va2, Vb2, Vc2, Vd2}.
|
||||
|
||||
blake_iv() ->
|
||||
IV0 = 16#6A09E667F3BCC908,
|
||||
IV1 = 16#BB67AE8584CAA73B,
|
||||
IV2 = 16#3C6EF372FE94F82B,
|
||||
IV3 = 16#A54FF53A5F1D36F1,
|
||||
IV4 = 16#510E527FADE682D1,
|
||||
IV5 = 16#9B05688C2B3E6C1F,
|
||||
IV6 = 16#1F83D9ABFB41BD6B,
|
||||
IV7 = 16#5BE0CD19137E2179,
|
||||
<<IV0:64, IV1:64, IV2:64, IV3:64, IV4:64, IV5:64, IV6:64, IV7:64>>.
|
||||
|
||||
sigma(N) ->
|
||||
{_, Row} = lists:keyfind(N, 1, sigma()), Row.
|
||||
|
||||
sigma() ->
|
||||
[{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]},
|
||||
{1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]},
|
||||
{2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]},
|
||||
{3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]},
|
||||
{4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]},
|
||||
{5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]},
|
||||
{6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]},
|
||||
{7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]},
|
||||
{8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]},
|
||||
{9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}].
|
||||
|
||||
rotr64(N, I64) ->
|
||||
<<I64rot:64>> = rotr641(N, <<I64:64>>),
|
||||
I64rot.
|
||||
|
||||
rotr641(16, <<X:(64-16), Y:16>>) -> <<Y:16, X:(64-16)>>;
|
||||
rotr641(24, <<X:(64-24), Y:24>>) -> <<Y:24, X:(64-24)>>;
|
||||
rotr641(32, <<X:(64-32), Y:32>>) -> <<Y:32, X:(64-32)>>;
|
||||
rotr641(63, <<X:(64-63), Y:63>>) -> <<Y:63, X:(64-63)>>.
|
||||
|
||||
pad(N, Bin) ->
|
||||
case (N - (byte_size(Bin) rem N)) rem N of
|
||||
0 -> Bin;
|
||||
Pad -> <<Bin/binary, 0:(Pad *8)>>
|
||||
end.
|
||||
|
||||
to_big_endian(Bin) -> to_big_endian(Bin, <<>>).
|
||||
to_big_endian(<<>>, Acc) -> Acc;
|
||||
to_big_endian(<<UInt64:1/little-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
|
||||
to_big_endian(Rest, <<Acc/binary, UInt64:1/big-unsigned-integer-unit:64>>).
|
||||
|
||||
to_little_endian(Bin) -> to_little_endian(Bin, <<>>).
|
||||
to_little_endian(<<>>, Acc) -> Acc;
|
||||
to_little_endian(<<UInt64:1/big-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
|
||||
to_little_endian(Rest, <<Acc/binary, UInt64:1/little-unsigned-integer-unit:64>>).
|
||||
|
||||
+50
-5
@@ -17,6 +17,7 @@
|
||||
, sophia_type_to_typerep/1
|
||||
, to_sophia_value/4
|
||||
, to_sophia_value/5
|
||||
, decode_calldata/3
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
@@ -226,7 +227,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) ->
|
||||
#{ typed_ast := TypedAst,
|
||||
type_env := TypeEnv,
|
||||
icode := Icode } = string_to_icode(ContractString, Options),
|
||||
{ok, Type0} = get_decode_type(FunName, TypedAst),
|
||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
VmType = aeso_ast_to_icode:ast_typerep(Type, Icode),
|
||||
case aeso_heap:from_binary(VmType, Data) of
|
||||
@@ -259,6 +260,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) ->
|
||||
address_literal(N) -> {hash, [], <<N:256>>}. % TODO
|
||||
|
||||
%% TODO: somewhere else
|
||||
-spec translate_vm_value(aeso_sophia:type(), aeso_syntax:type(), aeso_sophia:data()) -> aeso_syntax:expr().
|
||||
translate_vm_value(word, {id, _, "address"}, N) -> address_literal(N);
|
||||
translate_vm_value(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(N);
|
||||
translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(N);
|
||||
@@ -320,6 +322,49 @@ create_calldata(Code, Fun, Args) ->
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
-spec decode_calldata(string(), string(), binary()) ->
|
||||
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
||||
| {error, term()}.
|
||||
decode_calldata(ContractString, FunName, Calldata) ->
|
||||
try
|
||||
#{ typed_ast := TypedAst,
|
||||
type_env := TypeEnv,
|
||||
icode := Icode } = string_to_icode(ContractString, []),
|
||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||
DropArg = fun({arg, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(DropArg, Args),
|
||||
Type0 = {tuple_t, [], ArgTypes},
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
VmType = aeso_ast_to_icode:ast_typerep(Type, Icode),
|
||||
case aeso_heap:from_binary({tuple, [word, VmType]}, Calldata) of
|
||||
{ok, {_, VmValue}} ->
|
||||
try
|
||||
{tuple, [], Values} = translate_vm_value(VmType, Type, VmValue),
|
||||
{ok, ArgTypes, Values}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||
{error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
||||
[VmValue, VmType, Type0Str]))],
|
||||
fun (E) -> E end)}
|
||||
end;
|
||||
{error, _Err} ->
|
||||
{error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
|
||||
fun(E) -> E end)}
|
||||
end
|
||||
catch
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
|
||||
error:{badmatch, {error, missing_function}} ->
|
||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
end.
|
||||
|
||||
|
||||
get_arg_icode(Funs) ->
|
||||
case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of
|
||||
[Args] -> Args;
|
||||
@@ -340,12 +385,12 @@ get_call_type([_ | Contracts]) ->
|
||||
get_call_type(Contracts).
|
||||
|
||||
get_decode_type(FunName, [{contract, _, _, Defs}]) ->
|
||||
GetType = fun({letfun, _, {id, _, Name}, _, Ret, _}) when Name == FunName -> [Ret];
|
||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, _, Ret}}) when Name == FunName -> [Ret];
|
||||
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
||||
(_) -> [] end,
|
||||
case lists:flatmap(GetType, Defs) of
|
||||
[Type] -> {ok, Type};
|
||||
[] -> {error, missing_function}
|
||||
[{Args, Ret}] -> {ok, Args, Ret};
|
||||
[] -> {error, missing_function}
|
||||
end;
|
||||
get_decode_type(FunName, [_ | Contracts]) ->
|
||||
%% The __decode should be in the final contract
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Ulf Norell
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Fate backend for Sophia compiler
|
||||
%%% @end
|
||||
%%% Created : 11 Jan 2019
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_icode_to_fate).
|
||||
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
-export([compile/2]).
|
||||
|
||||
%% -- Preamble ---------------------------------------------------------------
|
||||
|
||||
-define(TODO(What), error({todo, ?FILE, ?LINE, ?FUNCTION_NAME, What})).
|
||||
|
||||
-define(i(__X__), {immediate, __X__}).
|
||||
-define(a, {stack, 0}).
|
||||
|
||||
-record(env, { args = [], stack = [], tailpos = true }).
|
||||
|
||||
%% -- Debugging --------------------------------------------------------------
|
||||
|
||||
%% debug(Options, Fmt) -> debug(Options, Fmt, []).
|
||||
debug(Options, Fmt, Args) ->
|
||||
case proplists:get_value(debug, Options, true) of
|
||||
true -> io:format(Fmt, Args);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
%% -- Main -------------------------------------------------------------------
|
||||
|
||||
%% @doc Main entry point.
|
||||
compile(ICode, Options) ->
|
||||
#{ contract_name := _ContractName,
|
||||
state_type := _StateType,
|
||||
functions := Functions } = ICode,
|
||||
SFuns = functions_to_scode(Functions, Options),
|
||||
SFuns1 = optimize_scode(SFuns, Options),
|
||||
to_basic_blocks(SFuns1, Options).
|
||||
|
||||
functions_to_scode(Functions, Options) ->
|
||||
maps:from_list(
|
||||
[ {list_to_binary(Name), function_to_scode(Name, Args, Body, Type, Options)}
|
||||
|| {Name, _Ann, Args, Body, Type} <- Functions, Name /= "init" ]). %% TODO: skip init for now
|
||||
|
||||
function_to_scode(Name, Args, Body, Type, Options) ->
|
||||
debug(Options, "Compiling ~p ~p : ~p ->\n ~p\n", [Name, Args, Type, Body]),
|
||||
ArgTypes = [ icode_type_to_fate(T) || {_, T} <- Args ],
|
||||
ResType = icode_type_to_fate(Type),
|
||||
SCode = to_scode(init_env(Args), Body),
|
||||
debug(Options, " scode: ~p\n", [SCode]),
|
||||
{{ArgTypes, ResType}, SCode}.
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
|
||||
%% TODO: the Fate types don't seem to be specified anywhere...
|
||||
icode_type_to_fate(word) -> integer;
|
||||
icode_type_to_fate(string) -> string;
|
||||
icode_type_to_fate({tuple, Types}) ->
|
||||
{tuple, lists:map(fun icode_type_to_fate/1, Types)};
|
||||
icode_type_to_fate({list, Type}) ->
|
||||
{list, icode_type_to_fate(Type)};
|
||||
icode_type_to_fate(typerep) -> typerep;
|
||||
icode_type_to_fate(Type) -> ?TODO(Type).
|
||||
|
||||
%% -- Phase I ----------------------------------------------------------------
|
||||
%% Icode to structured assembly
|
||||
|
||||
%% -- Environment functions --
|
||||
|
||||
init_env(Args) ->
|
||||
#env{ args = Args, stack = [], tailpos = true }.
|
||||
|
||||
push_env(Type, Env) ->
|
||||
Env#env{ stack = [{"_", Type} | Env#env.stack] }.
|
||||
|
||||
notail(Env) -> Env#env{ tailpos = false }.
|
||||
|
||||
lookup_var(#env{ args = Args, stack = S }, X) ->
|
||||
case {keyfind_index(X, 1, S), keyfind_index(X, 1, Args)} of
|
||||
{false, false} -> false;
|
||||
{false, Arg} -> {arg, Arg};
|
||||
{Local, _} -> {stack, Local}
|
||||
end.
|
||||
|
||||
%% -- The compiler --
|
||||
|
||||
to_scode(_Env, #integer{ value = N }) ->
|
||||
[aeb_fate_code:push(?i(N))]; %% Doesn't exist (yet), translated by desugaring
|
||||
|
||||
to_scode(Env, #var_ref{name = X}) ->
|
||||
case lookup_var(Env, X) of
|
||||
false -> error({unbound_variable, X, Env});
|
||||
{stack, N} -> [aeb_fate_code:dup(?i(N))];
|
||||
{arg, N} -> [aeb_fate_code:push({arg, N})]
|
||||
end;
|
||||
to_scode(Env, #binop{ op = Op, left = A, right = B }) ->
|
||||
[ to_scode(notail(Env), B)
|
||||
, to_scode(push_env(binop_type_r(Op), Env), A)
|
||||
, binop_to_scode(Op) ];
|
||||
|
||||
to_scode(Env, #ifte{decision = Dec, then = Then, else = Else}) ->
|
||||
[ to_scode(notail(Env), Dec)
|
||||
, {ifte, to_scode(Env, Then), to_scode(Env, Else)} ];
|
||||
|
||||
to_scode(_Env, Icode) -> ?TODO(Icode).
|
||||
|
||||
%% -- Operators --
|
||||
|
||||
binop_types('+') -> {word, word};
|
||||
binop_types('-') -> {word, word};
|
||||
binop_types('==') -> {word, word};
|
||||
binop_types(Op) -> ?TODO(Op).
|
||||
|
||||
%% binop_type_l(Op) -> element(1, binop_types(Op)).
|
||||
binop_type_r(Op) -> element(2, binop_types(Op)).
|
||||
|
||||
binop_to_scode('+') -> add_a_a_a(); %% Optimization introduces other variants
|
||||
binop_to_scode('-') -> sub_a_a_a();
|
||||
binop_to_scode('==') -> eq_a_a_a().
|
||||
% binop_to_scode(Op) -> ?TODO(Op).
|
||||
|
||||
add_a_a_a() -> aeb_fate_code:add(?a, ?a, ?a).
|
||||
sub_a_a_a() -> aeb_fate_code:sub(?a, ?a, ?a).
|
||||
eq_a_a_a() -> aeb_fate_code:eq(?a, ?a, ?a).
|
||||
|
||||
%% -- Phase II ---------------------------------------------------------------
|
||||
%% Optimize
|
||||
|
||||
optimize_scode(Funs, Options) ->
|
||||
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
||||
Funs).
|
||||
|
||||
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
||||
|
||||
flatten_s({ifte, Then, Else}) -> {ifte, flatten(Then), flatten(Else)};
|
||||
flatten_s(I) -> I.
|
||||
|
||||
optimize_fun(_Funs, Name, {{Args, Res}, Code}, Options) ->
|
||||
Code0 = flatten(Code),
|
||||
debug(Options, "Optimizing ~s\n", [Name]),
|
||||
debug(Options, " original : ~p\n", [Code0]),
|
||||
Code1 = simplify(Code0),
|
||||
debug(Options, " simplified: ~p\n", [Code1]),
|
||||
Code2 = desugar(Code1),
|
||||
debug(Options, " desugared : ~p\n", [Code2]),
|
||||
{{Args, Res}, Code2}.
|
||||
|
||||
simplify([]) -> [];
|
||||
simplify([I | Code]) ->
|
||||
simpl_top(simpl_s(I), simplify(Code)).
|
||||
|
||||
simpl_s({ifte, Then, Else}) ->
|
||||
{ifte, simplify(Then), simplify(Else)};
|
||||
simpl_s(I) -> I.
|
||||
|
||||
%% add_i 0 --> nop
|
||||
simpl_top({'ADD', _, ?i(0), _}, Code) -> Code;
|
||||
%% push n, add_a --> add_i n
|
||||
simpl_top({'PUSH', ?a, ?i(N)},
|
||||
[{'ADD', ?a, ?a, ?a} | Code]) ->
|
||||
simpl_top( aeb_fate_code:add(?a, ?i(N), ?a), Code);
|
||||
%% push n, add_i m --> add_i (n + m)
|
||||
simpl_top({'PUSH', ?a, ?i(N)}, [{'ADD', ?a, ?i(M), ?a} | Code]) ->
|
||||
simpl_top(aeb_fate_code:push(?i(N + M)), Code);
|
||||
%% add_i n, add_i m --> add_i (n + m)
|
||||
simpl_top({'ADD', ?a, ?i(N), ?a}, [{'ADD', ?a, ?i(M), ?a} | Code]) ->
|
||||
simpl_top({'ADD', ?a, ?i(N + M), ?a}, Code);
|
||||
|
||||
simpl_top(I, Code) -> [I | Code].
|
||||
|
||||
%% Desugar and specialize
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_code:inc()];
|
||||
desugar({ifte, Then, Else}) -> [{ifte, desugar(Then), desugar(Else)}];
|
||||
desugar(Code) when is_list(Code) ->
|
||||
lists:flatmap(fun desugar/1, Code);
|
||||
desugar(I) -> [I].
|
||||
|
||||
%% -- Phase III --------------------------------------------------------------
|
||||
%% Constructing basic blocks
|
||||
|
||||
to_basic_blocks(Funs, Options) ->
|
||||
maps:from_list([ {Name, {{Args, Res},
|
||||
bb(Name, Code ++ [aeb_fate_code:return()], Options)}}
|
||||
|| {Name, {{Args, Res}, Code}} <- maps:to_list(Funs) ]).
|
||||
|
||||
bb(Name, Code, Options) ->
|
||||
Blocks0 = blocks(Code),
|
||||
Blocks = optimize_blocks(Blocks0),
|
||||
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
||||
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
||||
debug(Options, "Final code for ~s:\n ~p\n", [Name, BBs]),
|
||||
maps:from_list(BBs).
|
||||
|
||||
%% -- Break up scode into basic blocks --
|
||||
|
||||
blocks(Code) ->
|
||||
Top = make_ref(),
|
||||
blocks([{Top, Code}], []).
|
||||
|
||||
blocks([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
blocks([{Ref, Code} | Blocks], Acc) ->
|
||||
block(Ref, Code, [], Blocks, Acc).
|
||||
|
||||
block(Ref, [], CodeAcc, Blocks, BlockAcc) ->
|
||||
blocks(Blocks, [{Ref, lists:reverse(CodeAcc)} | BlockAcc]);
|
||||
block(Ref, [{ifte, Then, Else} | Code], Acc, Blocks, BlockAcc) ->
|
||||
ThenLbl = make_ref(),
|
||||
RestLbl = make_ref(),
|
||||
block(Ref, Else ++ [{jump, RestLbl}],
|
||||
[{jumpif, ThenLbl} | Acc],
|
||||
[{ThenLbl, Then ++ [{jump, RestLbl}]},
|
||||
{RestLbl, Code} | Blocks],
|
||||
BlockAcc);
|
||||
block(Ref, [I | Code], Acc, Blocks, BlockAcc) ->
|
||||
block(Ref, Code, [I | Acc], Blocks, BlockAcc).
|
||||
|
||||
%% -- Reorder, inline, and remove dead blocks --
|
||||
|
||||
optimize_blocks(Blocks) ->
|
||||
%% We need to look at the last instruction a lot, so reverse all blocks.
|
||||
Rev = fun(Bs) -> [ {Ref, lists:reverse(Code)} || {Ref, Code} <- Bs ] end,
|
||||
RBlocks = Rev(Blocks),
|
||||
RBlockMap = maps:from_list(RBlocks),
|
||||
RBlocks1 = reorder_blocks(RBlocks, []),
|
||||
RBlocks2 = [ {Ref, inline_block(RBlockMap, Ref, Code)} || {Ref, Code} <- RBlocks1 ],
|
||||
RBlocks3 = remove_dead_blocks(RBlocks2),
|
||||
Rev(RBlocks3).
|
||||
|
||||
%% Choose the next block based on the final jump.
|
||||
reorder_blocks([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
reorder_blocks([{Ref, Code} | Blocks], Acc) ->
|
||||
reorder_blocks(Ref, Code, Blocks, Acc).
|
||||
|
||||
reorder_blocks(Ref, Code, Blocks, Acc) ->
|
||||
Acc1 = [{Ref, Code} | Acc],
|
||||
case Code of
|
||||
['RETURN'|_] -> reorder_blocks(Blocks, Acc1);
|
||||
[{'RETURNR', _}|_] -> reorder_blocks(Blocks, Acc1);
|
||||
[{jump, L}|_] ->
|
||||
NotL = fun({L1, _}) -> L1 /= L end,
|
||||
case lists:splitwith(NotL, Blocks) of
|
||||
{Blocks1, [{L, Code1} | Blocks2]} ->
|
||||
reorder_blocks(L, Code1, Blocks1 ++ Blocks2, Acc1);
|
||||
{_, []} -> reorder_blocks(Blocks, Acc1)
|
||||
end
|
||||
end.
|
||||
|
||||
%% Inline short blocks (≤ 2 instructions)
|
||||
inline_block(BlockMap, Ref, [{jump, L} | Code] = Code0) when L /= Ref ->
|
||||
case maps:get(L, BlockMap, nocode) of
|
||||
Dest when length(Dest) < 3 ->
|
||||
%% Remove Ref to avoid infinite loops
|
||||
inline_block(maps:remove(Ref, BlockMap), L, Dest) ++ Code;
|
||||
_ -> Code0
|
||||
end;
|
||||
inline_block(_, _, Code) -> Code.
|
||||
|
||||
%% Remove unused blocks
|
||||
remove_dead_blocks(Blocks = [{Top, _} | _]) ->
|
||||
BlockMap = maps:from_list(Blocks),
|
||||
LiveBlocks = chase_labels([Top], BlockMap, #{}),
|
||||
[ B || B = {L, _} <- Blocks, maps:is_key(L, LiveBlocks) ].
|
||||
|
||||
chase_labels([], _, Live) -> Live;
|
||||
chase_labels([L | Ls], Map, Live) ->
|
||||
Code = maps:get(L, Map),
|
||||
Jump = fun({jump, A}) -> [A || not maps:is_key(A, Live)];
|
||||
({jumpif, A}) -> [A || not maps:is_key(A, Live)];
|
||||
(_) -> [] end,
|
||||
New = lists:flatmap(Jump, Code),
|
||||
chase_labels(New ++ Ls, Map, Live#{ L => true }).
|
||||
|
||||
|
||||
%% -- Translate label refs to indices --
|
||||
|
||||
set_labels(Labels, {Ref, Code}) when is_reference(Ref) ->
|
||||
{maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]};
|
||||
set_labels(Labels, {jump, Ref}) -> aeb_fate_code:jump(maps:get(Ref, Labels));
|
||||
set_labels(Labels, {jumpif, Ref}) -> aeb_fate_code:jumpif(?a, maps:get(Ref, Labels));
|
||||
set_labels(_, I) -> I.
|
||||
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
with_ixs(Xs) ->
|
||||
lists:zip(lists:seq(0, length(Xs) - 1), Xs).
|
||||
|
||||
keyfind_index(X, J, Xs) ->
|
||||
case [ I || {I, E} <- with_ixs(Xs), X == element(J, E) ] of
|
||||
[I | _] -> I;
|
||||
[] -> false
|
||||
end.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, aesophia,
|
||||
[{description, "Contract Language for Aethernity"},
|
||||
{vsn, {cmd, "cat VERSION | tr -d '[:space:]'"}},
|
||||
[{description, "Contract Language for aeternity"},
|
||||
{vsn, "2.0.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -162,6 +162,14 @@ encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
|
||||
{ok, Calldata, CalldataType, RetVMType1} = aeso_compiler:create_calldata(Code, FunName, Args),
|
||||
?assertEqual(RetVMType1, RetVMType),
|
||||
{ok, {_Hash, ArgTuple}} = aeso_heap:from_binary(CalldataType, Calldata),
|
||||
case FunName of
|
||||
"init" ->
|
||||
ok;
|
||||
_ ->
|
||||
{ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata),
|
||||
Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ],
|
||||
?assertMatch({X, X}, {Args, Values})
|
||||
end,
|
||||
tuple_to_list(ArgTuple).
|
||||
|
||||
encode_decode(T, D) ->
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
%%%=============================================================================
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Unit tests for the aeso_blake2 module
|
||||
%%%
|
||||
%%% In addition the aeso_blake2 module was compared to the C reference
|
||||
%%% implementation by writing a QuickCheck property.
|
||||
%%% @end
|
||||
%%%=============================================================================
|
||||
-module(aeso_blake2_tests).
|
||||
|
||||
-ifdef(TEST).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
blake2b_test_() ->
|
||||
{"Tests for BLAKE2b hash implementation",
|
||||
[ fun() -> blake2b(Data) end || Data <- test_data_blake2b() ]}.
|
||||
|
||||
blake2b({Msg0, Key0, ExpectedOut0}) ->
|
||||
Msg = mk_binary(Msg0),
|
||||
Key = mk_binary(Key0),
|
||||
ExpectedOut = mk_binary(ExpectedOut0),
|
||||
Result = aeso_blake2:blake2b(byte_size(ExpectedOut), Msg, Key),
|
||||
?assertEqual(Result, {ok, ExpectedOut}).
|
||||
|
||||
mk_binary(Bin) when is_binary(Bin) -> Bin;
|
||||
mk_binary(HexStr) when is_list(HexStr) ->
|
||||
<< << (erlang:list_to_integer([H], 16)):4 >> || H <- HexStr >>.
|
||||
|
||||
test_data_blake2b() ->
|
||||
[ %% {Message, Key, ExpectedHash}
|
||||
%% From Wikipedia
|
||||
%% https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2
|
||||
{<<>>,
|
||||
<<>>,
|
||||
"786A02F742015903C6C6FD852552D272912F4740E15847618A86E217F71F5419D25E1031AFEE585313896444934EB04B903A685B1448B755D56F701AFE9BE2CE"}
|
||||
, {<<"The quick brown fox jumps over the lazy dog">>,
|
||||
<<>>,
|
||||
"A8ADD4BDDDFD93E4877D2746E62817B116364A1FA7BC148D95090BC7333B3673F82401CF7AA2E4CB1ECD90296E3F14CB5413F8ED77BE73045B13914CDCD6A918"}
|
||||
|
||||
%% From reference implementation testvectors
|
||||
%% https://github.com/BLAKE2/BLAKE2/tree/master/testvectors
|
||||
%%
|
||||
%% Non-keyed
|
||||
, {"00",
|
||||
"",
|
||||
"2FA3F686DF876995167E7C2E5D74C4C7B6E48F8068FE0E44208344D480F7904C36963E44115FE3EB2A3AC8694C28BCB4F5A0F3276F2E79487D8219057A506E4B"}
|
||||
, {"0001",
|
||||
"",
|
||||
"1C08798DC641ABA9DEE435E22519A4729A09B2BFE0FF00EF2DCD8ED6F8A07D15EAF4AEE52BBF18AB5608A6190F70B90486C8A7D4873710B1115D3DEBBB4327B5"}
|
||||
, {"00010203040506070809",
|
||||
"",
|
||||
"29102511D749DB3CC9B4E335FA1F5E8FACA8421D558F6A3F3321D50D044A248BA595CFC3EFD3D2ADC97334DA732413F5CBF4751C362BA1D53862AC1E8DABEEE8"}
|
||||
|
||||
%% Keyed
|
||||
, {"",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"}
|
||||
, {"00",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"}
|
||||
, {"0001",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"}
|
||||
, {"00010203040506070809",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd"}
|
||||
].
|
||||
|
||||
|
||||
-endif.
|
||||
|
||||
@@ -97,7 +97,8 @@ compilable_contracts() ->
|
||||
"variant_types",
|
||||
"state_handling",
|
||||
"events",
|
||||
"include"
|
||||
"include",
|
||||
"basic_auth"
|
||||
].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Contract replicating "normal" Aeternity authentication
|
||||
contract BasicAuth =
|
||||
record state = { nonce : int, owner : address }
|
||||
|
||||
function init() = { nonce = 1, owner = Call.caller }
|
||||
|
||||
function authorize(n : int, s : signature) : bool =
|
||||
require(n >= state.nonce, "Nonce too low")
|
||||
require(n =< state.nonce, "Nonce too high")
|
||||
put(state{ nonce = n + 1 })
|
||||
switch(Auth.tx_hash)
|
||||
None => abort("Not in Auth context")
|
||||
Some(tx_hash) => Crypto.ecverify(to_sign(tx_hash, n), state.owner, s)
|
||||
|
||||
function to_sign(h : hash, n : int) =
|
||||
Crypto.blake2b((h, n))
|
||||
|
||||
private function require(b : bool, err : string) =
|
||||
if(!b) abort(err)
|
||||
Reference in New Issue
Block a user