From 5fc6e18cd2ea0dfbac85e1f52ee0976caf4fabb9 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Mon, 30 Sep 2019 14:37:47 +0200 Subject: [PATCH] Add Address.to_contract Casts an address to a (any) contract type. --- src/aeso_ast_infer_types.erl | 43 ++++++++++++++++++------- src/aeso_ast_to_fcode.erl | 5 +-- src/aeso_ast_to_icode.erl | 3 ++ src/aeso_fcode_to_fate.erl | 3 ++ test/aeso_compiler_tests.erl | 14 +++++--- test/contracts/address_literals.aes | 2 ++ test/contracts/bad_address_literals.aes | 2 ++ 7 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 0aa09db..b546620 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -60,7 +60,8 @@ -record(is_contract_constraint, { 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{}. @@ -83,7 +84,7 @@ -type qname() :: [string()]. -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 type_info() :: {aeso_syntax:ann(), typedef()}. @@ -519,6 +520,7 @@ global_env() -> %% Conversion IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) }, AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, + {"to_contract", FunC(address_to_contract, [Address], A)}, {"is_oracle", Fun1(Address, Bool)}, {"is_contract", 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, _}) -> Con = fresh_uvar(As), constrain([#is_contract_constraint{ contract_t = Con, - context = Body }]), + context = {contract_literal, Body} }]), {typed, As, Body, Con}; infer_expr(_Env, Body={id, 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, [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)), case lookup_type(Env, record_type_name(Type1)) of {_, {_Ann, {[], {contract_t, _}}}} -> ok; - _ -> type_error({not_a_contract_type, Type1, Lit}) + _ -> type_error({not_a_contract_type, Type1, Cxt}) end, check_is_contract_constraints(Env, Cs). @@ -2078,6 +2080,9 @@ freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) -> FunT. 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}) -> add_bytes_constraint({add_bytes, Ann, concat, A, B, C}); 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)]), {Pos, Ctxt} = pp_why_record(Why), mk_t_err(Pos, Msg, Ctxt); -mk_error({not_a_contract_type, Type, Lit}) -> - Msg = 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)]), - mk_t_err(pos(Lit), Msg); +mk_error({not_a_contract_type, Type, Cxt}) -> + Msg = + 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", + [pp_expr(" ", Lit), pp_type(" ", Type)])}; + {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}) -> Msg = io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n", [plural("", "s", Nonlinear), string:join(Nonlinear, ", "), diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index e795594..35d5d06 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -32,7 +32,7 @@ map_delete | map_member | map_size | string_length | string_concat | bits_set | bits_clear | bits_test | bits_sum | 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_ecverify_secp256k1 | crypto_ecrecover_secp256k1. @@ -200,7 +200,7 @@ builtins() -> {"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]}, {["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 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}} || {NS, Funs} <- Scopes, @@ -904,6 +904,7 @@ op_builtins() -> string_length, string_concat, string_sha3, string_sha256, string_blake2b, bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_difference, int_to_str, address_to_str, crypto_verify_sig, + address_to_contract, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1 ]. diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index d369b30..acf68ea 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -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_contract"]}, _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_str"]}, _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) -> prim_call(?PRIM_CALL_ADDR_IS_PAYABLE, #integer{value = 0}, [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) -> {typed, _, _, {bytes_t, _, N}} = Bytes, diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 56ac1aa..2fcce83 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -103,6 +103,7 @@ Op =:= 'ECVERIFY_SECP256K1' orelse Op =:= 'ECRECOVER_SECP256K1' orelse Op =:= 'CONTRACT_TO_ADDRESS' orelse + Op =:= 'ADDRESS_TO_CONTRACT' orelse Op =:= 'AUTH_TX_HASH' orelse Op =:= 'BYTES_TO_INT' 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(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(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_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); @@ -851,6 +853,7 @@ attributes(I) -> {'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); + {'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]); {'AUTH_TX_HASH', A} -> Pure(A, []); {'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 9d6def9..776746f 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -356,19 +356,19 @@ failing_contracts() -> , ?TYPE_ERROR(bad_address_literals, [<>, <>, <>, @@ -440,7 +440,13 @@ failing_contracts() -> "when checking the type of the expression at line 7, column 5\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" "against the expected type\n" - " bytes(32)">>]) + " bytes(32)">>, + <>]) , ?TYPE_ERROR(stateful, [<>, diff --git a/test/contracts/address_literals.aes b/test/contracts/address_literals.aes index fd69227..15007cf 100644 --- a/test/contracts/address_literals.aes +++ b/test/contracts/address_literals.aes @@ -11,4 +11,6 @@ contract AddressLiterals = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY entrypoint contr() : Remote = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ + entrypoint contr_addr() : Remote = + Address.to_contract(addr()) diff --git a/test/contracts/bad_address_literals.aes b/test/contracts/bad_address_literals.aes index 216ab57..1750846 100644 --- a/test/contracts/bad_address_literals.aes +++ b/test/contracts/bad_address_literals.aes @@ -30,4 +30,6 @@ contract AddressLiterals = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ entrypoint contr3() : bytes(32) = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ + entrypoint contr4() : address = + Address.to_contract(Contract.address)