diff --git a/CHANGELOG.md b/CHANGELOG.md index a41d621..1e8044c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- New builtin function `Crypto.ecrecover_secp256k1: (hash, bytes(65)) => bytes(32)` + for recovering Ethereum address from message hash and signature. + ### Changed - New syntax for tuple types. Now 0-tuple type is encoded as `unit` instead of `()` and regular tuples are encoded by interspersing inner types with `*`, for instance `int * string`. diff --git a/rebar.config b/rebar.config index 3f11305..e399269 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"af6224c"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"3954bd2"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index dadefca..f0dfdfa 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"af6224cb3b3732562df58485b2bd2a0534d74f2a"}}, + {ref,"3954bd22daa24d4a82a8349325d115cd7b7d1750"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 0f506d8..2bd79fa 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -83,7 +83,7 @@ -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type type_info() :: {aeso_syntax:ann(), typedef()}. --type var_info() :: {aeso_syntax:ann(), type()}. +-type var_info() :: {aeso_syntax:ann(), utype()}. -type fun_env() :: [{name(), fun_info()}]. -type type_env() :: [{name(), type_info()}]. @@ -139,11 +139,11 @@ on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) -> on_scopes(Env = #env{ scopes = Scopes }, Fun) -> Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }. --spec bind_var(aeso_syntax:id(), type(), env()) -> env(). +-spec bind_var(aeso_syntax:id(), utype(), env()) -> env(). bind_var({id, Ann, X}, T, Env) -> Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }. --spec bind_vars([{aeso_syntax:id(), type()}], env()) -> env(). +-spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env(). bind_vars([], Env) -> Env; bind_vars([{X, T} | Vars], Env) -> bind_vars(Vars, bind_var(X, T, Env)). @@ -460,7 +460,8 @@ global_env() -> %% Crypto/Curve operations CryptoScope = #scope { funs = MkDefs( - [{"ecverify", Fun([Hash, Address, SignId], Bool)}, + [{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Hash)}, + {"ecverify", Fun([Hash, Address, SignId], Bool)}, {"ecverify_secp256k1", Fun([Hash, Bytes(64), Bytes(64)], Bool)}, {"sha3", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)}, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index cc51155..4572b02 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -33,7 +33,8 @@ string_concat | bits_set | bits_clear | bits_test | bits_sum | bits_intersection | bits_union | bits_difference | contract_to_address | crypto_ecverify | crypto_ecverify_secp256k1 | - crypto_sha3 | crypto_sha256 | crypto_blake2b. + crypto_sha3 | crypto_sha256 | crypto_blake2b | + crypto_ecrecover_secp256k1. -type flit() :: {int, integer()} | {string, binary()} @@ -187,8 +188,8 @@ builtins() -> {"revoke", 3}]}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, - {["Crypto"], [{"ecverify", 3}, {"ecverify_secp256k1", 3}, {"sha3", 1}, - {"sha256", 1}, {"blake2b", 1}]}, + {["Crypto"], [{"ecrecover_secp256k1", 2}, {"ecverify", 3}, {"ecverify_secp256k1", 3}, + {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, {["Auth"], [{"tx_hash", none}]}, {["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, {["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2}, @@ -818,7 +819,9 @@ 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_ecverify, - crypto_ecverify_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b]. + crypto_ecverify_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, + crypto_ecrecover_secp256k1 + ]. builtin_to_fcode(require, [Cond, Msg]) -> make_if(Cond, {tuple, []}, {builtin, abort, [Msg]}); diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 6aac6c1..82fee1f 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -350,6 +350,11 @@ ast_body({map, Ann, Map, [Upd | Upds]}, Icode) -> ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode); %% Crypto +ast_body(?qid_app(["Crypto", "ecrecover_secp256k1"], [Msg, Sig], _, _), Icode) -> + prim_call(?PRIM_CALL_CRYPTO_ECRECOVER_SECP256K1, #integer{value = 0}, + [ast_body(Msg, Icode), ast_body(Sig, Icode)], + [word, bytes_t(65)], word); + ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) -> prim_call(?PRIM_CALL_CRYPTO_ECVERIFY, #integer{value = 0}, [ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)], diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index fae063f..9f54d87 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -574,14 +574,13 @@ pp(Code, Options, Option, PPFun) -> ok end. - %% ------------------------------------------------------------------- -%% TODO: Tempoary parser hook below... +-spec parse_stdlib() -> none() | aeso_syntax:ast(). parse_stdlib() -> lists:foldr( fun ({Lib, LibCode}, Acc) -> - parse(LibCode, [{src_file, Lib}]) ++ Acc + parse(LibCode, [{src_file, binary_to_list(Lib)}]) ++ Acc end, [], aeso_stdlib:stdlib_list()). @@ -593,8 +592,11 @@ sophia_type_to_typerep(String) -> catch _:_ -> {error, bad_type} end. +-spec parse(string(), aeso_compiler:options()) -> none() | aeso_syntax:ast(). parse(Text, Options) -> parse(Text, sets:new(), Options). + +-spec parse(string(), sets:set(), aeso_compiler:options()) -> none() | aeso_syntax:ast(). parse(Text, Included, Options) -> %% Try and return something sensible here! case aeso_parser:string(Text, Included, Options) of @@ -616,6 +618,7 @@ parse(Text, Included, Options) -> parse_error(Pos, io_lib:format("could not find include file '~s'", [File])) end. +-spec parse_error(aeso_parse_lib:pos(), string()) -> none(). parse_error(Pos, ErrorString) -> Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]), error({parse_errors, [Error]}). diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 1762263..5dc930f 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -98,17 +98,18 @@ Op =:= 'SHA3' orelse Op =:= 'SHA256' orelse Op =:= 'BLAKE2B' orelse - Op =:= 'ECVERIFY' orelse - Op =:= 'ECVERIFY_SECP256K1' orelse + Op =:= 'ECRECOVER_SECP256K1' orelse + Op =:= 'ECVERIFY' orelse + Op =:= 'ECVERIFY_SECP256K1' orelse Op =:= 'CONTRACT_TO_ADDRESS' orelse - Op =:= 'AUTH_TX_HASH' orelse - Op =:= 'BYTES_TO_INT' orelse - Op =:= 'BYTES_TO_STR' orelse - Op =:= 'ORACLE_CHECK' orelse - Op =:= 'ORACLE_CHECK_QUERY' orelse - Op =:= 'IS_ORACLE' orelse - Op =:= 'IS_CONTRACT' orelse - Op =:= 'CREATOR' orelse + Op =:= 'AUTH_TX_HASH' orelse + Op =:= 'BYTES_TO_INT' orelse + Op =:= 'BYTES_TO_STR' orelse + Op =:= 'ORACLE_CHECK' orelse + Op =:= 'ORACLE_CHECK_QUERY' orelse + Op =:= 'IS_ORACLE' orelse + Op =:= 'IS_CONTRACT' orelse + Op =:= 'CREATOR' orelse false)). -record(env, { contract, vars = [], locals = [], tailpos = true }). @@ -591,15 +592,16 @@ op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a); 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(crypto_ecverify) -> aeb_fate_ops:ecverify(?a, ?a, ?a, ?a); -op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a); -op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); -op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); -op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); -op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a); -op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a); -op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a). +op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a); +op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a, ?a, ?a); +op_to_scode(crypto_ecverify) -> aeb_fate_ops:ecverify(?a, ?a, ?a, ?a); +op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a); +op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); +op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); +op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); +op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a); +op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a); +op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a). %% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% easier, and specialize to PUSH (which is cheaper) at the end. @@ -819,6 +821,7 @@ attributes(I) -> {'SHA3', A, B} -> Pure(A, [B]); {'SHA256', A, B} -> Pure(A, [B]); {'BLAKE2B', A, B} -> Pure(A, [B]); + {'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]); {'ECVERIFY', A, B, C, D} -> Pure(A, [B, C, D]); {'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index f0bd60f..1dd0ca0 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -22,8 +22,7 @@ string(String) -> string(String, sets:new(), []). - --spec string(string(), compiler:options()) -> parse_result(). +-spec string(string(), aeso_compiler:options()) -> parse_result(). string(String, Opts) -> case lists:keyfind(src_file, 1, Opts) of {src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts);