Merge pull request #59 from aeternity/PT-164629541-generic_hash_and_signature

Add bytes(int), add address_literalsm add ecverify_secp256k1
This commit is contained in:
Hans Svensson 2019-04-23 11:23:32 +02:00 committed by GitHub
commit 51b63f9559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 341 additions and 88 deletions

View File

@ -3,7 +3,7 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
{ref, "56cf62b"}}} {ref, "e8253b0"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", , {jsx, {git, "https://github.com/talentdeficit/jsx.git",
{tag, "2.8.0"}}} {tag, "2.8.0"}}}

View File

@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"56cf62b48753a61e30b81d805eee5ec36959b688"}}, {ref,"e8253b09709f1595d8bd6a1756a0ce93185c6518"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",

View File

@ -24,6 +24,7 @@
%% Types %% Types
-record(app_t, {ann,id,fields}). -record(app_t, {ann,id,fields}).
-record(tuple_t, {ann,args}). -record(tuple_t, {ann,args}).
-record(bytes_t, {ann,len}).
-record(record_t, {fields}). -record(record_t, {fields}).
-record(field_t, {ann,id,type}). -record(field_t, {ann,id,type}).
-record(alias_t, {type}). -record(alias_t, {type}).
@ -43,7 +44,7 @@
-record(bool, {ann,bool}). -record(bool, {ann,bool}).
-record(int, {ann,value}). -record(int, {ann,value}).
-record(string, {ann,bin}). -record(string, {ann,bin}).
-record(hash, {ann,hash}). -record(bytes, {ann,bin}).
-record(tuple, {ann,args}). -record(tuple, {ann,args}).
-record(list, {ann,args}). -record(list, {ann,args}).
-record(app, {ann,func,args}). -record(app, {ann,func,args}).
@ -133,6 +134,8 @@ encode_type(#qcon{names=Ns}) ->
encode_type(#tuple_t{args=As}) -> encode_type(#tuple_t{args=As}) ->
Eas = encode_types(As), Eas = encode_types(As),
[{<<"tuple">>,Eas}]; [{<<"tuple">>,Eas}];
encode_type(#bytes_t{len=Len}) ->
{<<"bytes">>, Len};
encode_type(#record_t{fields=Fs}) -> encode_type(#record_t{fields=Fs}) ->
Efs = encode_fields(Fs), Efs = encode_fields(Fs),
[{<<"record">>,Efs}]; [{<<"record">>,Efs}];
@ -208,7 +211,7 @@ encode_expr(#typed{expr=E}) ->
encode_expr(#bool{bool=B}) -> B; encode_expr(#bool{bool=B}) -> B;
encode_expr(#int{value=V}) -> V; encode_expr(#int{value=V}) -> V;
encode_expr(#string{bin=B}) -> B; encode_expr(#string{bin=B}) -> B;
encode_expr(#hash{hash=H}) -> H; encode_expr(#bytes{bin=B}) -> B;
encode_expr(#tuple{args=As}) -> encode_expr(#tuple{args=As}) ->
Eas = encode_exprs(As), Eas = encode_exprs(As),
[{<<"tuple">>,Eas}]; [{<<"tuple">>,Eas}];

View File

@ -56,7 +56,12 @@
, fields :: [aeso_syntax:id()] , fields :: [aeso_syntax:id()]
, context :: why_record() }). , context :: why_record() }).
-type field_constraint() :: #field_constraint{} | #record_create_constraint{}. -record(is_contract_constraint,
{ contract_t :: utype(),
context :: aeso_syntax:expr() %% The address literal
}).
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
-record(field_info, -record(field_info,
{ ann :: aeso_syntax:ann() { ann :: aeso_syntax:ann()
@ -341,6 +346,7 @@ global_env() ->
Address = {id, Ann, "address"}, Address = {id, Ann, "address"},
Hash = {id, Ann, "hash"}, Hash = {id, Ann, "hash"},
Bits = {id, Ann, "bits"}, Bits = {id, Ann, "bits"},
Bytes = fun(Len) -> {bytes_t, Ann, Len} end,
Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end,
Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end,
Unit = {tuple_t, Ann, []}, Unit = {tuple_t, Ann, []},
@ -373,7 +379,9 @@ global_env() ->
{"abort", Fun1(String, A)}]) {"abort", Fun1(String, A)}])
, types = MkDefs( , types = MkDefs(
[{"int", 0}, {"bool", 0}, {"string", 0}, {"address", 0}, [{"int", 0}, {"bool", 0}, {"string", 0}, {"address", 0},
{"hash", 0}, {"signature", 0}, {"bits", 0}, {"hash", {[], {alias_t, Bytes(32)}}},
{"signature", {[], {alias_t, Bytes(64)}}},
{"bits", 0},
{"option", 1}, {"list", 1}, {"map", 2}, {"option", 1}, {"list", 1}, {"map", 2},
{"oracle", 2}, {"oracle_query", 2} {"oracle", 2}, {"oracle_query", 2}
]) }, ]) },
@ -439,6 +447,7 @@ global_env() ->
CryptoScope = #scope CryptoScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"ecverify", Fun([Hash, Address, SignId], Bool)}, [{"ecverify", Fun([Hash, Address, SignId], Bool)},
{"ecverify_secp256k1", Fun([Hash, Bytes(64), Bytes(64)], Bool)},
{"sha3", Fun1(A, Hash)}, {"sha3", Fun1(A, Hash)},
{"sha256", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)},
{"blake2b", Fun1(A, Hash)}]) }, {"blake2b", Fun1(A, Hash)}]) },
@ -497,7 +506,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
infer(Contracts) -> infer(Contracts) ->
infer(Contracts, []). infer(Contracts, []).
-type option() :: permissive_address_literals | return_env. -type option() :: return_env.
-spec init_env(list(option())) -> env(). -spec init_env(list(option())) -> env().
init_env(_Options) -> global_env(). init_env(_Options) -> global_env().
@ -675,6 +684,9 @@ check_type(Env, X = {Tag, _, _}, Arity) when Tag == con; Tag == qcon; Tag == id;
check_type(Env, Type = {tuple_t, Ann, Types}, Arity) -> check_type(Env, Type = {tuple_t, Ann, Types}, Arity) ->
ensure_base_type(Type, Arity), ensure_base_type(Type, Arity),
{tuple_t, Ann, [ check_type(Env, T, 0) || T <- Types ]}; {tuple_t, Ann, [ check_type(Env, T, 0) || T <- Types ]};
check_type(_Env, Type = {bytes_t, _Ann, _Len}, Arity) ->
ensure_base_type(Type, Arity),
Type;
check_type(Env, {app_t, Ann, Type, Types}, Arity) -> check_type(Env, {app_t, Ann, Type, Types}, Arity) ->
Types1 = [ check_type(Env, T, 0) || T <- Types ], Types1 = [ check_type(Env, T, 0) || T <- Types ],
Type1 = check_type(Env, Type, Arity + length(Types)), Type1 = check_type(Env, Type, Arity + length(Types)),
@ -898,20 +910,29 @@ infer_expr(_Env, Body={int, As, _}) ->
{typed, As, Body, {id, As, "int"}}; {typed, As, Body, {id, As, "int"}};
infer_expr(_Env, Body={string, As, _}) -> infer_expr(_Env, Body={string, As, _}) ->
{typed, As, Body, {id, As, "string"}}; {typed, As, Body, {id, As, "string"}};
infer_expr(_Env, Body={hash, As, Hash}) -> infer_expr(_Env, Body={bytes, As, Bin}) ->
case byte_size(Hash) of {typed, As, Body, {bytes_t, As, byte_size(Bin)}};
32 -> {typed, As, Body, {id, As, "address"}}; infer_expr(_Env, Body={account_pubkey, As, _}) ->
64 -> {typed, As, Body, {id, As, "signature"}} {typed, As, Body, {id, As, "address"}};
end; infer_expr(_Env, Body={oracle_pubkey, As, _}) ->
Q = fresh_uvar(As),
R = fresh_uvar(As),
{typed, As, Body, {app_t, As, {id, As, "oracle"}, [Q, R]}};
infer_expr(_Env, Body={oracle_query_id, As, _}) ->
Q = fresh_uvar(As),
R = fresh_uvar(As),
{typed, As, Body, {app_t, As, {id, As, "oracle_query"}, [Q, R]}};
infer_expr(_Env, Body={contract_pubkey, As, _}) ->
Con = fresh_uvar(As),
constrain([#is_contract_constraint{ contract_t = Con,
context = Body }]),
{typed, As, Body, Con};
infer_expr(_Env, Body={id, As, "_"}) -> infer_expr(_Env, Body={id, As, "_"}) ->
{typed, As, Body, fresh_uvar(As)}; {typed, As, Body, fresh_uvar(As)};
infer_expr(Env, Id = {id, As, _}) -> infer_expr(Env, Id = {Tag, As, _}) when Tag == id; Tag == qid ->
{QName, Type} = lookup_name(Env, As, Id), {QName, Type} = lookup_name(Env, As, Id),
{typed, As, QName, Type}; {typed, As, QName, Type};
infer_expr(Env, Id = {qid, As, _}) -> infer_expr(Env, Id = {Tag, As, _}) when Tag == con; Tag == qcon ->
{QName, Type} = lookup_name(Env, As, Id),
{typed, As, QName, Type};
infer_expr(Env, Id = {con, As, _}) ->
{QName, Type} = lookup_name(Env, As, Id, [freshen]), {QName, Type} = lookup_name(Env, As, Id, [freshen]),
{typed, As, QName, Type}; {typed, As, QName, Type};
infer_expr(Env, {unit, As}) -> infer_expr(Env, {unit, As}) ->
@ -1348,6 +1369,16 @@ check_record_create_constraints(Env, [C | Cs]) ->
end, end,
check_record_create_constraints(Env, Cs). check_record_create_constraints(Env, Cs).
check_is_contract_constraints(_Env, []) -> ok;
check_is_contract_constraints(Env, [C | Cs]) ->
#is_contract_constraint{ contract_t = Type, context = Lit } = C,
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case lookup_type(Env, record_type_name(Type1)) of
{_, {_Ann, {[], {contract_t, _}}}} -> ok;
_ -> type_error({not_a_contract_type, Type1, Lit})
end,
check_is_contract_constraints(Env, Cs).
-spec solve_field_constraints(env(), [field_constraint()]) -> ok. -spec solve_field_constraints(env(), [field_constraint()]) -> ok.
solve_field_constraints(Env, Constraints) -> solve_field_constraints(Env, Constraints) ->
%% First look for record fields that appear in only one type definition %% First look for record fields that appear in only one type definition
@ -1446,9 +1477,12 @@ solve_known_record_types(Env, Constraints) ->
DerefConstraints--SolvedConstraints. DerefConstraints--SolvedConstraints.
destroy_and_report_unsolved_field_constraints(Env) -> destroy_and_report_unsolved_field_constraints(Env) ->
{FieldCs, CreateCs} = {FieldCs, OtherCs} =
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end, lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
get_field_constraints()), get_field_constraints()),
{CreateCs, ContractCs} =
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
OtherCs),
Unknown = solve_known_record_types(Env, FieldCs), Unknown = solve_known_record_types(Env, FieldCs),
if Unknown == [] -> ok; if Unknown == [] -> ok;
true -> true ->
@ -1458,6 +1492,7 @@ destroy_and_report_unsolved_field_constraints(Env) ->
end end
end, end,
check_record_create_constraints(Env, CreateCs), check_record_create_constraints(Env, CreateCs),
check_is_contract_constraints(Env, ContractCs),
destroy_field_constraints(), destroy_field_constraints(),
ok. ok.
@ -1583,6 +1618,8 @@ unfold_types_in_type(Env, {field_t, Attr, Name, Type}, Options) ->
{field_t, Attr, Name, unfold_types_in_type(Env, Type, Options)}; {field_t, Attr, Name, unfold_types_in_type(Env, Type, Options)};
unfold_types_in_type(Env, {constr_t, Ann, Con, Types}, Options) -> unfold_types_in_type(Env, {constr_t, Ann, Con, Types}, Options) ->
{constr_t, Ann, Con, unfold_types_in_type(Env, Types, Options)}; {constr_t, Ann, Con, unfold_types_in_type(Env, Types, Options)};
unfold_types_in_type(Env, {named_arg_t, Ann, Con, Types, Default}, Options) ->
{named_arg_t, Ann, Con, unfold_types_in_type(Env, Types, Options), Default};
unfold_types_in_type(Env, T, Options) when is_tuple(T) -> unfold_types_in_type(Env, T, Options) when is_tuple(T) ->
list_to_tuple(unfold_types_in_type(Env, tuple_to_list(T), Options)); list_to_tuple(unfold_types_in_type(Env, tuple_to_list(T), Options));
unfold_types_in_type(Env, [H|T], Options) -> unfold_types_in_type(Env, [H|T], Options) ->
@ -1638,6 +1675,8 @@ unify1(_Env, {qid, _, Name}, {qid, _, Name}, _When) ->
true; true;
unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) -> unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
true; true;
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
true;
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) -> unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) ->
unify(Env, Named1, Named2, When) andalso unify(Env, Named1, Named2, When) andalso
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When); unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
@ -1655,26 +1694,8 @@ unify1(Env, {app_t, _, T, []}, B, When) ->
unify1(Env, A, {app_t, _, T, []}, When) -> unify1(Env, A, {app_t, _, T, []}, When) ->
unify(Env, A, T, When); unify(Env, A, T, When);
unify1(_Env, A, B, When) -> unify1(_Env, A, B, When) ->
Ok = cannot_unify(A, B, When),
case get_option(permissive_address_literals, false) of false.
true ->
Kind = fun({qcon, _, _}) -> con;
({con, _, _}) -> con;
({id, _, "address"}) -> addr;
({id, _, "hash"}) -> hash;
({app_t, _, {id, _, "oracle"}, _}) -> oracle;
({app_t, _, {id, _, "oracle_query"}, _}) -> query;
(_) -> other end,
%% If permissive_address_literals we allow unifying adresses
%% with contract types or oracles/oracle queries
case lists:usort([Kind(A), Kind(B)]) of
[addr, K] -> K /= other;
_ -> false
end;
false -> false
end,
[ cannot_unify(A, B, When) || not Ok ],
Ok.
dereference(T = {uvar, _, R}) -> dereference(T = {uvar, _, R}) ->
case ets_lookup(type_vars, R) of case ets_lookup(type_vars, R) of
@ -1703,6 +1724,7 @@ occurs_check1(_, {con, _, _}) -> false;
occurs_check1(_, {qid, _, _}) -> false; occurs_check1(_, {qid, _, _}) -> false;
occurs_check1(_, {qcon, _, _}) -> false; occurs_check1(_, {qcon, _, _}) -> false;
occurs_check1(_, {tvar, _, _}) -> false; occurs_check1(_, {tvar, _, _}) -> false;
occurs_check1(_, {bytes_t, _, _}) -> false;
occurs_check1(R, {fun_t, _, Named, Args, Res}) -> occurs_check1(R, {fun_t, _, Named, Args, Res}) ->
occurs_check(R, [Res, Named | Args]); occurs_check(R, [Res, Named | Args]);
occurs_check1(R, {app_t, _, T, Ts}) -> occurs_check1(R, {app_t, _, T, Ts}) ->
@ -1819,6 +1841,11 @@ pp_error({undefined_field, Id}) ->
io_lib:format("Unbound field ~s at ~s\n", [pp(Id), pp_loc(Id)]); io_lib:format("Unbound field ~s at ~s\n", [pp(Id), pp_loc(Id)]);
pp_error({not_a_record_type, Type, Why}) -> pp_error({not_a_record_type, Type, Why}) ->
io_lib:format("~s\n~s\n", [pp_type("Not a record type: ", Type), pp_why_record(Why)]); io_lib:format("~s\n~s\n", [pp_type("Not a record type: ", Type), pp_why_record(Why)]);
pp_error({not_a_contract_type, Type, Lit}) ->
io_lib:format("The type ~s is not a contract type\n"
"when checking that the contract literal at ~s\n~s\n"
"has the type\n~s\n",
[pp_type("", Type), pp_loc(Lit), pp_expr(" ", Lit), pp_type(" ", Type)]);
pp_error({non_linear_pattern, Pattern, Nonlinear}) -> pp_error({non_linear_pattern, Pattern, Nonlinear}) ->
Plural = [ $s || length(Nonlinear) > 1 ], Plural = [ $s || length(Nonlinear) > 1 ],
io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n", io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n",
@ -2043,6 +2070,8 @@ pp({qid, _, Name}) ->
string:join(Name, "."); string:join(Name, ".");
pp({con, _, Name}) -> pp({con, _, Name}) ->
Name; Name;
pp({qcon, _, Name}) ->
string:join(Name, ".");
pp({uvar, _, Ref}) -> pp({uvar, _, Ref}) ->
%% Show some unique representation %% Show some unique representation
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ]; ["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
@ -2050,6 +2079,8 @@ pp({tvar, _, Name}) ->
Name; Name;
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
["(", pp(Cpts), ")"]; ["(", pp(Cpts), ")"];
pp({bytes_t, _, Len}) ->
["bytes(", integer_to_list(Len), ")"];
pp({app_t, _, T, []}) -> pp({app_t, _, T, []}) ->
pp(T); pp(T);
pp({app_t, _, Type, Args}) -> pp({app_t, _, Type, Args}) ->

View File

@ -346,6 +346,11 @@ ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) ->
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)], [ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[word, word, sign_t()], word); [word, word, sign_t()], word);
ast_body(?qid_app(["Crypto", "ecverify_secp256k1"], [Msg, PK, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[bytes_t(32), bytes_t(64), bytes_t(64)], word);
ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) -> ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode); generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) -> ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) ->
@ -416,14 +421,17 @@ ast_body({bool, _, Bool}, _Icode) -> %BOOL as ints
#integer{value = Value}; #integer{value = Value};
ast_body({int, _, Value}, _Icode) -> ast_body({int, _, Value}, _Icode) ->
#integer{value = Value}; #integer{value = Value};
ast_body({hash, _, Hash}, _Icode) -> ast_body({bytes, _, Bin}, _Icode) ->
case Hash of case aeb_memory:binary_to_words(Bin) of
<<Value:32/unit:8>> -> %% address [Word] -> #integer{value = Word};
#integer{value = Value}; Words -> #tuple{cpts = [#integer{value = W} || W <- Words]}
<<Hi:32/unit:8, Lo:32/unit:8>> -> %% signature
#tuple{cpts = [#integer{value = Hi},
#integer{value = Lo}]}
end; end;
ast_body({Key, _, Bin}, _Icode) when Key == account_pubkey;
Key == contract_pubkey;
Key == oracle_pubkey;
Key == oracle_query_id ->
<<Value:32/unit:8>> = Bin,
#integer{value = Value};
ast_body({string,_,Bin}, _Icode) -> ast_body({string,_,Bin}, _Icode) ->
Cpts = [size(Bin) | aeb_memory:binary_to_words(Bin)], Cpts = [size(Bin) | aeb_memory:binary_to_words(Bin)],
#tuple{cpts = [#integer{value=X} || X <- Cpts]}; #tuple{cpts = [#integer{value=X} || X <- Cpts]};
@ -688,6 +696,8 @@ ast_typerep({qid, _, Name}, Icode) ->
lookup_type_id(Name, [], Icode); lookup_type_id(Name, [], Icode);
ast_typerep({con, _, _}, _) -> ast_typerep({con, _, _}, _) ->
word; %% Contract type word; %% Contract type
ast_typerep({bytes_t, _, Len}, _) ->
{bytes, Len};
ast_typerep({app_t, _, {id, _, Name}, Args}, Icode) -> ast_typerep({app_t, _, {id, _, Name}, Args}, Icode) ->
ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ], ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ],
lookup_type_id(Name, ArgReps, Icode); lookup_type_id(Name, ArgReps, Icode);
@ -715,8 +725,8 @@ ast_typerep({variant_t, Cons}, Icode) ->
ttl_t(Icode) -> ttl_t(Icode) ->
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode). ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
sign_t() -> sign_t() -> bytes_t(64).
{tuple, [word, word]}. bytes_t(Len) -> {bytes, Len}.
get_signature_arg(Args0) -> get_signature_arg(Args0) ->
NamedArgs = [Arg || Arg = {named_arg, _, _, _} <- Args0], NamedArgs = [Arg || Arg = {named_arg, _, _, _} <- Args0],
@ -750,6 +760,8 @@ type_value({list, A}) ->
type_value({tuple, As}) -> type_value({tuple, As}) ->
#tuple{ cpts = [#integer{ value = ?TYPEREP_TUPLE_TAG }, #tuple{ cpts = [#integer{ value = ?TYPEREP_TUPLE_TAG },
#list{ elems = [ type_value(A) || A <- As ] }] }; #list{ elems = [ type_value(A) || A <- As ] }] };
type_value({bytes, Len}) ->
#tuple{ cpts = [#integer{ value = ?TYPEREP_BYTES_TAG }, #integer{ value = Len }] };
type_value({variant, Cs}) -> type_value({variant, Cs}) ->
#tuple{ cpts = [#integer{ value = ?TYPEREP_VARIANT_TAG }, #tuple{ cpts = [#integer{ value = ?TYPEREP_VARIANT_TAG },
#list{ elems = [ #list{ elems = [ type_value(A) || A <- As ] } || As <- Cs ] }] }; #list{ elems = [ #list{ elems = [ type_value(A) || A <- As ] } || As <- Cs ] }] };

View File

@ -104,13 +104,12 @@ from_string(ContractString, Options) ->
%% General programming errors in the compiler just signal error. %% General programming errors in the compiler just signal error.
end. end.
-spec string_to_icode(string(), [option() | permissive_address_literals]) -> map(). -spec string_to_icode(string(), [option()]) -> map().
string_to_icode(ContractString, Options0) -> string_to_icode(ContractString, Options) ->
{InferOptions, Options} = lists:partition(fun(Opt) -> Opt == permissive_address_literals end, Options0),
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
pp_sophia_code(Ast, Options), pp_sophia_code(Ast, Options),
pp_ast(Ast, Options), pp_ast(Ast, Options),
{TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | InferOptions]), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]),
pp_typed_ast(TypedAst, Options), pp_typed_ast(TypedAst, Options),
Icode = ast_to_icode(TypedAst, Options), Icode = ast_to_icode(TypedAst, Options),
pp_icode(Icode, Options), pp_icode(Icode, Options),
@ -151,11 +150,11 @@ check_call(Source, FunName, Args, Options) ->
check_call(ContractString0, FunName, Args, Options, PatchFun) -> check_call(ContractString0, FunName, Args, Options, PatchFun) ->
try try
%% First check the contract without the __call function and no permissive literals %% First check the contract without the __call function
#{} = string_to_icode(ContractString0, Options), #{} = string_to_icode(ContractString0, Options),
ContractString = insert_call_function(ContractString0, FunName, Args, Options), ContractString = insert_call_function(ContractString0, FunName, Args, Options),
#{typed_ast := TypedAst, #{typed_ast := TypedAst,
icode := Icode} = string_to_icode(ContractString, [permissive_address_literals | Options]), icode := Icode} = string_to_icode(ContractString, Options),
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
RetVMType = case RetType of RetVMType = case RetType of
@ -257,19 +256,21 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) ->
fun (E) -> io_lib:format("~p", [E]) end)} fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
address_literal(N) -> {hash, [], <<N:256>>}. % TODO address_literal(Type, N) -> {Type, [], <<N:256>>}.
%% TODO: somewhere else %% TODO: somewhere else
-spec translate_vm_value(aeb_aevm_data:type(), aeso_syntax:type(), aeb_aevm_data:data()) -> aeso_syntax:expr(). -spec translate_vm_value(aeb_aevm_data:type(), aeso_syntax:type(), aeb_aevm_data:data()) -> aeso_syntax:expr().
translate_vm_value(word, {id, _, "address"}, N) -> address_literal(N); translate_vm_value(word, {id, _, "address"}, N) -> address_literal(account_pubkey, N);
translate_vm_value(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(N); translate_vm_value(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N);
translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(N); translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N);
translate_vm_value(word, {id, _, "hash"}, N) -> {hash, [], <<N:256>>}; translate_vm_value(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N);
translate_vm_value(word, {id, _, "int"}, N) -> {int, [], N}; translate_vm_value(word, {id, _, "int"}, N) -> {int, [], N};
translate_vm_value(word, {id, _, "bits"}, N) -> error({todo, bits, N}); translate_vm_value(word, {id, _, "bits"}, N) -> error({todo, bits, N});
translate_vm_value(word, {id, _, "bool"}, N) -> {bool, [], N /= 0}; translate_vm_value(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
translate_vm_value({tuple, [word, word]}, {id, _, "signature"}, {tuple, [Hi, Lo]}) -> translate_vm_value({bytes, Len}, {bytes_t, _, Len}, Val) when Len =< 32 ->
{hash, [], <<Hi:256, Lo:256>>}; {bytes, [], <<Val:Len/unit:8>>};
translate_vm_value({bytes, Len}, {bytes_t, _, Len}, Val) ->
{bytes, [], binary:part(<< <<W:32/unit:8>> || W <- tuple_to_list(Val) >>, 0, Len)};
translate_vm_value(string, {id, _, "string"}, S) -> {string, [], S}; translate_vm_value(string, {id, _, "string"}, S) -> {string, [], S};
translate_vm_value({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, List) -> translate_vm_value({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, List) ->
{list, [], [translate_vm_value(VmType, Type, X) || X <- List]}; {list, [], [translate_vm_value(VmType, Type, X) || X <- List]};
@ -402,6 +403,10 @@ icode_to_term(word, {integer, N}) -> N;
icode_to_term(string, {tuple, [{integer, Len} | Words]}) -> icode_to_term(string, {tuple, [{integer, Len} | Words]}) ->
<<Str:Len/binary, _/binary>> = << <<W:256>> || {integer, W} <- Words >>, <<Str:Len/binary, _/binary>> = << <<W:256>> || {integer, W} <- Words >>,
Str; Str;
icode_to_term({bytes, Len}, {integer, Value}) when Len =< 32 ->
Value;
icode_to_term({bytes, Len}, {tuple, Words}) when Len > 32->
list_to_tuple([W || {integer, W} <- Words]);
icode_to_term({list, T}, {list, Vs}) -> icode_to_term({list, T}, {list, Vs}) ->
[ icode_to_term(T, V) || V <- Vs ]; [ icode_to_term(T, V) || V <- Vs ];
icode_to_term({tuple, Ts}, {tuple, Vs}) -> icode_to_term({tuple, Ts}, {tuple, Vs}) ->

View File

@ -136,11 +136,15 @@ type200() ->
type300() -> type400(). type300() -> type400().
type400() -> type400() ->
?RULE(typeAtom(), optional(type_args()), choice(
[?RULE(typeAtom(), optional(type_args()),
case _2 of case _2 of
none -> _1; none -> _1;
{ok, Args} -> {app_t, get_ann(_1), _1, Args} {ok, Args} -> {app_t, get_ann(_1), _1, Args}
end). end),
?RULE(id("bytes"), parens(token(int)),
{bytes_t, get_ann(_1), element(3, _2)})
]).
typeAtom() -> typeAtom() ->
?LAZY_P(choice( ?LAZY_P(choice(
@ -203,8 +207,8 @@ exprAtom() ->
?LAZY_P(begin ?LAZY_P(begin
Expr = ?LAZY_P(expr()), Expr = ?LAZY_P(expr()),
choice( choice(
[ id(), con(), token(qid), token(qcon) [ id_or_addr(), con(), token(qid), token(qcon)
, token(hash), token(string), token(char) , token(bytes), token(string), token(char)
, token(int) , token(int)
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int))) , ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
, {bool, keyword(true), true} , {bool, keyword(true), true}
@ -326,6 +330,26 @@ token(Tag) ->
{Tok, {Line, Col}, Val} -> {Tok, pos_ann(Line, Col), Val} {Tok, {Line, Col}, Val} -> {Tok, pos_ann(Line, Col), Val}
end). end).
id(Id) ->
?LET_P({id, A, X} = Y, id(),
if X == Id -> Y;
true -> fail({A, "expected 'bytes'"})
end).
id_or_addr() ->
?RULE(id(), parse_addr_literal(_1)).
parse_addr_literal(Id = {id, Ann, Name}) ->
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of
false -> Id;
true ->
try aeser_api_encoder:decode(list_to_binary(Name)) of
{Type, Bin} -> {Type, Ann, Bin}
catch _:_ ->
Id
end
end.
%% -- Helpers ---------------------------------------------------------------- %% -- Helpers ----------------------------------------------------------------
keyword(K) -> ann(tok(K)). keyword(K) -> ann(tok(K)).
@ -457,7 +481,7 @@ parse_pattern(E = {id, _, _}) -> E;
parse_pattern(E = {unit, _}) -> E; parse_pattern(E = {unit, _}) -> E;
parse_pattern(E = {int, _, _}) -> E; parse_pattern(E = {int, _, _}) -> E;
parse_pattern(E = {bool, _, _}) -> E; parse_pattern(E = {bool, _, _}) -> E;
parse_pattern(E = {hash, _, _}) -> E; parse_pattern(E = {bytes, _, _}) -> E;
parse_pattern(E = {string, _, _}) -> E; parse_pattern(E = {string, _, _}) -> E;
parse_pattern(E = {char, _, _}) -> E; parse_pattern(E = {char, _, _}) -> E;
parse_pattern(E) -> bad_expr_err("Not a valid pattern", E). parse_pattern(E) -> bad_expr_err("Not a valid pattern", E).

View File

@ -236,6 +236,8 @@ type({app_t, _, Type, Args}) ->
beside(type(Type), tuple_type(Args)); beside(type(Type), tuple_type(Args));
type({tuple_t, _, Args}) -> type({tuple_t, _, Args}) ->
tuple_type(Args); tuple_type(Args);
type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"]));
type({named_arg_t, _, Name, Type, _Default}) -> type({named_arg_t, _, Name, Type, _Default}) ->
%% Drop the default value %% Drop the default value
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default)); %% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
@ -319,8 +321,17 @@ expr_p(_, E = {int, _, N}) ->
end, end,
text(S); text(S);
expr_p(_, {bool, _, B}) -> text(atom_to_list(B)); expr_p(_, {bool, _, B}) -> text(atom_to_list(B));
expr_p(_, {hash, _, <<N:256>>}) -> text("#" ++ integer_to_list(N, 16)); expr_p(_, {bytes, _, Bin}) ->
Digits = byte_size(Bin),
<<N:Digits/unit:8>> = Bin,
text(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
expr_p(_, {hash, _, <<N:512>>}) -> text("#" ++ integer_to_list(N, 16)); expr_p(_, {hash, _, <<N:512>>}) -> text("#" ++ integer_to_list(N, 16));
expr_p(_, {Type, _, Bin})
when Type == account_pubkey;
Type == contract_pubkey;
Type == oracle_pubkey;
Type == oracle_query_id ->
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
expr_p(_, {unit, _}) -> text("()"); expr_p(_, {unit, _}) -> text("()");
expr_p(_, {string, _, S}) -> term(binary_to_list(S)); expr_p(_, {string, _, S}) -> term(binary_to_list(S));
expr_p(_, {char, _, C}) -> expr_p(_, {char, _, C}) ->

View File

@ -20,7 +20,7 @@ lexer() ->
CON = [UPPER, "[a-zA-Z0-9_]*"], CON = [UPPER, "[a-zA-Z0-9_]*"],
INT = [DIGIT, "+"], INT = [DIGIT, "+"],
HEX = ["0x", HEXDIGIT, "+"], HEX = ["0x", HEXDIGIT, "+"],
HASH = ["#", HEXDIGIT, "+"], BYTES = ["#", HEXDIGIT, "+"],
WS = "[\\000-\\ ]+", WS = "[\\000-\\ ]+",
ID = [LOWER, "[a-zA-Z0-9_']*"], ID = [LOWER, "[a-zA-Z0-9_']*"],
TVAR = ["'", ID], TVAR = ["'", ID],
@ -54,7 +54,7 @@ lexer() ->
, {STRING, token(string, fun parse_string/1)} , {STRING, token(string, fun parse_string/1)}
, {HEX, token(hex, fun parse_hex/1)} , {HEX, token(hex, fun parse_hex/1)}
, {INT, token(int, fun list_to_integer/1)} , {INT, token(int, fun list_to_integer/1)}
, {HASH, token(hash, fun parse_hash/1)} , {BYTES, token(bytes, fun parse_bytes/1)}
%% Identifiers (qualified first!) %% Identifiers (qualified first!)
, {QID, token(qid, fun(S) -> string:tokens(S, ".") end)} , {QID, token(qid, fun(S) -> string:tokens(S, ".") end)}
@ -117,10 +117,8 @@ unescape([C | Chars], Acc) ->
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
parse_hash("#" ++ Chars) -> parse_bytes("#" ++ Chars) ->
N = list_to_integer(Chars, 16), N = list_to_integer(Chars, 16),
case length(Chars) > 64 of %% 64 hex digits = 32 bytes Digits = (length(Chars) + 1) div 2,
true -> <<N:64/unit:8>>; %% signature <<N:Digits/unit:8>>.
false -> <<N:32/unit:8>> %% address
end.

View File

@ -60,6 +60,7 @@
-type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()} -type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()}
| {app_t, ann(), type(), [type()]} | {app_t, ann(), type(), [type()]}
| {tuple_t, ann(), [type()]} | {tuple_t, ann(), [type()]}
| {bytes_t, ann(), integer()}
| id() | qid() | id() | qid()
| con() | qcon() %% contracts | con() | qcon() %% contracts
| tvar(). | tvar().
@ -70,6 +71,10 @@
:: {int, ann(), integer()} :: {int, ann(), integer()}
| {bool, ann(), true | false} | {bool, ann(), true | false}
| {hash, ann(), binary()} | {hash, ann(), binary()}
| {account_pubkey, binary()}
| {contract_pubkey, binary()}
| {oracle_pubkey, binary()}
| {oracle_query_id, binary()}
| {unit, ann()} | {unit, ann()}
| {string, ann(), binary()} | {string, ann(), binary()}
| {char, ann(), integer()}. | {char, ann(), integer()}.

View File

@ -4,6 +4,9 @@
-compile(export_all). -compile(export_all).
-define(SANDBOX(Code), sandbox(fun() -> Code end)). -define(SANDBOX(Code), sandbox(fun() -> Code end)).
-define(DUMMY_HASH_WORD, 16#123).
-define(DUMMY_HASH, <<0:30/unit:8, 127, 119>>). %% 16#123
-define(DUMMY_HASH_LIT, "#0000000000000000000000000000000000000000000000000000000000000123").
sandbox(Code) -> sandbox(Code) ->
Parent = self(), Parent = self(),
@ -97,7 +100,15 @@ calldata_test() ->
Map = #{ <<"a">> => 4 }, Map = #{ <<"a">> => 4 },
[{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] = [{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] =
encode_decode_calldata("foo", ["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]), encode_decode_calldata("foo", ["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]),
[16#123, 16#456] = encode_decode_calldata("foo", ["hash", "address"], ["#123", "#456"]), [?DUMMY_HASH_WORD, 16#456] = encode_decode_calldata("foo", ["bytes(32)", "address"],
[?DUMMY_HASH_LIT, "ak_1111111111111111111111111111113AFEFpt5"]),
[?DUMMY_HASH_WORD, ?DUMMY_HASH_WORD] =
encode_decode_calldata("foo", ["bytes(32)", "hash"], [?DUMMY_HASH_LIT, ?DUMMY_HASH_LIT]),
[119, {0, 0}] = encode_decode_calldata("foo", ["int", "signature"], ["119", [$# | lists:duplicate(128, $0)]]),
[16#456] = encode_decode_calldata("foo", ["Remote"], ["ct_1111111111111111111111111111113AFEFpt5"]),
ok. ok.
calldata_init_test() -> calldata_init_test() ->
@ -126,7 +137,9 @@ parameterized_contract(FunName, Types) ->
parameterized_contract(ExtraCode, FunName, Types) -> parameterized_contract(ExtraCode, FunName, Types) ->
lists:flatten( lists:flatten(
["contract Dummy =\n", ["contract Remote =\n"
" function bla : () => ()\n\n"
"contract Dummy =\n",
ExtraCode, "\n", ExtraCode, "\n",
" type an_alias('a) = (string, 'a)\n" " type an_alias('a) = (string, 'a)\n"
" record r = {x : an_alias(int), y : variant}\n" " record r = {x : an_alias(int), y : variant}\n"
@ -139,7 +152,9 @@ oracle_test() ->
" function question(o, q : oracle_query(list(string), option(int))) =\n" " function question(o, q : oracle_query(list(string), option(int))) =\n"
" Oracle.get_question(o, q)\n", " Oracle.get_question(o, q)\n",
{ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} =
aeso_compiler:check_call(Contract, "question", ["#123", "#456"], []), aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], []),
ok. ok.
permissive_literals_fail_test() -> permissive_literals_fail_test() ->

View File

@ -8,6 +8,8 @@
-module(aeso_compiler_tests). -module(aeso_compiler_tests).
-compile([export_all, nowarn_export_all]).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
%% Very simply test compile the given contracts. Only basic checks %% Very simply test compile the given contracts. Only basic checks
@ -16,10 +18,14 @@
simple_compile_test_() -> simple_compile_test_() ->
[ {"Testing the " ++ ContractName ++ " contract", [ {"Testing the " ++ ContractName ++ " contract",
fun() -> fun() ->
case compile(ContractName) of
#{byte_code := ByteCode, #{byte_code := ByteCode,
contract_source := _, contract_source := _,
type_info := _} = compile(ContractName), type_info := _} -> ?assertMatch(Code when is_binary(Code), ByteCode);
?assertMatch(Code when is_binary(Code), ByteCode) ErrBin ->
io:format("\n~s", [ErrBin]),
error(ErrBin)
end
end} || ContractName <- compilable_contracts() ] ++ end} || ContractName <- compilable_contracts() ] ++
[ {"Testing error messages of " ++ ContractName, [ {"Testing error messages of " ++ ContractName,
fun() -> fun() ->
@ -98,7 +104,9 @@ compilable_contracts() ->
"state_handling", "state_handling",
"events", "events",
"include", "include",
"basic_auth" "basic_auth",
"bitcoin_auth",
"address_literals"
]. ].
%% Contracts that should produce type errors %% Contracts that should produce type errors
@ -218,4 +226,80 @@ failing_contracts() ->
, {"bad_include_and_ns", , {"bad_include_and_ns",
[<<"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>, [<<"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>,
<<"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]} <<"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]}
, {"bad_address_literals",
[<<"The type bytes(32) is not a contract type\n"
"when checking that the contract literal at line 32, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" bytes(32)">>,
<<"The type oracle(int, bool) is not a contract type\n"
"when checking that the contract literal at line 30, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" oracle(int, bool)">>,
<<"The type address is not a contract type\n"
"when checking that the contract literal at line 28, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" address">>,
<<"Cannot unify oracle_query('1, '2)\n"
" and Remote\n"
"when checking the type of the expression at line 25, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('1, '2)\n"
"against the expected type\n"
" Remote">>,
<<"Cannot unify oracle_query('3, '4)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 23, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('3, '4)\n"
"against the expected type\n"
" bytes(32)">>,
<<"Cannot unify oracle_query('5, '6)\n"
" and oracle(int, bool)\n"
"when checking the type of the expression at line 21, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('5, '6)\n"
"against the expected type\n"
" oracle(int, bool)">>,
<<"Cannot unify oracle('7, '8)\n"
" and Remote\n"
"when checking the type of the expression at line 18, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('7, '8)\n"
"against the expected type\n"
" Remote">>,
<<"Cannot unify oracle('9, '10)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 16, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('9, '10)\n"
"against the expected type\n"
" bytes(32)">>,
<<"Cannot unify oracle('11, '12)\n"
" and oracle_query(int, bool)\n"
"when checking the type of the expression at line 14, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('11, '12)\n"
"against the expected type\n"
" oracle_query(int, bool)">>,
<<"Cannot unify address\n"
" and oracle(int, bool)\n"
"when checking the type of the expression at line 11, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n"
" oracle(int, bool)">>,
<<"Cannot unify address\n"
" and Remote\n"
"when checking the type of the expression at line 9, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n"
" Remote">>,
<<"Cannot unify address\n"
" and bytes(32)\n"
"when checking the type of the expression at line 7, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n"
" bytes(32)">>]}
]. ].

View File

@ -48,7 +48,7 @@ all_tokens() ->
%% Literals %% Literals
[ Lit(true), Lit(false) [ Lit(true), Lit(false)
, Tok(id, "foo"), Tok(id, "_"), Tok(con, "Foo") , Tok(id, "foo"), Tok(id, "_"), Tok(con, "Foo")
, Tok(hash, Hash) , Tok(bytes, Hash)
, Tok(int, 1234567890), Tok(hex, 9876543210) , Tok(int, 1234567890), Tok(hex, 9876543210)
, Tok(string, <<"bla\"\\\b\e\f\n\r\t\vbla">>) , Tok(string, <<"bla\"\\\b\e\f\n\r\t\vbla">>)
]. ].
@ -78,7 +78,7 @@ show_token({param, _, P}) -> "@" ++ P;
show_token({string, _, S}) -> fmt(binary_to_list(S)); show_token({string, _, S}) -> fmt(binary_to_list(S));
show_token({int, _, N}) -> fmt(N); show_token({int, _, N}) -> fmt(N);
show_token({hex, _, N}) -> fmt("0x~.16b", N); show_token({hex, _, N}) -> fmt("0x~.16b", N);
show_token({hash, _, <<N:256>>}) -> fmt("#~.16b", N); show_token({bytes, _, <<N:256>>}) -> fmt("#~64.16.0b", N);
show_token({comment, _, S}) -> S; show_token({comment, _, S}) -> S;
show_token({_, _, _}) -> "TODO". show_token({_, _, _}) -> "TODO".

View File

@ -0,0 +1,14 @@
contract Remote =
function foo : () => ()
contract AddressLiterals =
function addr() : address =
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
function oracle() : oracle(int, bool) =
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
function query() : oracle_query(int, bool) =
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
function contr() : Remote =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ

View File

@ -0,0 +1,33 @@
contract Remote =
function foo : () => ()
contract AddressLiterals =
function addr1() : bytes(32) =
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
function addr2() : Remote =
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
function addr3() : oracle(int, bool) =
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
function oracle1() : oracle_query(int, bool) =
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
function oracle2() : bytes(32) =
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
function oracle3() : Remote =
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
function query1() : oracle(int, bool) =
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
function query2() : bytes(32) =
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
function query3() : Remote =
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
function contr1() : address =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
function contr2() : oracle(int, bool) =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
function contr3() : bytes(32) =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ

View File

@ -0,0 +1,18 @@
contract BitcoinAuth =
record state = { nonce : int, owner : bytes(64) }
function init(owner' : bytes(64)) = { nonce = 1, owner = owner' }
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_secp256k1(to_sign(tx_hash, n), state.owner, s)
function to_sign(h : hash, n : int) : hash =
Crypto.blake2b((h, n))
private function require(b : bool, err : string) =
if(!b) abort(err)

View File

@ -53,7 +53,7 @@ contract FundMe =
require(state.total >= state.goal, "Project was not funded") require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary, spend({recipient = state.beneficiary,
amount = Contract.balance }) amount = Contract.balance })
put(state{ beneficiary = #0 }) put(state{ beneficiary = ak_11111111111111111111111111111111273Yts })
private stateful function withdraw_contributor() = private stateful function withdraw_contributor() =
if(state.total >= state.goal) if(state.total >= state.goal)