Add aeser_contract_code #22

Merged
zxq9 merged 1 commits from PT-168026424-prepare_sophia_4_0_RC1 into master 2019-08-23 02:44:36 +09:00
4 changed files with 154 additions and 1 deletions

View File

@ -0,0 +1,3 @@
-define(SOPHIA_CONTRACT_VSN_1, 1).
-define(SOPHIA_CONTRACT_VSN_2, 2).
-define(SOPHIA_CONTRACT_VSN_3, 3).

View File

@ -1,3 +1,4 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {base58, {git, "https://github.com/aeternity/erl-base58.git", {ref, "60a3356"}}} {deps, [ {base58, {git, "https://github.com/aeternity/erl-base58.git", {ref, "60a3356"}}}
, {enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "26180f4"}}}
]}. ]}.

114
src/aeser_contract_code.erl Normal file
View File

@ -0,0 +1,114 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Serialization of contract code
%%% @end
%%%-------------------------------------------------------------------
-module(aeser_contract_code).
-include("aeser_contract_code.hrl").
-export([ deserialize/1
, serialize/1
, serialize/2 ]).
-spec serialize(map()) -> binary().
serialize(CodeMap) ->
serialize(CodeMap, ?SOPHIA_CONTRACT_VSN_3).
-spec serialize(map(), non_neg_integer()) -> binary().
serialize(CodeMap = #{ byte_code := ByteCode
, type_info := TypeInfo }, SophiaContractVersion) ->
%% Source hash
SourceHash = case CodeMap of
#{ source_hash := SHash } -> SHash;
#{ contract_source := SrcStr } ->
{ok, SHash} = enacl:generichash(32, list_to_binary(SrcStr)),
SHash
end,
%% Compiler version
Version = maps:get(compiler_version, CodeMap, <<"unknown">>),
BinVersion = if is_integer(Version) -> integer_to_binary(Version);
is_binary(Version) -> Version
end,
%% Payable
Payable = maps:get(payable, CodeMap, true),
Fields = [ {source_hash, SourceHash}
, {type_info, TypeInfo}
, {byte_code, ByteCode} ] ++
[ {compiler_version, BinVersion}
|| SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_1 ] ++
[ {payable, Payable}
|| SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_2 ],
aeser_chain_objects:serialize(compiler_sophia,
SophiaContractVersion,
serialization_template(SophiaContractVersion),
Fields).
-spec deserialize(binary()) -> map().
deserialize(Binary) ->
case aeser_chain_objects:deserialize_type_and_vsn(Binary) of
{compiler_sophia = Type, ?SOPHIA_CONTRACT_VSN_1 = Vsn, _Rest} ->
Template = serialization_template(Vsn),
[ {source_hash, Hash}
, {type_info, TypeInfo}
, {byte_code, ByteCode}
] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash
, type_info => TypeInfo
, byte_code => ByteCode
, contract_vsn => Vsn
, payable => true
};
{compiler_sophia = Type, ?SOPHIA_CONTRACT_VSN_2 = Vsn, _Rest} ->
Template = serialization_template(Vsn),
[ {source_hash, Hash}
, {type_info, TypeInfo}
, {byte_code, ByteCode}
, {compiler_version, CompilerVersion}
] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash
, type_info => TypeInfo
, byte_code => ByteCode
, compiler_version => CompilerVersion
, contract_vsn => Vsn
, payable => true
};
{compiler_sophia = Type, ?SOPHIA_CONTRACT_VSN_3 = Vsn, _Rest} ->
Template = serialization_template(Vsn),
[ {source_hash, Hash}
, {type_info, TypeInfo}
, {byte_code, ByteCode}
, {compiler_version, CompilerVersion}
, {payable, Payable}
] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash
, type_info => TypeInfo
, byte_code => ByteCode
, compiler_version => CompilerVersion
, contract_vsn => Vsn
, payable => Payable
};
Other ->
error({illegal_code_object, Other})
end.
serialization_template(?SOPHIA_CONTRACT_VSN_1) ->
[ {source_hash, binary}
, {type_info, [{binary, binary, binary, binary}]} %% {type hash, name, arg type, out type}
, {byte_code, binary} ];
serialization_template(?SOPHIA_CONTRACT_VSN_2) ->
[ {source_hash, binary}
, {type_info, [{binary, binary, binary, binary}]} %% {type hash, name, arg type, out type}
, {byte_code, binary}
, {compiler_version, binary} ];
serialization_template(?SOPHIA_CONTRACT_VSN_3) ->
[ {source_hash, binary}
, {type_info, [{binary, binary, bool, binary, binary}]} %% {type hash, name, payable, arg type, out type}
, {byte_code, binary}
, {compiler_version, binary}
, {payable, bool} ].

View File

@ -0,0 +1,35 @@
-module(aeser_contract_code_tests).
-include_lib("eunit/include/eunit.hrl").
-include("aeser_contract_code.hrl").
-define(DUMMY_CODE_MAP_1,
#{ byte_code => <<"DUMMY CODE">>
, type_info => [{<<>>, <<>>, <<>>, <<>>}]
, contract_source => "contract Foo = ..." }).
-define(DUMMY_CODE_MAP_2,
#{ byte_code => <<"DUMMY CODE">>
, type_info => [{<<>>, <<>>, <<>>, <<>>}]
, compiler_version => <<"3.1.4">>
, source_hash => <<1, 2, 3, 4>> }).
-define(DUMMY_CODE_MAP_3,
#{ byte_code => <<"DUMMY CODE">>
, type_info => [{<<>>, <<>>, false, <<>>, <<>>}]
, compiler_version => <<"3.1.4">>
, contract_source => "contract Foo = ..."
, payable => true} ).
vsn_1_test() ->
aeser_contract_code:deserialize(
aeser_contract_code:serialize(?DUMMY_CODE_MAP_1, ?SOPHIA_CONTRACT_VSN_1)).
vsn_2_test() ->
aeser_contract_code:deserialize(
aeser_contract_code:serialize(?DUMMY_CODE_MAP_2, ?SOPHIA_CONTRACT_VSN_2)).
vsn_3_test() ->
aeser_contract_code:deserialize(
aeser_contract_code:serialize(?DUMMY_CODE_MAP_3, ?SOPHIA_CONTRACT_VSN_3)).