All checks were successful
Gajumaru Serialization Tests / tests (push) Successful in -4m22s
132 lines
4.8 KiB
Erlang
132 lines
4.8 KiB
Erlang
%%%-------------------------------------------------------------------
|
|
%%% @copyright (C) 2025, QPQ AG
|
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
|
%%% @doc
|
|
%%% Serialization of contract code
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-module(gmser_contract_code).
|
|
-vsn("0.1.2").
|
|
|
|
-include("gmser_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 } -> blake2(32, list_to_binary(SrcStr))
|
|
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 ],
|
|
gmser_chain_objects:serialize(compiler_sophia,
|
|
SophiaContractVersion,
|
|
serialization_template(SophiaContractVersion),
|
|
Fields).
|
|
|
|
% NOTE:
|
|
% This form significantly favors the presence of enacl and slows fallback
|
|
% invokation of eblake2. `try' is really fast; the error throwing machinery
|
|
% is comparatively slow. The assumption here is that in cases where you want
|
|
% eblake2 performance isn't the problem you're solving (you're probably not
|
|
% syncing a new node, for example).
|
|
blake2(Size, Bin) ->
|
|
try
|
|
enacl:generichash(Size, Bin)
|
|
catch error:undef ->
|
|
{ok, Hash} = eblake2:blake2b(Size, Bin),
|
|
Hash
|
|
end.
|
|
|
|
|
|
-spec deserialize(binary()) -> map().
|
|
deserialize(Binary) ->
|
|
case gmser_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}
|
|
] = gmser_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}
|
|
] = gmser_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}
|
|
] = gmser_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} ].
|
|
|