Add Address.to_contract

Casts an address to a (any) contract type.
This commit is contained in:
Ulf Norell 2019-09-30 14:37:47 +02:00
parent 7f86b7d301
commit 5fc6e18cd2
7 changed files with 55 additions and 17 deletions

View File

@ -60,7 +60,8 @@
-record(is_contract_constraint, -record(is_contract_constraint,
{ contract_t :: utype(), { contract_t :: utype(),
context :: aeso_syntax:expr() %% The address literal context :: {contract_literal, aeso_syntax:expr()} |
{address_to_contract, aeso_syntax:ann()}
}). }).
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}. -type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
@ -83,7 +84,7 @@
-type qname() :: [string()]. -type qname() :: [string()].
-type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. -type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}.
-type type_constraints() :: none | bytes_concat | bytes_split. -type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract.
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
-type type_info() :: {aeso_syntax:ann(), typedef()}. -type type_info() :: {aeso_syntax:ann(), typedef()}.
@ -519,6 +520,7 @@ global_env() ->
%% Conversion %% Conversion
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) }, IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
{"to_contract", FunC(address_to_contract, [Address], A)},
{"is_oracle", Fun1(Address, Bool)}, {"is_oracle", Fun1(Address, Bool)},
{"is_contract", Fun1(Address, Bool)}, {"is_contract", Fun1(Address, Bool)},
{"is_payable", Fun1(Address, Bool)}]) }, {"is_payable", Fun1(Address, Bool)}]) },
@ -1081,7 +1083,7 @@ infer_expr(_Env, Body={oracle_query_id, As, _}) ->
infer_expr(_Env, Body={contract_pubkey, As, _}) -> infer_expr(_Env, Body={contract_pubkey, As, _}) ->
Con = fresh_uvar(As), Con = fresh_uvar(As),
constrain([#is_contract_constraint{ contract_t = Con, constrain([#is_contract_constraint{ contract_t = Con,
context = Body }]), context = {contract_literal, Body} }]),
{typed, As, Body, Con}; {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)};
@ -1653,11 +1655,11 @@ check_record_create_constraints(Env, [C | Cs]) ->
check_is_contract_constraints(_Env, []) -> ok; check_is_contract_constraints(_Env, []) -> ok;
check_is_contract_constraints(Env, [C | Cs]) -> check_is_contract_constraints(Env, [C | Cs]) ->
#is_contract_constraint{ contract_t = Type, context = Lit } = C, #is_contract_constraint{ contract_t = Type, context = Cxt } = C,
Type1 = unfold_types_in_type(Env, instantiate(Type)), Type1 = unfold_types_in_type(Env, instantiate(Type)),
case lookup_type(Env, record_type_name(Type1)) of case lookup_type(Env, record_type_name(Type1)) of
{_, {_Ann, {[], {contract_t, _}}}} -> ok; {_, {_Ann, {[], {contract_t, _}}}} -> ok;
_ -> type_error({not_a_contract_type, Type1, Lit}) _ -> type_error({not_a_contract_type, Type1, Cxt})
end, end,
check_is_contract_constraints(Env, Cs). check_is_contract_constraints(Env, Cs).
@ -2078,6 +2080,9 @@ freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) ->
FunT. FunT.
apply_typesig_constraint(_Ann, none, _FunT) -> ok; apply_typesig_constraint(_Ann, none, _FunT) -> ok;
apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) ->
constrain([#is_contract_constraint{ contract_t = Type,
context = {address_to_contract, Ann}}]);
apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) -> apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
add_bytes_constraint({add_bytes, Ann, concat, A, B, C}); add_bytes_constraint({add_bytes, Ann, concat, A, B, C});
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) -> apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
@ -2176,12 +2181,28 @@ mk_error({not_a_record_type, Type, Why}) ->
Msg = io_lib:format("~s\n", [pp_type("Not a record type: ", Type)]), Msg = io_lib:format("~s\n", [pp_type("Not a record type: ", Type)]),
{Pos, Ctxt} = pp_why_record(Why), {Pos, Ctxt} = pp_why_record(Why),
mk_t_err(Pos, Msg, Ctxt); mk_t_err(Pos, Msg, Ctxt);
mk_error({not_a_contract_type, Type, Lit}) -> mk_error({not_a_contract_type, Type, Cxt}) ->
Msg = io_lib:format("The type ~s is not a contract type\n" Msg =
"when checking that the contract literal at ~s\n~s\n" case Type of
{tvar, _, _} ->
"Unresolved contract type\n";
_ ->
io_lib:format("The type ~s is not a contract type\n", [pp_type("", Type)])
end,
{Pos, Cxt1} =
case Cxt of
{contract_literal, Lit} ->
{pos(Lit),
io_lib:format("when checking that the contract literal\n~s\n"
"has the type\n~s\n", "has the type\n~s\n",
[pp_type("", Type), pp_loc(Lit), pp_expr(" ", Lit), pp_type(" ", Type)]), [pp_expr(" ", Lit), pp_type(" ", Type)])};
mk_t_err(pos(Lit), Msg); {address_to_contract, Ann} ->
{pos(Ann),
io_lib:format("when checking that the call to\n Address.to_contract\n"
"has the type\n~s\n",
[pp_type(" ", Type)])}
end,
mk_t_err(Pos, Msg, Cxt1);
mk_error({non_linear_pattern, Pattern, Nonlinear}) -> mk_error({non_linear_pattern, Pattern, Nonlinear}) ->
Msg = io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n", Msg = io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n",
[plural("", "s", Nonlinear), string:join(Nonlinear, ", "), [plural("", "s", Nonlinear), string:join(Nonlinear, ", "),

View File

@ -32,7 +32,7 @@
map_delete | map_member | map_size | string_length | map_delete | map_member | map_size | string_length |
string_concat | bits_set | bits_clear | bits_test | bits_sum | string_concat | bits_set | bits_clear | bits_test | bits_sum |
bits_intersection | bits_union | bits_difference | bits_intersection | bits_union | bits_difference |
contract_to_address | crypto_verify_sig | crypto_verify_sig_secp256k1 | contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
crypto_sha3 | crypto_sha256 | crypto_blake2b | crypto_sha3 | crypto_sha256 | crypto_blake2b |
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1. crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
@ -200,7 +200,7 @@ builtins() ->
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]}, {"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]}, {["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
{["Int"], [{"to_str", 1}]}, {["Int"], [{"to_str", 1}]},
{["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]} {["Address"], [{"to_str", 1}, {"to_contract", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
], ],
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}} maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|| {NS, Funs} <- Scopes, || {NS, Funs} <- Scopes,
@ -904,6 +904,7 @@ op_builtins() ->
string_length, string_concat, string_sha3, string_sha256, string_blake2b, string_length, string_concat, string_sha3, string_sha256, string_blake2b,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
bits_difference, int_to_str, address_to_str, crypto_verify_sig, bits_difference, int_to_str, address_to_str, crypto_verify_sig,
address_to_contract,
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1 crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
]. ].

View File

@ -494,6 +494,7 @@ is_builtin_fun({qid, _, ["Address", "to_str"]}, _Icode) ->
is_builtin_fun({qid, _, ["Address", "is_oracle"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Address", "is_oracle"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Address", "is_contract"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Address", "is_contract"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Address", "is_payable"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Address", "is_payable"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Address", "to_contract"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Bytes", "to_int"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Bytes", "to_int"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Bytes", "to_str"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Bytes", "to_str"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Bytes", "concat"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Bytes", "concat"]}, _Icode) -> true;
@ -713,6 +714,8 @@ builtin_code(_, {qid, _, ["Address", "is_contract"]}, [Addr], _, _, Icode) ->
builtin_code(_, {qid, _, ["Address", "is_payable"]}, [Addr], _, _, Icode) -> builtin_code(_, {qid, _, ["Address", "is_payable"]}, [Addr], _, _, Icode) ->
prim_call(?PRIM_CALL_ADDR_IS_PAYABLE, #integer{value = 0}, prim_call(?PRIM_CALL_ADDR_IS_PAYABLE, #integer{value = 0},
[ast_body(Addr, Icode)], [word], word); [ast_body(Addr, Icode)], [word], word);
builtin_code(_, {qid, _, ["Address", "to_contract"]}, [Addr], _, _, Icode) ->
ast_body(Addr, Icode);
builtin_code(_, {qid, _, ["Bytes", "to_int"]}, [Bytes], _, _, Icode) -> builtin_code(_, {qid, _, ["Bytes", "to_int"]}, [Bytes], _, _, Icode) ->
{typed, _, _, {bytes_t, _, N}} = Bytes, {typed, _, _, {bytes_t, _, N}} = Bytes,

View File

@ -103,6 +103,7 @@
Op =:= 'ECVERIFY_SECP256K1' orelse Op =:= 'ECVERIFY_SECP256K1' orelse
Op =:= 'ECRECOVER_SECP256K1' orelse Op =:= 'ECRECOVER_SECP256K1' orelse
Op =:= 'CONTRACT_TO_ADDRESS' orelse Op =:= 'CONTRACT_TO_ADDRESS' orelse
Op =:= 'ADDRESS_TO_CONTRACT' orelse
Op =:= 'AUTH_TX_HASH' orelse Op =:= 'AUTH_TX_HASH' orelse
Op =:= 'BYTES_TO_INT' orelse Op =:= 'BYTES_TO_INT' orelse
Op =:= 'BYTES_TO_STR' orelse Op =:= 'BYTES_TO_STR' orelse
@ -611,6 +612,7 @@ op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a); op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a); op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a); op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a); op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
op_to_scode(crypto_verify_sig_secp256k1) -> aeb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a); op_to_scode(crypto_verify_sig_secp256k1) -> aeb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a); op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
@ -851,6 +853,7 @@ attributes(I) ->
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]); {'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
{'AUTH_TX_HASH', A} -> Pure(A, []); {'AUTH_TX_HASH', A} -> Pure(A, []);
{'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_INT', A, B} -> Pure(A, [B]);
{'BYTES_TO_STR', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]);

View File

@ -356,19 +356,19 @@ failing_contracts() ->
, ?TYPE_ERROR(bad_address_literals, , ?TYPE_ERROR(bad_address_literals,
[<<?Pos(32, 5) [<<?Pos(32, 5)
"The type bytes(32) is not a contract type\n" "The type bytes(32) is not a contract type\n"
"when checking that the contract literal at line 32, column 5\n" "when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" bytes(32)">>, " bytes(32)">>,
<<?Pos(30, 5) <<?Pos(30, 5)
"The type oracle(int, bool) is not a contract type\n" "The type oracle(int, bool) is not a contract type\n"
"when checking that the contract literal at line 30, column 5\n" "when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" oracle(int, bool)">>, " oracle(int, bool)">>,
<<?Pos(28, 5) <<?Pos(28, 5)
"The type address is not a contract type\n" "The type address is not a contract type\n"
"when checking that the contract literal at line 28, column 5\n" "when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" address">>, " address">>,
@ -440,7 +440,13 @@ failing_contracts() ->
"when checking the type of the expression at line 7, column 5\n" "when checking the type of the expression at line 7, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n" "against the expected type\n"
" bytes(32)">>]) " bytes(32)">>,
<<?Pos(34, 5),
"The type address is not a contract type\n"
"when checking that the call to\n"
" Address.to_contract\n"
"has the type\n"
" address">>])
, ?TYPE_ERROR(stateful, , ?TYPE_ERROR(stateful,
[<<?Pos(13, 35) [<<?Pos(13, 35)
"Cannot reference stateful function Chain.spend (at line 13, column 35)\nin the definition of non-stateful function fail1.">>, "Cannot reference stateful function Chain.spend (at line 13, column 35)\nin the definition of non-stateful function fail1.">>,

View File

@ -11,4 +11,6 @@ contract AddressLiterals =
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
entrypoint contr() : Remote = entrypoint contr() : Remote =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
entrypoint contr_addr() : Remote =
Address.to_contract(addr())

View File

@ -30,4 +30,6 @@ contract AddressLiterals =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
entrypoint contr3() : bytes(32) = entrypoint contr3() : bytes(32) =
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
entrypoint contr4() : address =
Address.to_contract(Contract.address)