New application aeserialization for shared deps with compiler
This commit is contained in:
commit
b55c3726f4
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.rebar3
|
||||||
|
_*
|
||||||
|
.eunit
|
||||||
|
*.o
|
||||||
|
*.beam
|
||||||
|
*.plt
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.erlang.cookie
|
||||||
|
ebin
|
||||||
|
log
|
||||||
|
erl_crash.dump
|
||||||
|
.rebar
|
||||||
|
logs
|
||||||
|
_build
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
rebar3.crashdump
|
||||||
|
*~
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2017, aeternity developers
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
9
README.md
Normal file
9
README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
aeserialization
|
||||||
|
=====
|
||||||
|
|
||||||
|
Serialization helpers for Aeternity node.
|
||||||
|
|
||||||
|
Build
|
||||||
|
-----
|
||||||
|
|
||||||
|
$ rebar3 compile
|
3
rebar.config
Normal file
3
rebar.config
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{erl_opts, [debug_info]}.
|
||||||
|
{deps, [{base58, {git, "https://github.com/aeternity/erl-base58.git", {ref,"60a3356"}}}
|
||||||
|
]}.
|
280
src/aeser_api_encoder.erl
Normal file
280
src/aeser_api_encoder.erl
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% API encoding for the Aeternity node.
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(aeser_api_encoder).
|
||||||
|
|
||||||
|
-export([encode/2,
|
||||||
|
decode/1,
|
||||||
|
safe_decode/2,
|
||||||
|
byte_size_for_type/1]).
|
||||||
|
|
||||||
|
-export_type([encoded/0]).
|
||||||
|
|
||||||
|
-type known_type() :: key_block_hash
|
||||||
|
| micro_block_hash
|
||||||
|
| block_pof_hash
|
||||||
|
| block_tx_hash
|
||||||
|
| block_state_hash
|
||||||
|
| channel
|
||||||
|
| contract_pubkey
|
||||||
|
| transaction
|
||||||
|
| tx_hash
|
||||||
|
| oracle_pubkey
|
||||||
|
| oracle_query
|
||||||
|
| oracle_query_id
|
||||||
|
| oracle_response
|
||||||
|
| account_pubkey
|
||||||
|
| signature
|
||||||
|
| name
|
||||||
|
| commitment
|
||||||
|
| peer_pubkey
|
||||||
|
| state
|
||||||
|
| poi
|
||||||
|
| state_trees.
|
||||||
|
|
||||||
|
-type extended_type() :: known_type() | block_hash | {id_hash, [known_type()]}.
|
||||||
|
|
||||||
|
|
||||||
|
-type payload() :: binary().
|
||||||
|
-type encoded() :: binary().
|
||||||
|
|
||||||
|
-define(BASE58, 1).
|
||||||
|
-define(BASE64, 2).
|
||||||
|
|
||||||
|
-spec encode(known_type(), payload() | aeser_id:id()) -> encoded().
|
||||||
|
encode(id_hash, Payload) ->
|
||||||
|
{IdType, Val} = aeser_id:specialize(Payload),
|
||||||
|
encode(id2type(IdType), Val);
|
||||||
|
encode(Type, Payload) ->
|
||||||
|
Pfx = type2pfx(Type),
|
||||||
|
Enc = case type2enc(Type) of
|
||||||
|
?BASE58 -> base58_check(Payload);
|
||||||
|
?BASE64 -> base64_check(Payload)
|
||||||
|
end,
|
||||||
|
<<Pfx/binary, "_", Enc/binary>>.
|
||||||
|
|
||||||
|
-spec decode(binary()) -> {known_type(), payload()}.
|
||||||
|
decode(Bin0) ->
|
||||||
|
case split(Bin0) of
|
||||||
|
[Pfx, Payload] ->
|
||||||
|
Type = pfx2type(Pfx),
|
||||||
|
Bin = decode_check(Type, Payload),
|
||||||
|
case type_size_check(Type, Bin) of
|
||||||
|
ok -> {Type, Bin};
|
||||||
|
{error, Reason} -> erlang:error(Reason)
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
%% {<<>>, decode_check(Bin)}
|
||||||
|
erlang:error(missing_prefix)
|
||||||
|
end.
|
||||||
|
|
||||||
|
type_size_check(Type, Bin) ->
|
||||||
|
case byte_size_for_type(Type) of
|
||||||
|
not_applicable -> ok;
|
||||||
|
CorrectSize ->
|
||||||
|
Size = byte_size(Bin),
|
||||||
|
case Size =:= CorrectSize of
|
||||||
|
true -> ok;
|
||||||
|
false -> {error, incorrect_size}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec safe_decode(extended_type(), encoded()) -> {'ok', payload() | aeser_id:id()}
|
||||||
|
| {'error', atom()}.
|
||||||
|
safe_decode({id_hash, AllowedTypes}, Enc) ->
|
||||||
|
try decode(Enc) of
|
||||||
|
{ActualType, Dec} ->
|
||||||
|
case lists:member(ActualType, AllowedTypes) of
|
||||||
|
true ->
|
||||||
|
try {ok, aeser_id:create(type2id(ActualType), Dec)}
|
||||||
|
catch error:_ -> {error, invalid_prefix}
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, invalid_prefix}
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
error:_ ->
|
||||||
|
{error, invalid_encoding}
|
||||||
|
end;
|
||||||
|
safe_decode(block_hash, Enc) ->
|
||||||
|
try decode(Enc) of
|
||||||
|
{key_block_hash, Dec} ->
|
||||||
|
{ok, Dec};
|
||||||
|
{micro_block_hash, Dec} ->
|
||||||
|
{ok, Dec};
|
||||||
|
{_, _} ->
|
||||||
|
{error, invalid_prefix}
|
||||||
|
catch
|
||||||
|
error:_ ->
|
||||||
|
{error, invalid_encoding}
|
||||||
|
end;
|
||||||
|
safe_decode(Type, Enc) ->
|
||||||
|
try decode(Enc) of
|
||||||
|
{Type, Dec} ->
|
||||||
|
{ok, Dec};
|
||||||
|
{_, _} ->
|
||||||
|
{error, invalid_prefix}
|
||||||
|
catch
|
||||||
|
error:_ ->
|
||||||
|
{error, invalid_encoding}
|
||||||
|
end.
|
||||||
|
|
||||||
|
decode_check(Type, Bin) ->
|
||||||
|
Dec =
|
||||||
|
case type2enc(Type) of
|
||||||
|
?BASE58 -> base58_to_binary(Bin);
|
||||||
|
?BASE64 -> base64_to_binary(Bin)
|
||||||
|
end,
|
||||||
|
Sz = byte_size(Dec),
|
||||||
|
BSz = Sz - 4,
|
||||||
|
<<Body:BSz/binary, C:4/binary>> = Dec,
|
||||||
|
C = check_str(Body),
|
||||||
|
Body.
|
||||||
|
|
||||||
|
base64_check(Bin) ->
|
||||||
|
C = check_str(Bin),
|
||||||
|
binary_to_base64(iolist_to_binary([Bin, C])).
|
||||||
|
|
||||||
|
%% modified from github.com/mbrix/lib_hd
|
||||||
|
base58_check(Bin) ->
|
||||||
|
C = check_str(Bin),
|
||||||
|
binary_to_base58(iolist_to_binary([Bin, C])).
|
||||||
|
|
||||||
|
split(Bin) ->
|
||||||
|
binary:split(Bin, [<<"_">>], []).
|
||||||
|
|
||||||
|
check_str(Bin) ->
|
||||||
|
<<C:32/bitstring,_/binary>> =
|
||||||
|
sha256_hash(sha256_hash(Bin)),
|
||||||
|
C.
|
||||||
|
|
||||||
|
sha256_hash(Bin) ->
|
||||||
|
crypto:hash(sha256, Bin).
|
||||||
|
|
||||||
|
|
||||||
|
id2type(account) -> account_pubkey;
|
||||||
|
id2type(channel) -> channel;
|
||||||
|
id2type(commitment) -> commitment;
|
||||||
|
id2type(contract) -> contract_pubkey;
|
||||||
|
id2type(name) -> name;
|
||||||
|
id2type(oracle) -> oracle_pubkey.
|
||||||
|
|
||||||
|
type2id(account_pubkey) -> account;
|
||||||
|
type2id(channel) -> channel;
|
||||||
|
type2id(commitment) -> commitment;
|
||||||
|
type2id(contract_pubkey) -> contract;
|
||||||
|
type2id(name) -> name;
|
||||||
|
type2id(oracle_pubkey) -> oracle.
|
||||||
|
|
||||||
|
type2enc(key_block_hash) -> ?BASE58;
|
||||||
|
type2enc(micro_block_hash) -> ?BASE58;
|
||||||
|
type2enc(block_pof_hash) -> ?BASE58;
|
||||||
|
type2enc(block_tx_hash) -> ?BASE58;
|
||||||
|
type2enc(block_state_hash) -> ?BASE58;
|
||||||
|
type2enc(channel) -> ?BASE58;
|
||||||
|
type2enc(contract_pubkey) -> ?BASE58;
|
||||||
|
type2enc(contract_bytearray)-> ?BASE64;
|
||||||
|
type2enc(transaction) -> ?BASE64;
|
||||||
|
type2enc(tx_hash) -> ?BASE58;
|
||||||
|
type2enc(oracle_pubkey) -> ?BASE58;
|
||||||
|
type2enc(oracle_query) -> ?BASE64;
|
||||||
|
type2enc(oracle_query_id) -> ?BASE58;
|
||||||
|
type2enc(oracle_response) -> ?BASE64;
|
||||||
|
type2enc(account_pubkey) -> ?BASE58;
|
||||||
|
type2enc(signature) -> ?BASE58;
|
||||||
|
type2enc(commitment) -> ?BASE58;
|
||||||
|
type2enc(peer_pubkey) -> ?BASE58;
|
||||||
|
type2enc(name) -> ?BASE58;
|
||||||
|
type2enc(state) -> ?BASE64;
|
||||||
|
type2enc(poi) -> ?BASE64;
|
||||||
|
type2enc(state_trees) -> ?BASE64.
|
||||||
|
|
||||||
|
|
||||||
|
type2pfx(key_block_hash) -> <<"kh">>;
|
||||||
|
type2pfx(micro_block_hash) -> <<"mh">>;
|
||||||
|
type2pfx(block_pof_hash) -> <<"bf">>;
|
||||||
|
type2pfx(block_tx_hash) -> <<"bx">>;
|
||||||
|
type2pfx(block_state_hash) -> <<"bs">>;
|
||||||
|
type2pfx(channel) -> <<"ch">>;
|
||||||
|
type2pfx(contract_pubkey) -> <<"ct">>;
|
||||||
|
type2pfx(contract_bytearray)-> <<"cb">>;
|
||||||
|
type2pfx(transaction) -> <<"tx">>;
|
||||||
|
type2pfx(tx_hash) -> <<"th">>;
|
||||||
|
type2pfx(oracle_pubkey) -> <<"ok">>;
|
||||||
|
type2pfx(oracle_query) -> <<"ov">>;
|
||||||
|
type2pfx(oracle_query_id) -> <<"oq">>;
|
||||||
|
type2pfx(oracle_response) -> <<"or">>;
|
||||||
|
type2pfx(account_pubkey) -> <<"ak">>;
|
||||||
|
type2pfx(signature) -> <<"sg">>;
|
||||||
|
type2pfx(commitment) -> <<"cm">>;
|
||||||
|
type2pfx(peer_pubkey) -> <<"pp">>;
|
||||||
|
type2pfx(name) -> <<"nm">>;
|
||||||
|
type2pfx(state) -> <<"st">>;
|
||||||
|
type2pfx(poi) -> <<"pi">>;
|
||||||
|
type2pfx(state_trees) -> <<"ss">>.
|
||||||
|
|
||||||
|
pfx2type(<<"kh">>) -> key_block_hash;
|
||||||
|
pfx2type(<<"mh">>) -> micro_block_hash;
|
||||||
|
pfx2type(<<"bf">>) -> block_pof_hash;
|
||||||
|
pfx2type(<<"bx">>) -> block_tx_hash;
|
||||||
|
pfx2type(<<"bs">>) -> block_state_hash;
|
||||||
|
pfx2type(<<"ch">>) -> channel;
|
||||||
|
pfx2type(<<"cb">>) -> contract_bytearray;
|
||||||
|
pfx2type(<<"ct">>) -> contract_pubkey;
|
||||||
|
pfx2type(<<"tx">>) -> transaction;
|
||||||
|
pfx2type(<<"th">>) -> tx_hash;
|
||||||
|
pfx2type(<<"ok">>) -> oracle_pubkey;
|
||||||
|
pfx2type(<<"ov">>) -> oracle_query;
|
||||||
|
pfx2type(<<"oq">>) -> oracle_query_id;
|
||||||
|
pfx2type(<<"or">>) -> oracle_response;
|
||||||
|
pfx2type(<<"ak">>) -> account_pubkey;
|
||||||
|
pfx2type(<<"sg">>) -> signature;
|
||||||
|
pfx2type(<<"cm">>) -> commitment;
|
||||||
|
pfx2type(<<"pp">>) -> peer_pubkey;
|
||||||
|
pfx2type(<<"nm">>) -> name;
|
||||||
|
pfx2type(<<"st">>) -> state;
|
||||||
|
pfx2type(<<"pi">>) -> poi;
|
||||||
|
pfx2type(<<"ss">>) -> state_trees.
|
||||||
|
|
||||||
|
-spec byte_size_for_type(known_type()) -> non_neg_integer() | not_applicable.
|
||||||
|
|
||||||
|
byte_size_for_type(key_block_hash) -> 32;
|
||||||
|
byte_size_for_type(micro_block_hash) -> 32;
|
||||||
|
byte_size_for_type(block_pof_hash) -> 32;
|
||||||
|
byte_size_for_type(block_tx_hash) -> 32;
|
||||||
|
byte_size_for_type(block_state_hash) -> 32;
|
||||||
|
byte_size_for_type(channel) -> 32;
|
||||||
|
byte_size_for_type(contract_pubkey) -> 32;
|
||||||
|
byte_size_for_type(contract_bytearray)-> not_applicable;
|
||||||
|
byte_size_for_type(transaction) -> not_applicable;
|
||||||
|
byte_size_for_type(tx_hash) -> 32;
|
||||||
|
byte_size_for_type(oracle_pubkey) -> 32;
|
||||||
|
byte_size_for_type(oracle_query) -> not_applicable;
|
||||||
|
byte_size_for_type(oracle_query_id) -> 32;
|
||||||
|
byte_size_for_type(oracle_response) -> not_applicable;
|
||||||
|
byte_size_for_type(account_pubkey) -> 32;
|
||||||
|
byte_size_for_type(signature) -> 64;
|
||||||
|
byte_size_for_type(name) -> not_applicable;
|
||||||
|
byte_size_for_type(commitment) -> 32;
|
||||||
|
byte_size_for_type(peer_pubkey) -> 32;
|
||||||
|
byte_size_for_type(state) -> 32;
|
||||||
|
byte_size_for_type(poi) -> not_applicable;
|
||||||
|
byte_size_for_type(state_trees) -> not_applicable.
|
||||||
|
|
||||||
|
|
||||||
|
%% TODO: Fix the base58 module so that it consistently uses binaries instead
|
||||||
|
%%
|
||||||
|
binary_to_base58(Bin) ->
|
||||||
|
iolist_to_binary(base58:binary_to_base58(Bin)).
|
||||||
|
|
||||||
|
base58_to_binary(Bin) when is_binary(Bin) ->
|
||||||
|
base58:base58_to_binary(binary_to_list(Bin)).
|
||||||
|
|
||||||
|
binary_to_base64(Bin) ->
|
||||||
|
base64:encode(Bin).
|
||||||
|
|
||||||
|
base64_to_binary(Bin) when is_binary(Bin) ->
|
||||||
|
base64:decode(Bin).
|
143
src/aeser_chain_objects.erl
Normal file
143
src/aeser_chain_objects.erl
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Functions for serializing chain objects to binary format.
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeser_chain_objects).
|
||||||
|
|
||||||
|
-export([ serialize/4
|
||||||
|
, deserialize/4
|
||||||
|
, deserialize_type_and_vsn/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-type template() :: aeserialization:template().
|
||||||
|
-type fields() :: aeserialization:fields().
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec serialize(atom(), non_neg_integer(), template(), fields()) -> binary().
|
||||||
|
serialize(Type, Vsn, Template, Fields) ->
|
||||||
|
aeserialization:serialize(tag(Type), Vsn, Template, Fields).
|
||||||
|
|
||||||
|
deserialize_type_and_vsn(Binary) ->
|
||||||
|
{Tag, Vsn, Fields} = aeserialization:deserialize_tag_and_vsn(Binary),
|
||||||
|
{rev_tag(Tag), Vsn, Fields}.
|
||||||
|
|
||||||
|
-spec deserialize(atom(), non_neg_integer(), template(), binary()) -> fields().
|
||||||
|
deserialize(Type, Vsn, Template, Binary) ->
|
||||||
|
aeserialization:deserialize(Type, tag(Type), Vsn, Template, Binary).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
tag(account) -> 10;
|
||||||
|
tag(signed_tx) -> 11;
|
||||||
|
tag(spend_tx) -> 12;
|
||||||
|
tag(oracle) -> 20;
|
||||||
|
tag(oracle_query) -> 21;
|
||||||
|
tag(oracle_register_tx) -> 22;
|
||||||
|
tag(oracle_query_tx) -> 23;
|
||||||
|
tag(oracle_response_tx) -> 24;
|
||||||
|
tag(oracle_extend_tx) -> 25;
|
||||||
|
tag(name) -> 30;
|
||||||
|
tag(name_commitment) -> 31;
|
||||||
|
tag(name_claim_tx) -> 32;
|
||||||
|
tag(name_preclaim_tx) -> 33;
|
||||||
|
tag(name_update_tx) -> 34;
|
||||||
|
tag(name_revoke_tx) -> 35;
|
||||||
|
tag(name_transfer_tx) -> 36;
|
||||||
|
tag(contract) -> 40;
|
||||||
|
tag(contract_call) -> 41;
|
||||||
|
tag(contract_create_tx) -> 42;
|
||||||
|
tag(contract_call_tx) -> 43;
|
||||||
|
tag(channel_create_tx) -> 50;
|
||||||
|
tag(channel_deposit_tx) -> 51;
|
||||||
|
tag(channel_withdraw_tx) -> 52;
|
||||||
|
tag(channel_force_progress_tx) -> 521;
|
||||||
|
tag(channel_close_mutual_tx) -> 53;
|
||||||
|
tag(channel_close_solo_tx) -> 54;
|
||||||
|
tag(channel_slash_tx) -> 55;
|
||||||
|
tag(channel_settle_tx) -> 56;
|
||||||
|
tag(channel_offchain_tx) -> 57;
|
||||||
|
tag(channel_offchain_update_transfer) -> 570;
|
||||||
|
tag(channel_offchain_update_deposit) -> 571;
|
||||||
|
tag(channel_offchain_update_withdraw) -> 572;
|
||||||
|
tag(channel_offchain_update_create_contract) -> 573;
|
||||||
|
tag(channel_offchain_update_call_contract) -> 574;
|
||||||
|
tag(channel) -> 58;
|
||||||
|
tag(channel_snapshot_solo_tx) -> 59;
|
||||||
|
tag(trees_poi) -> 60;
|
||||||
|
tag(trees_db) -> 61;
|
||||||
|
tag(state_trees) -> 62;
|
||||||
|
tag(mtree) -> 63;
|
||||||
|
tag(mtree_value) -> 64;
|
||||||
|
tag(contracts_mtree) -> 621;
|
||||||
|
tag(calls_mtree) -> 622;
|
||||||
|
tag(channels_mtree) -> 623;
|
||||||
|
tag(nameservice_mtree) -> 624;
|
||||||
|
tag(oracles_mtree) -> 625;
|
||||||
|
tag(accounts_mtree) -> 626;
|
||||||
|
tag(compiler_sophia) -> 70;
|
||||||
|
tag(key_block) -> 100;
|
||||||
|
tag(micro_block) -> 101;
|
||||||
|
tag(light_micro_block) -> 102;
|
||||||
|
tag(pof) -> 200.
|
||||||
|
|
||||||
|
rev_tag(10) -> account;
|
||||||
|
rev_tag(11) -> signed_tx;
|
||||||
|
rev_tag(12) -> spend_tx;
|
||||||
|
rev_tag(20) -> oracle;
|
||||||
|
rev_tag(21) -> oracle_query;
|
||||||
|
rev_tag(22) -> oracle_register_tx;
|
||||||
|
rev_tag(23) -> oracle_query_tx;
|
||||||
|
rev_tag(24) -> oracle_response_tx;
|
||||||
|
rev_tag(25) -> oracle_extend_tx;
|
||||||
|
rev_tag(30) -> name;
|
||||||
|
rev_tag(31) -> name_commitment;
|
||||||
|
rev_tag(32) -> name_claim_tx;
|
||||||
|
rev_tag(33) -> name_preclaim_tx;
|
||||||
|
rev_tag(34) -> name_update_tx;
|
||||||
|
rev_tag(35) -> name_revoke_tx;
|
||||||
|
rev_tag(36) -> name_transfer_tx;
|
||||||
|
rev_tag(40) -> contract;
|
||||||
|
rev_tag(41) -> contract_call;
|
||||||
|
rev_tag(42) -> contract_create_tx;
|
||||||
|
rev_tag(43) -> contract_call_tx;
|
||||||
|
rev_tag(50) -> channel_create_tx;
|
||||||
|
rev_tag(51) -> channel_deposit_tx;
|
||||||
|
rev_tag(52) -> channel_withdraw_tx;
|
||||||
|
rev_tag(521) -> channel_force_progress_tx;
|
||||||
|
rev_tag(53) -> channel_close_mutual_tx;
|
||||||
|
rev_tag(54) -> channel_close_solo_tx;
|
||||||
|
rev_tag(55) -> channel_slash_tx;
|
||||||
|
rev_tag(56) -> channel_settle_tx;
|
||||||
|
rev_tag(57) -> channel_offchain_tx;
|
||||||
|
rev_tag(570) -> channel_offchain_update_transfer;
|
||||||
|
rev_tag(571) -> channel_offchain_update_deposit;
|
||||||
|
rev_tag(572) -> channel_offchain_update_withdraw;
|
||||||
|
rev_tag(573) -> channel_offchain_update_create_contract;
|
||||||
|
rev_tag(574) -> channel_offchain_update_call_contract;
|
||||||
|
rev_tag(58) -> channel;
|
||||||
|
rev_tag(59) -> channel_snapshot_solo_tx;
|
||||||
|
rev_tag(60) -> trees_poi;
|
||||||
|
rev_tag(61) -> trees_db;
|
||||||
|
rev_tag(62) -> state_trees;
|
||||||
|
rev_tag(63) -> mtree;
|
||||||
|
rev_tag(64) -> mtree_value;
|
||||||
|
rev_tag(621) -> contracts_mtree;
|
||||||
|
rev_tag(622) -> calls_mtree;
|
||||||
|
rev_tag(623) -> channels_mtree;
|
||||||
|
rev_tag(624) -> nameservice_mtree;
|
||||||
|
rev_tag(625) -> oracles_mtree;
|
||||||
|
rev_tag(626) -> accounts_mtree;
|
||||||
|
rev_tag(70) -> compiler_sophia;
|
||||||
|
rev_tag(100) -> key_block;
|
||||||
|
rev_tag(101) -> micro_block;
|
||||||
|
rev_tag(102) -> light_micro_block;
|
||||||
|
rev_tag(200) -> pof.
|
111
src/aeser_id.erl
Normal file
111
src/aeser_id.erl
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% ADT for identifiers
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeser_id).
|
||||||
|
|
||||||
|
-export([ create/2
|
||||||
|
, specialize/1
|
||||||
|
, specialize/2
|
||||||
|
, specialize_type/1
|
||||||
|
, is_id/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% For aec_serialization
|
||||||
|
-export([ encode/1
|
||||||
|
, decode/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-record(id, { tag
|
||||||
|
, val
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type tag() :: 'account' | 'oracle' | 'name'
|
||||||
|
| 'commitment' | 'contract' | 'channel'.
|
||||||
|
-type val() :: <<_:256>>.
|
||||||
|
-opaque(id() :: #id{}).
|
||||||
|
|
||||||
|
-export_type([ id/0
|
||||||
|
, tag/0
|
||||||
|
, val/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-define(PUB_SIZE, 32).
|
||||||
|
-define(TAG_SIZE, 1).
|
||||||
|
-define(SERIALIZED_SIZE, 33). %% ?TAG_SIZE + ?PUB_SIZE
|
||||||
|
|
||||||
|
-define(IS_TAG(___TAG___), ___TAG___ =:= account;
|
||||||
|
___TAG___ =:= oracle;
|
||||||
|
___TAG___ =:= name;
|
||||||
|
___TAG___ =:= commitment;
|
||||||
|
___TAG___ =:= contract;
|
||||||
|
___TAG___ =:= channel
|
||||||
|
).
|
||||||
|
-define(IS_VAL(___VAL___), byte_size(___VAL___) =:= 32).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec create(tag(), val()) -> id().
|
||||||
|
create(Tag, Val) when ?IS_TAG(Tag), ?IS_VAL(Val) ->
|
||||||
|
#id{ tag = Tag
|
||||||
|
, val = Val};
|
||||||
|
create(Tag, Val) when ?IS_VAL(Val) ->
|
||||||
|
error({illegal_tag, Tag});
|
||||||
|
create(Tag, Val) when ?IS_TAG(Tag)->
|
||||||
|
error({illegal_val, Val});
|
||||||
|
create(Tag, Val) ->
|
||||||
|
error({illegal_tag_and_val, Tag, Val}).
|
||||||
|
|
||||||
|
|
||||||
|
-spec specialize(id()) -> {tag(), val()}.
|
||||||
|
specialize(#id{tag = Tag, val = Val}) ->
|
||||||
|
{Tag, Val}.
|
||||||
|
|
||||||
|
-spec specialize(id(), tag()) -> val().
|
||||||
|
specialize(#id{tag = Tag, val = Val}, Tag) when ?IS_TAG(Tag), ?IS_VAL(Val) ->
|
||||||
|
Val.
|
||||||
|
|
||||||
|
-spec specialize_type(id()) -> tag().
|
||||||
|
specialize_type(#id{tag = Tag}) when ?IS_TAG(Tag) ->
|
||||||
|
Tag.
|
||||||
|
|
||||||
|
-spec is_id(term()) -> boolean().
|
||||||
|
is_id(#id{}) -> true;
|
||||||
|
is_id(_) -> false.
|
||||||
|
|
||||||
|
-spec encode(id()) -> binary().
|
||||||
|
encode(#id{tag = Tag, val = Val}) ->
|
||||||
|
Res = <<(encode_tag(Tag)):?TAG_SIZE/unit:8, Val/binary>>,
|
||||||
|
true = ?SERIALIZED_SIZE =:= byte_size(Res),
|
||||||
|
Res.
|
||||||
|
|
||||||
|
-spec decode(binary()) -> id().
|
||||||
|
decode(<<Tag:?TAG_SIZE/unit:8, Val:?PUB_SIZE/binary>>) ->
|
||||||
|
#id{ tag = decode_tag(Tag)
|
||||||
|
, val = Val}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
encode_tag(account) -> 1;
|
||||||
|
encode_tag(name) -> 2;
|
||||||
|
encode_tag(commitment) -> 3;
|
||||||
|
encode_tag(oracle) -> 4;
|
||||||
|
encode_tag(contract) -> 5;
|
||||||
|
encode_tag(channel) -> 6;
|
||||||
|
encode_tag(Other) -> error({illegal_id_tag_name, Other}).
|
||||||
|
|
||||||
|
decode_tag(1) -> account;
|
||||||
|
decode_tag(2) -> name;
|
||||||
|
decode_tag(3) -> commitment;
|
||||||
|
decode_tag(4) -> oracle;
|
||||||
|
decode_tag(5) -> contract;
|
||||||
|
decode_tag(6) -> channel;
|
||||||
|
decode_tag(X) -> error({illegal_id_tag, X}).
|
91
src/aeser_rlp.erl
Normal file
91
src/aeser_rlp.erl
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Implementation of the Recursive Length Prefix.
|
||||||
|
%%%
|
||||||
|
%%% https://github.com/ethereum/wiki/wiki/RLP
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeser_rlp).
|
||||||
|
-export([ decode/1
|
||||||
|
, decode_one/1
|
||||||
|
, encode/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export_type([ encodable/0
|
||||||
|
, encoded/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-type encodable() :: [encodable()] | binary().
|
||||||
|
-type encoded() :: <<_:8, _:_*8>>.
|
||||||
|
|
||||||
|
-define(UNTAGGED_SIZE_LIMIT , 55).
|
||||||
|
-define(UNTAGGED_LIMIT , 127).
|
||||||
|
-define(BYTE_ARRAY_OFFSET , 128).
|
||||||
|
-define(LIST_OFFSET , 192).
|
||||||
|
|
||||||
|
|
||||||
|
-spec encode(encodable()) -> encoded().
|
||||||
|
encode(X) ->
|
||||||
|
encode(X, []).
|
||||||
|
|
||||||
|
encode(<<B>> = X,_Opts) when B =< ?UNTAGGED_LIMIT ->
|
||||||
|
%% An untagged value
|
||||||
|
X;
|
||||||
|
encode(X,_Opts) when is_binary(X) ->
|
||||||
|
%% Byte array
|
||||||
|
add_size(?BYTE_ARRAY_OFFSET, X);
|
||||||
|
encode(L, Opts) when is_list(L) ->
|
||||||
|
%% Lists items are encoded and concatenated
|
||||||
|
ByteArray = << << (encode(X, Opts))/binary >> || X <- L >>,
|
||||||
|
add_size(?LIST_OFFSET, ByteArray).
|
||||||
|
|
||||||
|
add_size(Offset, X) when byte_size(X) =< ?UNTAGGED_SIZE_LIMIT ->
|
||||||
|
%% The size fits in one tagged byte
|
||||||
|
<<(Offset + byte_size(X)), X/binary>>;
|
||||||
|
add_size(Offset, X) when is_binary(X) ->
|
||||||
|
%% The size itself needs to be encoded as a byte array
|
||||||
|
%% Add the tagged size of the size byte array
|
||||||
|
SizeBin = binary:encode_unsigned(byte_size(X)),
|
||||||
|
TaggedSize = ?UNTAGGED_SIZE_LIMIT + Offset + byte_size(SizeBin),
|
||||||
|
true = (TaggedSize < 256 ), %% Assert
|
||||||
|
<<TaggedSize, SizeBin/binary, X/binary>>.
|
||||||
|
|
||||||
|
-spec decode(encoded()) -> encodable().
|
||||||
|
decode(Bin) when is_binary(Bin), byte_size(Bin) > 0 ->
|
||||||
|
case decode_one(Bin) of
|
||||||
|
{X, <<>>} -> X;
|
||||||
|
{X, Left} -> error({trailing, X, Bin, Left})
|
||||||
|
end.
|
||||||
|
|
||||||
|
decode_one(<<X, B/binary>>) when X =< ?UNTAGGED_LIMIT ->
|
||||||
|
%% Untagged value
|
||||||
|
{<<X>>, B};
|
||||||
|
decode_one(<<L, _/binary>> = B) when L < ?LIST_OFFSET ->
|
||||||
|
%% Byte array
|
||||||
|
{Size, Rest} = decode_size(B, ?BYTE_ARRAY_OFFSET),
|
||||||
|
<<X:Size/binary, Tail/binary>> = Rest,
|
||||||
|
{X, Tail};
|
||||||
|
decode_one(<<_/binary>> = B) ->
|
||||||
|
%% List
|
||||||
|
{Size, Rest} = decode_size(B, ?LIST_OFFSET),
|
||||||
|
<<X:Size/binary, Tail/binary>> = Rest,
|
||||||
|
{decode_list(X), Tail}.
|
||||||
|
|
||||||
|
decode_size(<<L, B/binary>>, Offset) when L =< Offset + ?UNTAGGED_SIZE_LIMIT->
|
||||||
|
%% One byte tagged size.
|
||||||
|
{L - Offset, B};
|
||||||
|
decode_size(<<_, 0, _/binary>>,_Offset) ->
|
||||||
|
error(leading_zeroes_in_size);
|
||||||
|
decode_size(<<L, B/binary>>, Offset) ->
|
||||||
|
%% Actual size is in a byte array.
|
||||||
|
BinSize = L - Offset - ?UNTAGGED_SIZE_LIMIT,
|
||||||
|
<<Size:BinSize/unit:8, Rest/binary>> = B,
|
||||||
|
{Size, Rest}.
|
||||||
|
|
||||||
|
decode_list(<<>>) -> [];
|
||||||
|
decode_list(B) ->
|
||||||
|
{Element, Rest} = decode_one(B),
|
||||||
|
[Element|decode_list(Rest)].
|
15
src/aeserialization.app.src
Normal file
15
src/aeserialization.app.src
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{application, aeserialization,
|
||||||
|
[{description, "Serialization of data for Aeternity"},
|
||||||
|
{vsn, "0.1.0"},
|
||||||
|
{registered, []},
|
||||||
|
{applications,
|
||||||
|
[kernel,
|
||||||
|
stdlib,
|
||||||
|
crypto,
|
||||||
|
base58
|
||||||
|
]},
|
||||||
|
{env,[]},
|
||||||
|
{modules, []},
|
||||||
|
{licenses, []},
|
||||||
|
{links, []}
|
||||||
|
]}.
|
134
src/aeserialization.erl
Normal file
134
src/aeserialization.erl
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Functions for serializing generic objects to/from binary format.
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeserialization).
|
||||||
|
|
||||||
|
-export([ decode_fields/2
|
||||||
|
, deserialize/5
|
||||||
|
, deserialize_tag_and_vsn/1
|
||||||
|
, encode_fields/2
|
||||||
|
, serialize/4 ]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Types
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
-export_type([ template/0
|
||||||
|
, fields/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-type template() :: [{field_name(), type()}].
|
||||||
|
-type field_name() :: atom().
|
||||||
|
-type type() :: 'int'
|
||||||
|
| 'bool'
|
||||||
|
| 'binary'
|
||||||
|
| 'id' %% As defined in aec_id.erl
|
||||||
|
| [type()] %% Length one in the type. This means a list of any length.
|
||||||
|
| tuple(). %% Any arity, containing type(). This means a static size array.
|
||||||
|
|
||||||
|
-type encodable_term() :: non_neg_integer()
|
||||||
|
| binary()
|
||||||
|
| boolean()
|
||||||
|
| [encodable_term()] %% Of any length
|
||||||
|
| tuple() %% Any arity, containing encodable_term().
|
||||||
|
| aeser_id:id().
|
||||||
|
|
||||||
|
-type fields() :: [{field_name(), encodable_term()}].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec serialize(non_neg_integer(), non_neg_integer(), template(), fields()) -> binary().
|
||||||
|
serialize(Tag, Vsn, Template, Fields) ->
|
||||||
|
List = encode_fields([{tag, int}, {vsn, int}|Template],
|
||||||
|
[{tag, Tag}, {vsn, Vsn}|Fields]),
|
||||||
|
aeser_rlp:encode(List).
|
||||||
|
|
||||||
|
%% Type isn't strictly necessary, but will give a better error reason
|
||||||
|
-spec deserialize(atom(), non_neg_integer(), non_neg_integer(),
|
||||||
|
template(), binary()) -> fields().
|
||||||
|
deserialize(Type, Tag, Vsn, Template0, Binary) ->
|
||||||
|
Decoded = aeser_rlp:decode(Binary),
|
||||||
|
Template = [{tag, int}, {vsn, int}|Template0],
|
||||||
|
case decode_fields(Template, Decoded) of
|
||||||
|
[{tag, Tag}, {vsn, Vsn}|Left] ->
|
||||||
|
Left;
|
||||||
|
Other ->
|
||||||
|
error({illegal_serialization, Type, Vsn,
|
||||||
|
Other, Binary, Decoded, Template})
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec deserialize_tag_and_vsn(binary()) ->
|
||||||
|
{non_neg_integer(), non_neg_integer(), fields()}.
|
||||||
|
deserialize_tag_and_vsn(Binary) ->
|
||||||
|
[TagBin, VsnBin|Fields] = aeser_rlp:decode(Binary),
|
||||||
|
Template = [{tag, int}, {vsn, int}],
|
||||||
|
[{tag, Tag}, {vsn, Vsn}] = decode_fields(Template, [TagBin, VsnBin]),
|
||||||
|
{Tag, Vsn, Fields}.
|
||||||
|
|
||||||
|
encode_fields([{Field, Type}|TypesLeft],
|
||||||
|
[{Field, Val}|FieldsLeft]) ->
|
||||||
|
try encode_field(Type, Val) of
|
||||||
|
Encoded -> [Encoded | encode_fields(TypesLeft, FieldsLeft)]
|
||||||
|
catch error:{illegal, T, V} ->
|
||||||
|
error({illegal_field, Field, Type, Val, T, V})
|
||||||
|
end;
|
||||||
|
encode_fields([], []) ->
|
||||||
|
[];
|
||||||
|
encode_fields(Template, Values) ->
|
||||||
|
error({illegal_template_or_values, Template, Values}).
|
||||||
|
|
||||||
|
decode_fields([{Field, Type}|TypesLeft],
|
||||||
|
[Bin |FieldsLeft]) ->
|
||||||
|
try decode_field(Type, Bin) of
|
||||||
|
Decoded -> [{Field, Decoded} | decode_fields(TypesLeft, FieldsLeft)]
|
||||||
|
catch error:{illegal, T, V} ->
|
||||||
|
error({illegal_field, Field, Type, Bin, T, V})
|
||||||
|
end;
|
||||||
|
decode_fields([], []) ->
|
||||||
|
[];
|
||||||
|
decode_fields(Template, Values) ->
|
||||||
|
error({illegal_template_or_values, Template, Values}).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
|
||||||
|
encode_field([Type], L) when is_list(L) ->
|
||||||
|
[encode_field(Type, X) || X <- L];
|
||||||
|
encode_field(Type, T) when tuple_size(Type) =:= tuple_size(T) ->
|
||||||
|
Zipped = lists:zip(tuple_to_list(Type), tuple_to_list(T)),
|
||||||
|
[encode_field(X, Y) || {X, Y} <- Zipped];
|
||||||
|
encode_field(int, X) when is_integer(X), X >= 0 ->
|
||||||
|
binary:encode_unsigned(X);
|
||||||
|
encode_field(binary, X) when is_binary(X) -> X;
|
||||||
|
encode_field(bool, true) -> <<1:8>>;
|
||||||
|
encode_field(bool, false) -> <<0:8>>;
|
||||||
|
encode_field(id, Val) ->
|
||||||
|
try aeser_id:encode(Val)
|
||||||
|
catch _:_ -> error({illegal, id, Val})
|
||||||
|
end;
|
||||||
|
encode_field(Type, Val) -> error({illegal, Type, Val}).
|
||||||
|
|
||||||
|
decode_field([Type], List) when is_list(List) ->
|
||||||
|
[decode_field(Type, X) || X <- List];
|
||||||
|
decode_field(Type, List) when length(List) =:= tuple_size(Type) ->
|
||||||
|
Zipped = lists:zip(tuple_to_list(Type), List),
|
||||||
|
list_to_tuple([decode_field(X, Y) || {X, Y} <- Zipped]);
|
||||||
|
decode_field(int, <<0:8, X/binary>> = B) when X =/= <<>> ->
|
||||||
|
error({illegal, int, B});
|
||||||
|
decode_field(int, X) when is_binary(X) -> binary:decode_unsigned(X);
|
||||||
|
decode_field(binary, X) when is_binary(X) -> X;
|
||||||
|
decode_field(bool, <<1:8>>) -> true;
|
||||||
|
decode_field(bool, <<0:8>>) -> false;
|
||||||
|
decode_field(id, Val) ->
|
||||||
|
try aeser_id:decode(Val)
|
||||||
|
catch _:_ -> error({illegal, id, Val})
|
||||||
|
end;
|
||||||
|
decode_field(Type, X) -> error({illegal, Type, X}).
|
||||||
|
|
138
test/aeser_api_encoder_tests.erl
Normal file
138
test/aeser_api_encoder_tests.erl
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeser_api_encoder_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(TEST_MODULE, aeser_api_encoder).
|
||||||
|
-define(TYPES, [ {key_block_hash , 32}
|
||||||
|
, {micro_block_hash , 32}
|
||||||
|
, {block_tx_hash , 32}
|
||||||
|
, {block_state_hash , 32}
|
||||||
|
, {channel , 32}
|
||||||
|
, {contract_pubkey , 32}
|
||||||
|
, {transaction , not_applicable}
|
||||||
|
, {tx_hash , 32}
|
||||||
|
, {oracle_pubkey , 32}
|
||||||
|
, {oracle_query_id , 32}
|
||||||
|
, {account_pubkey , 32}
|
||||||
|
, {signature , 64}
|
||||||
|
, {name , not_applicable}
|
||||||
|
, {commitment , 32}
|
||||||
|
, {peer_pubkey , 32}
|
||||||
|
, {state , 32}
|
||||||
|
, {poi , not_applicable}]).
|
||||||
|
|
||||||
|
encode_decode_test_() ->
|
||||||
|
[{"Byte sizes are correct",
|
||||||
|
fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, ByteSize}) ->
|
||||||
|
{_Type, _, ByteSize} = {Type, ByteSize,
|
||||||
|
?TEST_MODULE:byte_size_for_type(Type)}
|
||||||
|
end,
|
||||||
|
?TYPES)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{"Serialize/deserialize known types",
|
||||||
|
fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, Size0}) ->
|
||||||
|
ByteSize =
|
||||||
|
case Size0 of
|
||||||
|
not_applicable -> 42;
|
||||||
|
_ when is_integer(Size0) -> Size0
|
||||||
|
end,
|
||||||
|
Key = <<42:ByteSize/unit:8>>,
|
||||||
|
EncodedKey = ?TEST_MODULE:encode(Type, Key),
|
||||||
|
{Type, Key} = ?TEST_MODULE:decode(EncodedKey),
|
||||||
|
{ok, Key} = ?TEST_MODULE:safe_decode(Type, EncodedKey)
|
||||||
|
end,
|
||||||
|
?TYPES)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{"Key size check works",
|
||||||
|
fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({_Type, not_applicable}) -> ok;
|
||||||
|
({Type, ByteSize}) ->
|
||||||
|
CheckIlligalSize =
|
||||||
|
fun(S) ->
|
||||||
|
Key = <<42:S/unit:8>>,
|
||||||
|
EncodedKey = ?TEST_MODULE:encode(Type, Key),
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, EncodedKey)
|
||||||
|
end,
|
||||||
|
CheckIlligalSize(0),
|
||||||
|
CheckIlligalSize(ByteSize - 1),
|
||||||
|
CheckIlligalSize(ByteSize + 1)
|
||||||
|
end,
|
||||||
|
?TYPES)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{"Missing prefix",
|
||||||
|
fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, Size0}) ->
|
||||||
|
ByteSize =
|
||||||
|
case Size0 of
|
||||||
|
not_applicable -> 42;
|
||||||
|
_ when is_integer(Size0) -> Size0
|
||||||
|
end,
|
||||||
|
Key = <<42:ByteSize/unit:8>>,
|
||||||
|
EncodedKey = ?TEST_MODULE:encode(Type, Key),
|
||||||
|
<<_PartOfPrefix:1/unit:8, RestOfKey/binary>> = EncodedKey,
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey),
|
||||||
|
|
||||||
|
<<_PrefixWithoutDelimiter:2/unit:8, RestOfKey1/binary>> = EncodedKey,
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey1),
|
||||||
|
|
||||||
|
<<_WholePrefix:3/unit:8, RestOfKey2/binary>> = EncodedKey,
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey2)
|
||||||
|
end,
|
||||||
|
?TYPES)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{"Piece of encoded key",
|
||||||
|
fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, Size0}) ->
|
||||||
|
ByteSize =
|
||||||
|
case Size0 of
|
||||||
|
not_applicable -> 42;
|
||||||
|
_ when is_integer(Size0) -> Size0
|
||||||
|
end,
|
||||||
|
Key = <<42:ByteSize/unit:8>>,
|
||||||
|
EncodedKey = ?TEST_MODULE:encode(Type, Key),
|
||||||
|
HalfKeySize = byte_size(EncodedKey) div 2,
|
||||||
|
<<HalfKey:HalfKeySize/unit:8, RestOfKey/binary>> = EncodedKey,
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, HalfKey),
|
||||||
|
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey)
|
||||||
|
end,
|
||||||
|
?TYPES)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{"Encode/decode binary with only zeros",
|
||||||
|
fun() ->
|
||||||
|
Bins = [<<0:Size/unit:8>> || Size <- lists:seq(1,64)],
|
||||||
|
lists:foreach(
|
||||||
|
fun(Bin) ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Type, S}) ->
|
||||||
|
case S =:= byte_size(Bin) orelse S =:= not_applicable of
|
||||||
|
true ->
|
||||||
|
Encoded = ?TEST_MODULE:encode(Type, Bin),
|
||||||
|
{ok, Decoded} = ?TEST_MODULE:safe_decode(Type, Encoded),
|
||||||
|
?assertEqual(Decoded, Bin);
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
Encoded1 = base58:binary_to_base58(Bin),
|
||||||
|
Decoded1 = base58:base58_to_binary(Encoded1),
|
||||||
|
?assertEqual(Bin, Decoded1)
|
||||||
|
end, ?TYPES)
|
||||||
|
end,
|
||||||
|
Bins)
|
||||||
|
end}
|
||||||
|
].
|
87
test/aeser_chain_objects_tests.erl
Normal file
87
test/aeser_chain_objects_tests.erl
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% EUnit tests for aeser_chain_objects
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(aeser_chain_objects_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(DEFAULT_TAG, account).
|
||||||
|
-define(DEFAULT_VERSION, 1).
|
||||||
|
|
||||||
|
basic_test() ->
|
||||||
|
Template = [{foo, int}, {bar, binary}],
|
||||||
|
Values = [{foo, 1}, {bar, <<2>>}],
|
||||||
|
?assertEqual(Values, deserialize(Template, serialize(Template, Values))).
|
||||||
|
|
||||||
|
basic_fail_test() ->
|
||||||
|
Template = [{foo, int}, {bar, binary}],
|
||||||
|
Values = [{foo, 1}, {bar, 1}],
|
||||||
|
?assertError({illegal_field, _, _, _, _, _}, serialize(Template, Values)).
|
||||||
|
|
||||||
|
list_test() ->
|
||||||
|
Template = [{foo, [int]}, {bar, [binary]}, {baz, [int]}],
|
||||||
|
Values = [{foo, [1]}, {bar, [<<2>>, <<2>>]}, {baz, []}],
|
||||||
|
?assertEqual(Values, deserialize(Template, serialize(Template, Values))).
|
||||||
|
|
||||||
|
list_fail_test() ->
|
||||||
|
Template = [{foo, [int]}, {bar, [binary]}],
|
||||||
|
Values = [{foo, [1]}, {bar, [2, <<2>>]}],
|
||||||
|
?assertError({illegal_field, _, _, _, _, _}, serialize(Template, Values)).
|
||||||
|
|
||||||
|
deep_list_test() ->
|
||||||
|
Template = [{foo, [[int]]}, {bar, [[[[[binary]]]]]}],
|
||||||
|
Values = [{foo, [[1]]}, {bar, [[[[[<<2>>]]]]]}],
|
||||||
|
?assertEqual(Values, deserialize(Template, serialize(Template, Values))).
|
||||||
|
|
||||||
|
deep_list_fail_test() ->
|
||||||
|
Template = [{foo, [[int]]}, {bar, [[[[[binary]]]]]}],
|
||||||
|
Values = [{foo, [[1]]}, {bar, [[[[[2]]]]]}],
|
||||||
|
?assertError({illegal_field, _, _, _, _, _}, serialize(Template, Values)).
|
||||||
|
|
||||||
|
array_test() ->
|
||||||
|
Template = [{foo, {int, binary}}, {bar, [{int, int}]}, {baz, {int}}],
|
||||||
|
Values = [{foo, {1, <<"foo">>}}, {bar, [{1, 2}, {3, 4}, {5, 6}]}, {baz, {1}}],
|
||||||
|
?assertEqual(Values, deserialize(Template, serialize(Template, Values))).
|
||||||
|
|
||||||
|
array_fail_test() ->
|
||||||
|
Template = [{foo, {int, binary}}, {bar, [{int, int}]}, {baz, {int}}],
|
||||||
|
Values = [{foo, {1, <<"foo">>}}, {bar, [{1, 2}, {3, 4}, {5, 6}]}, {baz, {1, 1}}],
|
||||||
|
?assertError({illegal_field, _, _, _, _, _}, serialize(Template, Values)).
|
||||||
|
|
||||||
|
deep_array_test() ->
|
||||||
|
Template = [{foo, {{int, binary}}}, {bar, [{{int}, int}]}, {baz, {{int}}}],
|
||||||
|
Values = [{foo, {{1, <<"foo">>}}}, {bar, [{{1}, 2}, {{3}, 4}, {{5}, 6}]}, {baz, {{1}}}],
|
||||||
|
?assertEqual(Values, deserialize(Template, serialize(Template, Values))).
|
||||||
|
|
||||||
|
deep_array_fail_test() ->
|
||||||
|
Template = [{foo, {{int, binary}}}, {bar, [{{int}, int}]}, {baz, {{binary}}}],
|
||||||
|
Values = [{foo, {{1, <<"foo">>}}}, {bar, [{{1}, 2}, {{3}, 4}, {{5}, 6}]}, {baz, {{1}}}],
|
||||||
|
?assertError({illegal_field, _, _, _, _, _}, serialize(Template, Values)).
|
||||||
|
|
||||||
|
tag_fail_test() ->
|
||||||
|
Template = [{foo, int}, {bar, binary}],
|
||||||
|
Values = [{foo, 1}, {bar, <<2>>}],
|
||||||
|
?assertError({illegal_serialization, _, _, _, _, _, _},
|
||||||
|
deserialize(Template, serialize(Template, Values), signed_tx, ?DEFAULT_VERSION)).
|
||||||
|
|
||||||
|
vsn_fail_test() ->
|
||||||
|
Template = [{foo, int}, {bar, binary}],
|
||||||
|
Values = [{foo, 1}, {bar, <<2>>}],
|
||||||
|
?assertError({illegal_serialization, _, _, _, _, _, _},
|
||||||
|
deserialize(Template, serialize(Template, Values), ?DEFAULT_TAG, 2)).
|
||||||
|
|
||||||
|
deserialize(Template, Bin) ->
|
||||||
|
deserialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION).
|
||||||
|
|
||||||
|
deserialize(Template, Bin, Tag, Vsn) ->
|
||||||
|
aeser_chain_objects:deserialize(Tag, Vsn, Template, Bin).
|
||||||
|
|
||||||
|
serialize(Template, Bin) ->
|
||||||
|
serialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION).
|
||||||
|
|
||||||
|
serialize(Template, Bin, Tag, Vsn) ->
|
||||||
|
aeser_chain_objects:serialize(Tag, Vsn, Template, Bin).
|
132
test/aeser_rlp_tests.erl
Normal file
132
test/aeser_rlp_tests.erl
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||||
|
%%% @doc Tests for Recursive Length Prefix
|
||||||
|
%%% @end
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(aeser_rlp_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(UNTAGGED_SIZE_LIMIT , 55).
|
||||||
|
-define(UNTAGGED_LIMIT , 127).
|
||||||
|
-define(BYTE_ARRAY_OFFSET , 128).
|
||||||
|
-define(LIST_OFFSET , 192).
|
||||||
|
|
||||||
|
-define(TEST_MODULE, aeser_rlp).
|
||||||
|
|
||||||
|
rlp_one_byte_test() ->
|
||||||
|
B = <<42>>,
|
||||||
|
B = ?TEST_MODULE:encode(B),
|
||||||
|
B = ?TEST_MODULE:decode(B).
|
||||||
|
|
||||||
|
rlp_another_one_byte_test() ->
|
||||||
|
B = <<127>>,
|
||||||
|
B = ?TEST_MODULE:encode(B),
|
||||||
|
B = ?TEST_MODULE:decode(B).
|
||||||
|
|
||||||
|
rlp_zero_bytes_test() ->
|
||||||
|
B = <<>>,
|
||||||
|
S = ?BYTE_ARRAY_OFFSET + 0,
|
||||||
|
<<S, B/binary>> = ?TEST_MODULE:encode(B).
|
||||||
|
|
||||||
|
rlp_two_bytes_test() ->
|
||||||
|
B = <<128>>,
|
||||||
|
S = ?BYTE_ARRAY_OFFSET + 1,
|
||||||
|
<<S, B/binary>> = ?TEST_MODULE:encode(B).
|
||||||
|
|
||||||
|
rlp_one_byte_size_bytes_test() ->
|
||||||
|
L = 55,
|
||||||
|
S = ?BYTE_ARRAY_OFFSET + L,
|
||||||
|
X = << <<X>> || X <- lists:seq(1,L)>>,
|
||||||
|
E = <<S, X/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_tagged_size_one_byte_bytes_test() ->
|
||||||
|
L = 56,
|
||||||
|
Tag = ?BYTE_ARRAY_OFFSET + ?UNTAGGED_SIZE_LIMIT + 1,
|
||||||
|
X = list_to_binary(lists:duplicate(L, 42)),
|
||||||
|
S = byte_size(X),
|
||||||
|
E = <<Tag, S, X/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_tagged_size_two_bytes_bytes_test() ->
|
||||||
|
L = 256,
|
||||||
|
SizeSize = 2,
|
||||||
|
Tag = ?BYTE_ARRAY_OFFSET + ?UNTAGGED_SIZE_LIMIT + SizeSize,
|
||||||
|
X = list_to_binary(lists:duplicate(L, 42)),
|
||||||
|
S = byte_size(X),
|
||||||
|
E = <<Tag, S:SizeSize/unit:8, X/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_zero_bytes_list_test() ->
|
||||||
|
L = 0,
|
||||||
|
Tag = ?LIST_OFFSET + L,
|
||||||
|
X = [],
|
||||||
|
E = <<Tag>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_one_byte_list_test() ->
|
||||||
|
L = 1,
|
||||||
|
Tag = ?LIST_OFFSET + L,
|
||||||
|
X = lists:duplicate(L, <<42>>),
|
||||||
|
E = <<Tag, 42>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_byte_array_list_test() ->
|
||||||
|
L = 55,
|
||||||
|
Tag = ?LIST_OFFSET + L,
|
||||||
|
X = lists:duplicate(L, <<42>>),
|
||||||
|
Y = list_to_binary(X),
|
||||||
|
E = <<Tag, Y/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_byte_array_tagged_size_one_byte_list_test() ->
|
||||||
|
L = 56,
|
||||||
|
SizeSize = 1,
|
||||||
|
Tag = ?LIST_OFFSET + ?UNTAGGED_SIZE_LIMIT + SizeSize,
|
||||||
|
X = lists:duplicate(L, <<42>>),
|
||||||
|
Y = list_to_binary(X),
|
||||||
|
S = byte_size(Y),
|
||||||
|
E = <<Tag, S:SizeSize/unit:8, Y/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
rlp_byte_array_tagged_size_two_bytes_list_test() ->
|
||||||
|
L = 256,
|
||||||
|
SizeSize = 2,
|
||||||
|
Tag = ?LIST_OFFSET + ?UNTAGGED_SIZE_LIMIT + SizeSize,
|
||||||
|
X = lists:duplicate(L, <<42>>),
|
||||||
|
Y = list_to_binary(X),
|
||||||
|
S = byte_size(Y),
|
||||||
|
E = <<Tag, S:SizeSize/unit:8, Y/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E).
|
||||||
|
|
||||||
|
illegal_size_encoding_list_test() ->
|
||||||
|
%% Ensure we start with somehting legal.
|
||||||
|
L = 56,
|
||||||
|
SizeSize = 1,
|
||||||
|
Tag = ?LIST_OFFSET + ?UNTAGGED_SIZE_LIMIT + SizeSize,
|
||||||
|
X = lists:duplicate(L, <<42>>),
|
||||||
|
Y = list_to_binary(X),
|
||||||
|
S = byte_size(Y),
|
||||||
|
E = <<Tag, S:SizeSize/unit:8, Y/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E),
|
||||||
|
|
||||||
|
%% Add leading zeroes to the size field.
|
||||||
|
E1 = <<(Tag + 1), 0, S:SizeSize/unit:8, Y/binary>>,
|
||||||
|
?assertError(leading_zeroes_in_size, ?TEST_MODULE:decode(E1)).
|
||||||
|
|
||||||
|
illegal_size_encoding_byte_array_test() ->
|
||||||
|
%% Ensure we start with somehting legal.
|
||||||
|
L = 256,
|
||||||
|
SizeSize = 2,
|
||||||
|
Tag = ?BYTE_ARRAY_OFFSET + ?UNTAGGED_SIZE_LIMIT + SizeSize,
|
||||||
|
X = list_to_binary(lists:duplicate(L, 42)),
|
||||||
|
S = byte_size(X),
|
||||||
|
E = <<Tag, S:SizeSize/unit:8, X/binary>> = ?TEST_MODULE:encode(X),
|
||||||
|
X = ?TEST_MODULE:decode(E),
|
||||||
|
|
||||||
|
%% Add leading zeroes to the size field.
|
||||||
|
E1 = <<(Tag + 1), 0, S:SizeSize/unit:8, X/binary>>,
|
||||||
|
?assertError(leading_zeroes_in_size, ?TEST_MODULE:decode(E1)).
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user