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:
commit
51b63f9559
@ -3,7 +3,7 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
|
||||
{ref, "56cf62b"}}}
|
||||
{ref, "e8253b0"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
{tag, "2.8.0"}}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"56cf62b48753a61e30b81d805eee5ec36959b688"}},
|
||||
{ref,"e8253b09709f1595d8bd6a1756a0ce93185c6518"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
|
@ -24,6 +24,7 @@
|
||||
%% Types
|
||||
-record(app_t, {ann,id,fields}).
|
||||
-record(tuple_t, {ann,args}).
|
||||
-record(bytes_t, {ann,len}).
|
||||
-record(record_t, {fields}).
|
||||
-record(field_t, {ann,id,type}).
|
||||
-record(alias_t, {type}).
|
||||
@ -43,7 +44,7 @@
|
||||
-record(bool, {ann,bool}).
|
||||
-record(int, {ann,value}).
|
||||
-record(string, {ann,bin}).
|
||||
-record(hash, {ann,hash}).
|
||||
-record(bytes, {ann,bin}).
|
||||
-record(tuple, {ann,args}).
|
||||
-record(list, {ann,args}).
|
||||
-record(app, {ann,func,args}).
|
||||
@ -133,6 +134,8 @@ encode_type(#qcon{names=Ns}) ->
|
||||
encode_type(#tuple_t{args=As}) ->
|
||||
Eas = encode_types(As),
|
||||
[{<<"tuple">>,Eas}];
|
||||
encode_type(#bytes_t{len=Len}) ->
|
||||
{<<"bytes">>, Len};
|
||||
encode_type(#record_t{fields=Fs}) ->
|
||||
Efs = encode_fields(Fs),
|
||||
[{<<"record">>,Efs}];
|
||||
@ -208,7 +211,7 @@ encode_expr(#typed{expr=E}) ->
|
||||
encode_expr(#bool{bool=B}) -> B;
|
||||
encode_expr(#int{value=V}) -> V;
|
||||
encode_expr(#string{bin=B}) -> B;
|
||||
encode_expr(#hash{hash=H}) -> H;
|
||||
encode_expr(#bytes{bin=B}) -> B;
|
||||
encode_expr(#tuple{args=As}) ->
|
||||
Eas = encode_exprs(As),
|
||||
[{<<"tuple">>,Eas}];
|
||||
|
@ -56,7 +56,12 @@
|
||||
, fields :: [aeso_syntax:id()]
|
||||
, 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,
|
||||
{ ann :: aeso_syntax:ann()
|
||||
@ -341,6 +346,7 @@ global_env() ->
|
||||
Address = {id, Ann, "address"},
|
||||
Hash = {id, Ann, "hash"},
|
||||
Bits = {id, Ann, "bits"},
|
||||
Bytes = fun(Len) -> {bytes_t, Ann, Len} end,
|
||||
Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end,
|
||||
Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end,
|
||||
Unit = {tuple_t, Ann, []},
|
||||
@ -373,7 +379,9 @@ global_env() ->
|
||||
{"abort", Fun1(String, A)}])
|
||||
, types = MkDefs(
|
||||
[{"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},
|
||||
{"oracle", 2}, {"oracle_query", 2}
|
||||
]) },
|
||||
@ -439,6 +447,7 @@ global_env() ->
|
||||
CryptoScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"ecverify", Fun([Hash, Address, SignId], Bool)},
|
||||
{"ecverify_secp256k1", Fun([Hash, Bytes(64), Bytes(64)], Bool)},
|
||||
{"sha3", Fun1(A, Hash)},
|
||||
{"sha256", 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, []).
|
||||
|
||||
-type option() :: permissive_address_literals | return_env.
|
||||
-type option() :: return_env.
|
||||
|
||||
-spec init_env(list(option())) -> 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) ->
|
||||
ensure_base_type(Type, Arity),
|
||||
{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) ->
|
||||
Types1 = [ check_type(Env, T, 0) || T <- 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"}};
|
||||
infer_expr(_Env, Body={string, As, _}) ->
|
||||
{typed, As, Body, {id, As, "string"}};
|
||||
infer_expr(_Env, Body={hash, As, Hash}) ->
|
||||
case byte_size(Hash) of
|
||||
32 -> {typed, As, Body, {id, As, "address"}};
|
||||
64 -> {typed, As, Body, {id, As, "signature"}}
|
||||
end;
|
||||
infer_expr(_Env, Body={bytes, As, Bin}) ->
|
||||
{typed, As, Body, {bytes_t, As, byte_size(Bin)}};
|
||||
infer_expr(_Env, Body={account_pubkey, As, _}) ->
|
||||
{typed, As, Body, {id, As, "address"}};
|
||||
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, "_"}) ->
|
||||
{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),
|
||||
{typed, As, QName, Type};
|
||||
infer_expr(Env, Id = {qid, As, _}) ->
|
||||
{QName, Type} = lookup_name(Env, As, Id),
|
||||
{typed, As, QName, Type};
|
||||
infer_expr(Env, Id = {con, As, _}) ->
|
||||
infer_expr(Env, Id = {Tag, As, _}) when Tag == con; Tag == qcon ->
|
||||
{QName, Type} = lookup_name(Env, As, Id, [freshen]),
|
||||
{typed, As, QName, Type};
|
||||
infer_expr(Env, {unit, As}) ->
|
||||
@ -1348,6 +1369,16 @@ check_record_create_constraints(Env, [C | Cs]) ->
|
||||
end,
|
||||
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.
|
||||
solve_field_constraints(Env, Constraints) ->
|
||||
%% First look for record fields that appear in only one type definition
|
||||
@ -1446,9 +1477,12 @@ solve_known_record_types(Env, Constraints) ->
|
||||
DerefConstraints--SolvedConstraints.
|
||||
|
||||
destroy_and_report_unsolved_field_constraints(Env) ->
|
||||
{FieldCs, CreateCs} =
|
||||
{FieldCs, OtherCs} =
|
||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||
get_field_constraints()),
|
||||
{CreateCs, ContractCs} =
|
||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||
OtherCs),
|
||||
Unknown = solve_known_record_types(Env, FieldCs),
|
||||
if Unknown == [] -> ok;
|
||||
true ->
|
||||
@ -1458,6 +1492,7 @@ destroy_and_report_unsolved_field_constraints(Env) ->
|
||||
end
|
||||
end,
|
||||
check_record_create_constraints(Env, CreateCs),
|
||||
check_is_contract_constraints(Env, ContractCs),
|
||||
destroy_field_constraints(),
|
||||
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)};
|
||||
unfold_types_in_type(Env, {constr_t, Ann, Con, 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) ->
|
||||
list_to_tuple(unfold_types_in_type(Env, tuple_to_list(T), Options));
|
||||
unfold_types_in_type(Env, [H|T], Options) ->
|
||||
@ -1638,6 +1675,8 @@ unify1(_Env, {qid, _, Name}, {qid, _, Name}, _When) ->
|
||||
true;
|
||||
unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
|
||||
true;
|
||||
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
||||
true;
|
||||
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) ->
|
||||
unify(Env, Named1, Named2, When) andalso
|
||||
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) ->
|
||||
unify(Env, A, T, When);
|
||||
unify1(_Env, A, B, When) ->
|
||||
Ok =
|
||||
case get_option(permissive_address_literals, false) of
|
||||
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.
|
||||
cannot_unify(A, B, When),
|
||||
false.
|
||||
|
||||
dereference(T = {uvar, _, R}) ->
|
||||
case ets_lookup(type_vars, R) of
|
||||
@ -1703,6 +1724,7 @@ occurs_check1(_, {con, _, _}) -> false;
|
||||
occurs_check1(_, {qid, _, _}) -> false;
|
||||
occurs_check1(_, {qcon, _, _}) -> false;
|
||||
occurs_check1(_, {tvar, _, _}) -> false;
|
||||
occurs_check1(_, {bytes_t, _, _}) -> false;
|
||||
occurs_check1(R, {fun_t, _, Named, Args, Res}) ->
|
||||
occurs_check(R, [Res, Named | Args]);
|
||||
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)]);
|
||||
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)]);
|
||||
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}) ->
|
||||
Plural = [ $s || length(Nonlinear) > 1 ],
|
||||
io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n",
|
||||
@ -2043,6 +2070,8 @@ pp({qid, _, Name}) ->
|
||||
string:join(Name, ".");
|
||||
pp({con, _, Name}) ->
|
||||
Name;
|
||||
pp({qcon, _, Name}) ->
|
||||
string:join(Name, ".");
|
||||
pp({uvar, _, Ref}) ->
|
||||
%% Show some unique representation
|
||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||
@ -2050,6 +2079,8 @@ pp({tvar, _, Name}) ->
|
||||
Name;
|
||||
pp({tuple_t, _, Cpts}) ->
|
||||
["(", pp(Cpts), ")"];
|
||||
pp({bytes_t, _, Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
pp({app_t, _, T, []}) ->
|
||||
pp(T);
|
||||
pp({app_t, _, Type, Args}) ->
|
||||
|
@ -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)],
|
||||
[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) ->
|
||||
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, 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};
|
||||
ast_body({int, _, Value}, _Icode) ->
|
||||
#integer{value = Value};
|
||||
ast_body({hash, _, Hash}, _Icode) ->
|
||||
case Hash of
|
||||
<<Value:32/unit:8>> -> %% address
|
||||
#integer{value = Value};
|
||||
<<Hi:32/unit:8, Lo:32/unit:8>> -> %% signature
|
||||
#tuple{cpts = [#integer{value = Hi},
|
||||
#integer{value = Lo}]}
|
||||
ast_body({bytes, _, Bin}, _Icode) ->
|
||||
case aeb_memory:binary_to_words(Bin) of
|
||||
[Word] -> #integer{value = Word};
|
||||
Words -> #tuple{cpts = [#integer{value = W} || W <- Words]}
|
||||
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) ->
|
||||
Cpts = [size(Bin) | aeb_memory:binary_to_words(Bin)],
|
||||
#tuple{cpts = [#integer{value=X} || X <- Cpts]};
|
||||
@ -688,6 +696,8 @@ ast_typerep({qid, _, Name}, Icode) ->
|
||||
lookup_type_id(Name, [], Icode);
|
||||
ast_typerep({con, _, _}, _) ->
|
||||
word; %% Contract type
|
||||
ast_typerep({bytes_t, _, Len}, _) ->
|
||||
{bytes, Len};
|
||||
ast_typerep({app_t, _, {id, _, Name}, Args}, Icode) ->
|
||||
ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ],
|
||||
lookup_type_id(Name, ArgReps, Icode);
|
||||
@ -715,8 +725,8 @@ ast_typerep({variant_t, Cons}, Icode) ->
|
||||
ttl_t(Icode) ->
|
||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||
|
||||
sign_t() ->
|
||||
{tuple, [word, word]}.
|
||||
sign_t() -> bytes_t(64).
|
||||
bytes_t(Len) -> {bytes, Len}.
|
||||
|
||||
get_signature_arg(Args0) ->
|
||||
NamedArgs = [Arg || Arg = {named_arg, _, _, _} <- Args0],
|
||||
@ -750,6 +760,8 @@ type_value({list, A}) ->
|
||||
type_value({tuple, As}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_TUPLE_TAG },
|
||||
#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}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_VARIANT_TAG },
|
||||
#list{ elems = [ #list{ elems = [ type_value(A) || A <- As ] } || As <- Cs ] }] };
|
||||
|
@ -104,13 +104,12 @@ from_string(ContractString, Options) ->
|
||||
%% General programming errors in the compiler just signal error.
|
||||
end.
|
||||
|
||||
-spec string_to_icode(string(), [option() | permissive_address_literals]) -> map().
|
||||
string_to_icode(ContractString, Options0) ->
|
||||
{InferOptions, Options} = lists:partition(fun(Opt) -> Opt == permissive_address_literals end, Options0),
|
||||
-spec string_to_icode(string(), [option()]) -> map().
|
||||
string_to_icode(ContractString, Options) ->
|
||||
Ast = parse(ContractString, Options),
|
||||
pp_sophia_code(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),
|
||||
Icode = ast_to_icode(TypedAst, Options),
|
||||
pp_icode(Icode, Options),
|
||||
@ -151,11 +150,11 @@ check_call(Source, FunName, Args, Options) ->
|
||||
|
||||
check_call(ContractString0, FunName, Args, Options, PatchFun) ->
|
||||
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),
|
||||
ContractString = insert_call_function(ContractString0, FunName, Args, Options),
|
||||
#{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),
|
||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
||||
RetVMType = case RetType of
|
||||
@ -257,19 +256,21 @@ to_sophia_value(ContractString, FunName, ok, Data, Options) ->
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
end.
|
||||
|
||||
address_literal(N) -> {hash, [], <<N:256>>}. % TODO
|
||||
address_literal(Type, N) -> {Type, [], <<N:256>>}.
|
||||
|
||||
%% TODO: somewhere else
|
||||
-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, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(N);
|
||||
translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(N);
|
||||
translate_vm_value(word, {id, _, "hash"}, N) -> {hash, [], <<N:256>>};
|
||||
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, _, "bool"}, N) -> {bool, [], N /= 0};
|
||||
translate_vm_value({tuple, [word, word]}, {id, _, "signature"}, {tuple, [Hi, Lo]}) ->
|
||||
{hash, [], <<Hi:256, Lo:256>>};
|
||||
translate_vm_value(word, {id, _, "address"}, N) -> address_literal(account_pubkey, 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(oracle_query_id, N);
|
||||
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, _, "bits"}, N) -> error({todo, bits, N});
|
||||
translate_vm_value(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
|
||||
translate_vm_value({bytes, Len}, {bytes_t, _, Len}, Val) when Len =< 32 ->
|
||||
{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({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, 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]}) ->
|
||||
<<Str:Len/binary, _/binary>> = << <<W:256>> || {integer, W} <- Words >>,
|
||||
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(T, V) || V <- Vs ];
|
||||
icode_to_term({tuple, Ts}, {tuple, Vs}) ->
|
||||
|
@ -136,11 +136,15 @@ type200() ->
|
||||
type300() -> type400().
|
||||
|
||||
type400() ->
|
||||
?RULE(typeAtom(), optional(type_args()),
|
||||
choice(
|
||||
[?RULE(typeAtom(), optional(type_args()),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{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() ->
|
||||
?LAZY_P(choice(
|
||||
@ -203,8 +207,8 @@ exprAtom() ->
|
||||
?LAZY_P(begin
|
||||
Expr = ?LAZY_P(expr()),
|
||||
choice(
|
||||
[ id(), con(), token(qid), token(qcon)
|
||||
, token(hash), token(string), token(char)
|
||||
[ id_or_addr(), con(), token(qid), token(qcon)
|
||||
, token(bytes), token(string), token(char)
|
||||
, token(int)
|
||||
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
|
||||
, {bool, keyword(true), true}
|
||||
@ -326,6 +330,26 @@ token(Tag) ->
|
||||
{Tok, {Line, Col}, Val} -> {Tok, pos_ann(Line, Col), Val}
|
||||
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 ----------------------------------------------------------------
|
||||
|
||||
keyword(K) -> ann(tok(K)).
|
||||
@ -457,7 +481,7 @@ parse_pattern(E = {id, _, _}) -> E;
|
||||
parse_pattern(E = {unit, _}) -> E;
|
||||
parse_pattern(E = {int, _, _}) -> E;
|
||||
parse_pattern(E = {bool, _, _}) -> E;
|
||||
parse_pattern(E = {hash, _, _}) -> E;
|
||||
parse_pattern(E = {bytes, _, _}) -> E;
|
||||
parse_pattern(E = {string, _, _}) -> E;
|
||||
parse_pattern(E = {char, _, _}) -> E;
|
||||
parse_pattern(E) -> bad_expr_err("Not a valid pattern", E).
|
||||
|
@ -236,6 +236,8 @@ type({app_t, _, Type, Args}) ->
|
||||
beside(type(Type), tuple_type(Args));
|
||||
type({tuple_t, _, Args}) ->
|
||||
tuple_type(Args);
|
||||
type({bytes_t, _, Len}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||
%% Drop the default value
|
||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||
@ -319,8 +321,17 @@ expr_p(_, E = {int, _, N}) ->
|
||||
end,
|
||||
text(S);
|
||||
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(_, {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(_, {string, _, S}) -> term(binary_to_list(S));
|
||||
expr_p(_, {char, _, C}) ->
|
||||
|
@ -20,7 +20,7 @@ lexer() ->
|
||||
CON = [UPPER, "[a-zA-Z0-9_]*"],
|
||||
INT = [DIGIT, "+"],
|
||||
HEX = ["0x", HEXDIGIT, "+"],
|
||||
HASH = ["#", HEXDIGIT, "+"],
|
||||
BYTES = ["#", HEXDIGIT, "+"],
|
||||
WS = "[\\000-\\ ]+",
|
||||
ID = [LOWER, "[a-zA-Z0-9_']*"],
|
||||
TVAR = ["'", ID],
|
||||
@ -54,7 +54,7 @@ lexer() ->
|
||||
, {STRING, token(string, fun parse_string/1)}
|
||||
, {HEX, token(hex, fun parse_hex/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!)
|
||||
, {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_hash("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
case length(Chars) > 64 of %% 64 hex digits = 32 bytes
|
||||
true -> <<N:64/unit:8>>; %% signature
|
||||
false -> <<N:32/unit:8>> %% address
|
||||
end.
|
||||
parse_bytes("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
Digits = (length(Chars) + 1) div 2,
|
||||
<<N:Digits/unit:8>>.
|
||||
|
||||
|
@ -60,6 +60,7 @@
|
||||
-type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()}
|
||||
| {app_t, ann(), type(), [type()]}
|
||||
| {tuple_t, ann(), [type()]}
|
||||
| {bytes_t, ann(), integer()}
|
||||
| id() | qid()
|
||||
| con() | qcon() %% contracts
|
||||
| tvar().
|
||||
@ -70,6 +71,10 @@
|
||||
:: {int, ann(), integer()}
|
||||
| {bool, ann(), true | false}
|
||||
| {hash, ann(), binary()}
|
||||
| {account_pubkey, binary()}
|
||||
| {contract_pubkey, binary()}
|
||||
| {oracle_pubkey, binary()}
|
||||
| {oracle_query_id, binary()}
|
||||
| {unit, ann()}
|
||||
| {string, ann(), binary()}
|
||||
| {char, ann(), integer()}.
|
||||
|
@ -4,6 +4,9 @@
|
||||
-compile(export_all).
|
||||
|
||||
-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) ->
|
||||
Parent = self(),
|
||||
@ -97,7 +100,15 @@ calldata_test() ->
|
||||
Map = #{ <<"a">> => 4 },
|
||||
[{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] =
|
||||
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.
|
||||
|
||||
calldata_init_test() ->
|
||||
@ -126,7 +137,9 @@ parameterized_contract(FunName, Types) ->
|
||||
|
||||
parameterized_contract(ExtraCode, FunName, Types) ->
|
||||
lists:flatten(
|
||||
["contract Dummy =\n",
|
||||
["contract Remote =\n"
|
||||
" function bla : () => ()\n\n"
|
||||
"contract Dummy =\n",
|
||||
ExtraCode, "\n",
|
||||
" type an_alias('a) = (string, 'a)\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"
|
||||
" Oracle.get_question(o, q)\n",
|
||||
{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.
|
||||
|
||||
permissive_literals_fail_test() ->
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
-module(aeso_compiler_tests).
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%% Very simply test compile the given contracts. Only basic checks
|
||||
@ -16,10 +18,14 @@
|
||||
simple_compile_test_() ->
|
||||
[ {"Testing the " ++ ContractName ++ " contract",
|
||||
fun() ->
|
||||
#{byte_code := ByteCode,
|
||||
contract_source := _,
|
||||
type_info := _} = compile(ContractName),
|
||||
?assertMatch(Code when is_binary(Code), ByteCode)
|
||||
case compile(ContractName) of
|
||||
#{byte_code := ByteCode,
|
||||
contract_source := _,
|
||||
type_info := _} -> ?assertMatch(Code when is_binary(Code), ByteCode);
|
||||
ErrBin ->
|
||||
io:format("\n~s", [ErrBin]),
|
||||
error(ErrBin)
|
||||
end
|
||||
end} || ContractName <- compilable_contracts() ] ++
|
||||
[ {"Testing error messages of " ++ ContractName,
|
||||
fun() ->
|
||||
@ -98,7 +104,9 @@ compilable_contracts() ->
|
||||
"state_handling",
|
||||
"events",
|
||||
"include",
|
||||
"basic_auth"
|
||||
"basic_auth",
|
||||
"bitcoin_auth",
|
||||
"address_literals"
|
||||
].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
@ -218,4 +226,80 @@ failing_contracts() ->
|
||||
, {"bad_include_and_ns",
|
||||
[<<"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.">>]}
|
||||
, {"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)">>]}
|
||||
].
|
||||
|
@ -48,7 +48,7 @@ all_tokens() ->
|
||||
%% Literals
|
||||
[ Lit(true), Lit(false)
|
||||
, Tok(id, "foo"), Tok(id, "_"), Tok(con, "Foo")
|
||||
, Tok(hash, Hash)
|
||||
, Tok(bytes, Hash)
|
||||
, Tok(int, 1234567890), Tok(hex, 9876543210)
|
||||
, 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({int, _, N}) -> fmt(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({_, _, _}) -> "TODO".
|
||||
|
||||
|
14
test/contracts/address_literals.aes
Normal file
14
test/contracts/address_literals.aes
Normal 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
|
||||
|
33
test/contracts/bad_address_literals.aes
Normal file
33
test/contracts/bad_address_literals.aes
Normal 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
|
||||
|
18
test/contracts/bitcoin_auth.aes
Normal file
18
test/contracts/bitcoin_auth.aes
Normal 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)
|
@ -53,7 +53,7 @@ contract FundMe =
|
||||
require(state.total >= state.goal, "Project was not funded")
|
||||
spend({recipient = state.beneficiary,
|
||||
amount = Contract.balance })
|
||||
put(state{ beneficiary = #0 })
|
||||
put(state{ beneficiary = ak_11111111111111111111111111111111273Yts })
|
||||
|
||||
private stateful function withdraw_contributor() =
|
||||
if(state.total >= state.goal)
|
||||
|
Loading…
x
Reference in New Issue
Block a user