Compare commits

..

4 Commits

Author SHA1 Message Date
Hans Svensson
e2ce3a0414 Add rollup_tx and hash encoding 2024-03-08 14:19:33 +01:00
Hans Svensson
9d9026e760 More chain objects for associate chains 2024-03-05 20:54:58 +01:00
Hans Svensson
b3105c466f Add chain object acs_mtree 2024-03-01 08:26:57 +01:00
Hans Svensson
4d2beb0337 Add id tag and serializations for associate chain 2024-02-29 14:32:36 +01:00
29 changed files with 253 additions and 2202 deletions

37
.circleci/config.yml Normal file
View File

@ -0,0 +1,37 @@
version: 2.1
executors:
aebuilder:
docker:
- image: aeternity/builder:bionic-otp23
user: builder
working_directory: ~/src
jobs:
build:
executor: aebuilder
steps:
- checkout
- restore_cache:
keys:
- dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
- dialyzer-cache-v1-{{ .Branch }}-
- dialyzer-cache-v1-
- run:
name: Build
command: rebar3 compile
- run:
name: Static Analysis
command: rebar3 dialyzer
- run:
name: Eunit
command: rebar3 eunit
- run:
name: Common Tests
command: rebar3 ct
- save_cache:
key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
paths:
- _build/default/rebar3_20.3.8_plt
- store_artifacts:
path: _build/test/logs

View File

@ -1,14 +0,0 @@
name: Gajumaru Serialization Tests
run-name: ${{ gitea.actor }} testing Gajumaru Serialization
on: [push, workflow_dispatch]
jobs:
tests:
runs-on: linux_amd64
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: eunit
run: |
. /home/act_runner/.erts/27.2.1/activate
./rebar3 eunit

2
.gitignore vendored
View File

@ -7,7 +7,7 @@ _*
*.swp *.swp
*.swo *.swo
.erlang.cookie .erlang.cookie
ebin/*.beam ebin
log log
erl_crash.dump erl_crash.dump
.rebar .rebar

View File

@ -1 +0,0 @@
{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}.

View File

@ -1,6 +1,5 @@
ISC License ISC License
Copyright (c) 2025, QPQ AG
Copyright (c) 2017, aeternity developers Copyright (c) 2017, aeternity developers
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any

268
README.md
View File

@ -1,267 +1,9 @@
# GM Serialization aeserialization
=====
Serialization helpers for the Gajumaru. Serialization helpers for Aeternity node.
For an overview of the static serializer, see [this document](doc/static.md). Build
-----
## Build
$ rebar3 compile $ rebar3 compile
## Test
$ rebar3 eunit
## Dynamic encoding
The module `gmser_dyn` offers dynamic encoding support, encoding most 'regular'
Erlang data types into an internal RLP representation.
Main API:
* `encode(term()) -> iolist()`
* `encode_typed(template(), term()) -> iolist()`
* `decode(iolist()) -> term()`
* `serialize(term()) -> binary()`
* `serialize_typed(template(), term()) -> binary()`
* `deserialize(binary()) -> term()`
In the examples below, we use the `decode` functions, to illustrate
how the type information is represented. The fully serialized form is
produced by the `serialize` functions.
The basic types supported by the encoder are:
* `integer()` (`anyint`, code: 246)
* `neg_integer()` (`negint`, code: 247)
* `non_neg_integer()` (`int` , code: 248)
* `binary()` (`binary`, code: 249)
* `boolean()` (`bool` , code: 250)
* `list()` (`list` , code: 251)
* `map()` (`map` , code: 252)
* `tuple()` (`tuple` , code: 253)
* `gmser_id:id()` (`id` , code: 254)
* `atom()` (`label` , code: 255)
(The range of codes is chosen because the `gmser_chain_objects` codes
range from 10 to 200, and also to stay within 1 byte.)
When encoding `map` types, the map elements are first sorted.
When specifying a map type for template-driven encoding, use
the `#{items => [{Key, ValueType} | {opt, Key, ValueType}]}` construct.
The key names are included in the encoding, and are match against the item
specs during decoding. If the key names don't match, the decoding fails, unless
for an `{opt, K, V}` item, in which case that item spec is skipped.
```erlang
T = #{items => [{a,int},{opt,b,int},{c,int}]}
E1 = gmser_dyn:encode_typed(T, #{a => 1, b => 2, c => 3}) ->
[<<0>>,<<1>>,[<<252>>,
[[[<<255>>,<<97>>],[<<248>>,<<1>>]],
[[<<255>>,<<98>>],[<<248>>,<<2>>]],
[[<<255>>,<<99>>],[<<248>>,<<3>>]]]]]
E2 = gmser_dyn:encode_typed(T, #{a => 1, c => 3}) ->
[<<0>>,<<1>>,[<<252>>,
[[[<<255>>,<<97>>],[<<248>>,<<1>>]],
[[<<255>>,<<99>>],[<<248>>,<<3>>]]]]]
gmser_dyn:decode_typed(T,E2) ->
#{c => 3,a => 1}
```
## Labels
Labels correspond to (existing) atoms in Erlang.
Decoding of a label results in a call to `binary_to_existing_atom/2`, so will
fail if the corresponding atom does not already exist.
This behavior can be modified using the option `#{missing_labels => fail | create | convert}`,
where `fail` is the default, as described above, `convert` means that missing atoms are
converted to binaries, and `create` means that the atom is created dynamically.
The option can be passed e.g.:
```erlang
gmser_dyn:deserialize(Binary, gmser_dyn:set_opts(#{missing_labels => convert}))
```
or
```erlang
gmser_dyn:deserialize(Binary, gmser_dyn:set_opts(#{missing_labels => convert}, Types))
```
By calling `gmser_dyn:register_types/1`, after having added options to the type map,
the options can be made to take effect automatically.
It's possible to cache labels for more compact encoding.
Note that when caching labels, the same cache mapping needs to be used on the
decoder side.
Labels are encoded as `[<<255>>, << AtomToBinary/binary >>]`.
If a cached label is used, the encoding becomes `[<<255>, [Ix]]`, where
`Ix` is the integer-encoded index value of the cached label.
## Examples
Dynamically encoded objects have the basic structure `[<<0>>,V,Obj]`, where `V` is the
integer-coded version, and `Obj` is the top-level encoding on the form `[Tag,Data]`.
```erlang
E = fun(T) -> io:fwrite("~w~n", [gmser_dyn:encode(T)]) end.
E(17) -> [<<0>>,<<1>>,[<<248>>,<<17>>]]
E(<<"abc">>) -> [<<0>>,<<1>>,[<<249>>,<<97,98,99>>]]
E(true) -> [<<0>>,<<1>>,[<<250>>,<<1>>]]
E(false) -> [<<0>>,<<1>>,[<<250>>,<<0>>]]
E([1,2]) -> [<<0>>,<<1>>,[<<251>>,[[<<248>>,<<1>>],[<<248>>,<<2>>]]]]
E({1,2}) -> [<<0>>,<<1>>,[<<253>>,[[<<248>>,<<1>>],[<<248>>,<<2>>]]]]
E(#{a=>1, b=>2}) ->
[<<0>>,<<1>>,[<<252>>,[[[<<255>>,<<97>>],[<<248>>,<<1>>]],[[<<255>>,<<98>>],[<<248>>,<<2>>]]]]]
E(gmser_id:create(account,<<1:256>>)) ->
[<<0>>,<<1>>,[<<254>>,<<1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>>]]
```
Note that tuples and list are encoded the same way, except for the initial type tag.
Maps are encoded as `[<Map>, [KV1, KV2, ...]]`, where `[KV1, KV2, ...]` is the sorted
list of key-value tuples from `map:to_list(Map)`, but with the `tuple` type tag omitted.
## Template-driven encoding
Templates can be provided to the encoder by either naming an already registered
type, or by passing a template directly. In both cases, the encoder will enforce
the type information in the template.
If the template has been registered, the encoder uses the registered type specification
to drive the encoding. The code of the registered template is embedded in the encoded
output:
```erlang
gmser_dyn:encode_typed({int,int,int}, {1,2,3}) ->
[<<0>>,<<1>>,[<<253>>,
[[<<248>>,<<1>>],[<<248>>,<<2>>],[<<248>>,<<3>>]]]]
Types = gmser_dyn_types:add_type(t3,1013,{int,int,int}).
gmser_dyn:encode_typed(t3, {1,2,3}, Types) ->
[<<0>>,<<1>>,[[<<3,245>>,<<253>>],
[[<<248>>,<<1>>],[<<248>>,<<2>>],[<<248>>,<<3>>]]]]
```
Note that the original `<<253>>` type code is wrapped as `[<<3,245>>,<<253>>]`,
where `<<3,245>>` corresponds to the custom code `1013`.
Using the default option `#{strict => true}`, the decoder will extract the custom
type spec, and validate the encoded data against it. If the custom code is missing,
the decoder aborts. Using `#{strict => false}`, the custom code is used if it exists,
but otherwise, it's ignored, and the encoded data is decoded using the dynamic type
info.
### Alternative types
The dynamic encoder supports a few additions to the `gmserialization` template
language: `any`, `#{list => Type}`, `#{alt => [AltTypes]}` and `#{switch => [AltTypes]}`.
#### `any`
The `any` type doesn't have an associated code, but enforces dynamic encoding.
#### `list`
The original list type notation expects a key-value list, e.g.
`[{name, binary}, {age, int}]`
```erlang
EL = gmser_dyn:encode_typed([{name,binary},{age,int}], [{name,<<"Ulf">>},{age,29}]) ->
[<<0>>,<<1>>,[<<251>>,
[[<<253>>,[[<<255>>,<<110,97,109,101>>],[<<249>>,<<85,108,102>>]]],
[<<253>>,[[<<255>>,<<97,103,101>>],[<<248>>,<<29>>]]]]]]
```
Note that the encoding explicitly lays out a `[{Key, Value}]` structure, all
dynamically typed. This means it can be dynamically decoded without templates.
```erlang
gmser_dyn:decode(EL).
[{name,<<"Ulf">>},{age,29}]
```
In order to specify something like Erlang's `[integer()]` type, we can use
the following:
```erlang
gmser_dyn:encode_typed(#{list => int}, [1,2,3,4]) ->
[<<0>>,<<1>>,[<<251>>,
[[<<248>>,<<1>>],[<<248>>,<<2>>],[<<248>>,<<3>>],[<<248>>,<<4>>]]]]
```
#### `alt`
The `#{alt => [Type]}` construct also enforces dynamic encoding, and will try
to encode as each type in the list, in the specified order, until one matches.
```erlang
gmser_dyn:encode_typed(#{alt => [negint,int]}, 5) -> [<<0>>,<<1>>,[<<247>>,<<5>>]]
gmser_dyn:encode_typed(#{alt => [negint,int]}, 5) -> [<<0>>,<<1>>,[<<248>>,<<5>>]]
gmser_dyn:encode_typed(anyint,-5) -> [<<0>>,<<1>>,[<<246>>,[<<247>>,<<5>>]]]
gmser_dyn:encode_typed(anyint,5) -> [<<0>>,<<1>>,[<<246>>,[<<248>>,<<5>>]]]
```
#### `switch`
The `switch` type allows for encoding a 'tagged' object, where the tag determines
the type.
```erlang
E1 = gmser_dyn:encode_typed(#{switch => #{name => binary, age => int}}, #{age => 29}) ->
[<<0>>,<<1>>,[<<252>>,[[[<<255>>,<<97,103,101>>],[<<248>>,<<29>>]]]]]
gmser_dyn:decode_typed(#{switch => #{name => binary, age => int}}, E1) ->
#{age => 29}
E2 = gmser_dyn:encode_typed(#{switch => #{name => binary, age => int}}, #{name => <<"Ulf">>}) ->
[<<0>>,<<1>>,[<<252>>,[[[<<255>>,<<110,97,109,101>>],[<<249>>,<<85,108,102>>]]]]]
gmser_dyn:decode_typed(#{switch => #{name => binary, age => int}}, E1) ->
#{name => <<"Ulf">>}
```
A practical use of `switch` would be in a protocol schema:
```erlang
t_msg(_) ->
#{switch => #{ call => t_call
, reply => t_reply
, notification => t_notification }}.
t_call(_) ->
#{items => [ {id, anyint}
, {req, t_req} ]}.
t_reply(_) ->
#{alt => [#{items => [ {id, anyint}
, {result, t_result} ]},
#{items => [ {id, anyint}
, {code, anyint}
, {message, binary} ]}
]}.
```
In this scenario, messages are 'taggged' as 1-element maps, e.g.:
```erlang
async_request(Msg) ->
Id = erlang:unique_integer(),
gmmp_cp:to_server(
whereis(gmmp_core_connector),
#{call => #{ id => Id
, req => Msg }}),
Id.
```
### Notes
Note that `anyint` is a standard type. The static serializer supports only
positive integers (`int`), as negative numbers are forbidden on-chain.
For dynamic encoding e.g. in messaging protocols, handling negative numbers can
be useful, so the `negint` type was added as a dynamic type. To encode a full-range
integer, the `alt` construct is needed.
(Floats are not supported, as they are non-deterministic. Rationals and fixed-point
numbers could easily be handled as high-level types, e.g. as `{int,int}`.)

View File

@ -1,83 +0,0 @@
# Static Serialization
The `gmserialization` and `gmser_chain_objects` modules implement the
static serialization support used in the Gajumaru blockchain.
The purpose is to produce fully deterministic serialization, in order
to maintain predictable hashing.
Example:
```erlang
%% deterministic canonical serialization.
-spec serialize_to_binary(signed_tx()) -> binary_signed_tx().
serialize_to_binary(#signed_tx{tx = Tx, signatures = Sigs}) ->
gmser_chain_objects:serialize(
?SIG_TX_TYPE,
?SIG_TX_VSN,
serialization_template(?SIG_TX_VSN),
[ {signatures, lists:sort(Sigs)}
, {transaction, aetx:serialize_to_binary(Tx)}
]).
-spec deserialize_from_binary(binary()) -> signed_tx().
deserialize_from_binary(SignedTxBin) when is_binary(SignedTxBin) ->
[ {signatures, Sigs}
, {transaction, TxBin}
] = gmser_chain_objects:deserialize(
?SIG_TX_TYPE,
?SIG_TX_VSN,
serialization_template(?SIG_TX_VSN),
SignedTxBin),
assert_sigs_size(Sigs),
#signed_tx{ tx = aetx:deserialize_from_binary(TxBin)
, signatures = Sigs
}.
serialization_template(?SIG_TX_VSN) ->
[ {signatures, [binary]}
, {transaction, binary}
].
```
The terms that can be encoded using these templates are given by
this type in `gmserialization.erl`:
```erlang
-type encodable_term() :: non_neg_integer()
| binary()
| boolean()
| [encodable_term()] %% Of any length
| #{atom() => encodable_term()}
| tuple() %% Any arity, containing encodable_term().
| gmser_id:id().
```
The template 'language' is defined by these types:
```erlang
-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.
| #{items := [{field_name(), type()}]} %% Record with named fields
%% represented as a map.
%% Encoded as a list in the given
%% order.
| tuple(). %% Any arity, containing type(). This means a static size array.
```
The `gmser_chain_objects.erl` module specifies a serialization code for each
object that can go on-chain. E.g.:
```erlang
tag(signed_tx) -> 11;
...
rev_tag(11) -> signed_tx;
```
The `tag` and `vsn` are laid out in the beginning of the serialized object.

View File

@ -1,11 +0,0 @@
{application,gmserialization,
[{description,"Serialization of data for the Gajumaru"},
{vsn,"0.1.2"},
{registered,[]},
{applications,[kernel,stdlib,crypto,base58]},
{env,[]},
{modules,[gmser_api_encoder,gmser_chain_objects,
gmser_contract_code,gmser_delegation,gmser_id,
gmser_rlp,gmserialization]},
{licenses,[]},
{links,[]}]}.

View File

@ -1,17 +1,4 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, {deps, [ {base58, {git, "https://github.com/aeternity/erl-base58.git", {ref, "60a3356"}}}
[{base58, , {enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "67fceef"}}}
{git, ]}.
"https://git.qpq.swiss/QPQ-AG/erl-base58.git",
{ref, "e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}}},
{enacl,
{git,
"https://git.qpq.swiss/QPQ-AG/enacl.git",
{ref, "4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}}},
{eblake2, "1.0.0"}
]}.
{dialyzer,
[ {plt_apps, all_deps},
{base_plt_apps, [erts, kernel, stdlib, enacl, base58, eblake2]}
]}.

View File

@ -1,16 +1,8 @@
{"1.2.0",
[{<<"base58">>, [{<<"base58">>,
{git,"https://git.qpq.swiss/QPQ-AG/erl-base58.git", {git,"https://github.com/aeternity/erl-base58.git",
{ref,"e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}}, {ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
0}, 0},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"enacl">>, {<<"enacl">>,
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git", {git,"https://github.com/aeternity/enacl.git",
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}}, {ref,"67fceef42c0d055570f2e67b571f8d1f8de2f204"}},
0}]}. 0}].
[
{pkg_hash,[
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>}]},
{pkg_hash_ext,[
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>}]}
].

BIN
rebar3

Binary file not shown.

View File

@ -1,50 +1,41 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% API encoding for the Gajumaru %%% API encoding for the Aeternity node.
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_api_encoder). -module(aeser_api_encoder).
-vsn("0.1.2").
-export([encode/2, -export([encode/2,
decode/1, decode/1,
safe_decode/2, safe_decode/2,
byte_size_for_type/1]). byte_size_for_type/1]).
-export([encode_keypair/1, -export_type([encoded/0]).
safe_decode_keypair/1]).
-export([unsafe_encode/2]). %% Encode without size checks
-export_type([encoded/0,
known_type/0]).
-type known_type() :: key_block_hash -type known_type() :: key_block_hash
| micro_block_hash | micro_block_hash
| block_pof_hash | block_pof_hash
| block_tx_hash | block_tx_hash
| block_state_hash | block_state_hash
| block_witness_hash
| channel | channel
| contract_bytearray | contract_bytearray
| contract_pubkey | contract_pubkey
| contract_store_key | contract_store_key
| contract_store_value | contract_store_value
| contract_source
| transaction | transaction
| tx_hash | tx_hash
| oracle_pubkey
| oracle_query
| oracle_query_id
| oracle_response
| account_pubkey | account_pubkey
| account_seckey | account_seckey
| associate_chain | associate_chain
| entry
| signature | signature
| name | name
| native_token
| commitment | commitment
| peer_pubkey | peer_pubkey
| hash
| state | state
| poi | poi
| state_trees | state_trees
@ -58,66 +49,14 @@
-type payload() :: binary(). -type payload() :: binary().
-type encoded() :: binary(). -type encoded() :: binary().
-type keypair() :: #{public := <<_:(32*8)>>, secret := <<_:(64*8)>>}.
-type encoded_keypair() :: #{binary() => binary()}.
-export_type([ keypair/0
, encoded_keypair/0 ]).
-define(BASE58, 1). -define(BASE58, 1).
-define(BASE64, 2). -define(BASE64, 2).
-spec encode_keypair(keypair()) -> encoded_keypair(). -spec encode(known_type(), payload() | aeser_id:id()) -> encoded().
encode_keypair(#{public := Pub, secret := Sec}) ->
case Sec of
<<Seed:32/binary, Pub1:32/binary>> when Pub1 =:= Pub ->
#{ <<"pub">> => encode(account_pubkey, Pub)
, <<"priv">> => encode(account_seckey, Seed) };
_ ->
erlang:error(invalid_keypair)
end.
-spec safe_decode_keypair(encoded_keypair()) -> {'ok', keypair()} | {'error', atom()}.
safe_decode_keypair(#{<<"pub">> := EncPub, <<"priv">> := EncPriv}) ->
case safe_decode(account_pubkey, EncPub) of
{ok, Pub} ->
case safe_decode(account_seckey, EncPriv) of
{ok, Seed} when byte_size(Seed) =:= 32 ->
case enacl:sign_seed_keypair(Seed) of
#{public := Pub, secret := _} = KP ->
{ok, KP};
_ ->
{error, illegal_encoding}
end;
{ok, <<Seed:32/binary, Pub:32/binary>>} ->
case enacl:sign_seed_keypair(Seed) of
#{public := Pub} = KP ->
{ok, KP};
_ ->
{error, illegal_encoding}
end;
{ok, _} ->
{error, illegal_encoding};
{error, _} = Error1 ->
Error1
end;
Error ->
Error
end.
-spec encode(known_type(), payload() | gmser_id:id()) -> encoded().
encode(id_hash, Payload) -> encode(id_hash, Payload) ->
{IdType, Val} = gmser_id:specialize(Payload), {IdType, Val} = aeser_id:specialize(Payload),
encode(id2type(IdType), Val); encode(id2type(IdType), Val);
encode(Type, Payload) -> encode(Type, Payload) ->
case type_size_check(Type, Payload) of
ok ->
unsafe_encode(Type, Payload);
{error, Reason} ->
erlang:error(Reason)
end.
unsafe_encode(Type, Payload) ->
Pfx = type2pfx(Type), Pfx = type2pfx(Type),
Enc = case type2enc(Type) of Enc = case type2enc(Type) of
?BASE58 -> base58_check(Payload); ?BASE58 -> base58_check(Payload);
@ -125,7 +64,6 @@ unsafe_encode(Type, Payload) ->
end, end,
<<Pfx/binary, "_", Enc/binary>>. <<Pfx/binary, "_", Enc/binary>>.
-spec decode(binary()) -> {known_type(), payload()}. -spec decode(binary()) -> {known_type(), payload()}.
decode(Bin0) -> decode(Bin0) ->
case split(Bin0) of case split(Bin0) of
@ -141,13 +79,6 @@ decode(Bin0) ->
erlang:error(missing_prefix) erlang:error(missing_prefix)
end. end.
type_size_check(account_seckey, Bin) ->
case byte_size(Bin) of
Sz when Sz =:= 32; Sz =:= 64 ->
ok;
_ ->
{error, incorrect_size}
end;
type_size_check(Type, Bin) -> type_size_check(Type, Bin) ->
case byte_size_for_type(Type) of case byte_size_for_type(Type) of
not_applicable -> ok; not_applicable -> ok;
@ -159,14 +90,14 @@ type_size_check(Type, Bin) ->
end end
end. end.
-spec safe_decode(extended_type(), encoded()) -> {'ok', payload() | gmser_id:id()} -spec safe_decode(extended_type(), encoded()) -> {'ok', payload() | aeser_id:id()}
| {'error', atom()}. | {'error', atom()}.
safe_decode({id_hash, AllowedTypes}, Enc) -> safe_decode({id_hash, AllowedTypes}, Enc) ->
try decode(Enc) of try decode(Enc) of
{ActualType, Dec} -> {ActualType, Dec} ->
case lists:member(ActualType, AllowedTypes) of case lists:member(ActualType, AllowedTypes) of
true -> true ->
try {ok, gmser_id:create(type2id(ActualType), Dec)} try {ok, aeser_id:create(type2id(ActualType), Dec)}
catch error:_ -> {error, invalid_prefix} catch error:_ -> {error, invalid_prefix}
end; end;
false -> false ->
@ -237,35 +168,33 @@ id2type(associate_chain) -> associate_chain;
id2type(channel) -> channel; id2type(channel) -> channel;
id2type(commitment) -> commitment; id2type(commitment) -> commitment;
id2type(contract) -> contract_pubkey; id2type(contract) -> contract_pubkey;
id2type(contract_source) -> contract_source;
id2type(name) -> name; id2type(name) -> name;
id2type(native_token) -> native_token; id2type(oracle) -> oracle_pubkey.
id2type(entry) -> entry.
type2id(account_pubkey) -> account; type2id(account_pubkey) -> account;
type2id(associate_chain) -> associate_chain; type2id(associate_chain) -> associate_chain;
type2id(channel) -> channel; type2id(channel) -> channel;
type2id(commitment) -> commitment; type2id(commitment) -> commitment;
type2id(contract_pubkey) -> contract; type2id(contract_pubkey) -> contract;
type2id(contract_source) -> contract_source;
type2id(name) -> name; type2id(name) -> name;
type2id(native_token) -> native_token; type2id(oracle_pubkey) -> oracle.
type2id(entry) -> entry.
type2enc(key_block_hash) -> ?BASE58; type2enc(key_block_hash) -> ?BASE58;
type2enc(micro_block_hash) -> ?BASE58; type2enc(micro_block_hash) -> ?BASE58;
type2enc(block_pof_hash) -> ?BASE58; type2enc(block_pof_hash) -> ?BASE58;
type2enc(block_tx_hash) -> ?BASE58; type2enc(block_tx_hash) -> ?BASE58;
type2enc(block_state_hash) -> ?BASE58; type2enc(block_state_hash) -> ?BASE58;
type2enc(block_witness_hash) -> ?BASE58;
type2enc(channel) -> ?BASE58; type2enc(channel) -> ?BASE58;
type2enc(contract_pubkey) -> ?BASE58; type2enc(contract_pubkey) -> ?BASE58;
type2enc(contract_bytearray) -> ?BASE64; type2enc(contract_bytearray) -> ?BASE64;
type2enc(contract_store_key) -> ?BASE64; type2enc(contract_store_key) -> ?BASE64;
type2enc(contract_store_value) -> ?BASE64; type2enc(contract_store_value) -> ?BASE64;
type2enc(contract_source) -> ?BASE64;
type2enc(transaction) -> ?BASE64; type2enc(transaction) -> ?BASE64;
type2enc(tx_hash) -> ?BASE58; 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(account_pubkey) -> ?BASE58;
type2enc(account_seckey) -> ?BASE58; type2enc(account_seckey) -> ?BASE58;
type2enc(associate_chain) -> ?BASE58; type2enc(associate_chain) -> ?BASE58;
@ -273,14 +202,12 @@ type2enc(signature) -> ?BASE58;
type2enc(commitment) -> ?BASE58; type2enc(commitment) -> ?BASE58;
type2enc(peer_pubkey) -> ?BASE58; type2enc(peer_pubkey) -> ?BASE58;
type2enc(name) -> ?BASE58; type2enc(name) -> ?BASE58;
type2enc(native_token) -> ?BASE58;
type2enc(state) -> ?BASE64; type2enc(state) -> ?BASE64;
type2enc(poi) -> ?BASE64; type2enc(poi) -> ?BASE64;
type2enc(state_trees) -> ?BASE64; type2enc(state_trees) -> ?BASE64;
type2enc(call_state_tree) -> ?BASE64; type2enc(call_state_tree) -> ?BASE64;
type2enc(mp_tree_hash) -> ?BASE58; type2enc(mp_tree_hash) -> ?BASE58;
type2enc(hash) -> ?BASE58; type2enc(hash) -> ?BASE58;
type2enc(entry) -> ?BASE64;
type2enc(bytearray) -> ?BASE64. type2enc(bytearray) -> ?BASE64.
@ -289,15 +216,17 @@ type2pfx(micro_block_hash) -> <<"mh">>;
type2pfx(block_pof_hash) -> <<"bf">>; type2pfx(block_pof_hash) -> <<"bf">>;
type2pfx(block_tx_hash) -> <<"bx">>; type2pfx(block_tx_hash) -> <<"bx">>;
type2pfx(block_state_hash) -> <<"bs">>; type2pfx(block_state_hash) -> <<"bs">>;
type2pfx(block_witness_hash) -> <<"ws">>;
type2pfx(channel) -> <<"ch">>; type2pfx(channel) -> <<"ch">>;
type2pfx(contract_pubkey) -> <<"ct">>; type2pfx(contract_pubkey) -> <<"ct">>;
type2pfx(contract_bytearray) -> <<"cb">>; type2pfx(contract_bytearray) -> <<"cb">>;
type2pfx(contract_store_key) -> <<"ck">>; type2pfx(contract_store_key) -> <<"ck">>;
type2pfx(contract_store_value) -> <<"cv">>; type2pfx(contract_store_value) -> <<"cv">>;
type2pfx(contract_source) -> <<"cx">>;
type2pfx(transaction) -> <<"tx">>; type2pfx(transaction) -> <<"tx">>;
type2pfx(tx_hash) -> <<"th">>; 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(account_pubkey) -> <<"ak">>;
type2pfx(account_seckey) -> <<"sk">>; type2pfx(account_seckey) -> <<"sk">>;
type2pfx(associate_chain) -> <<"ac">>; type2pfx(associate_chain) -> <<"ac">>;
@ -305,14 +234,12 @@ type2pfx(signature) -> <<"sg">>;
type2pfx(commitment) -> <<"cm">>; type2pfx(commitment) -> <<"cm">>;
type2pfx(peer_pubkey) -> <<"pp">>; type2pfx(peer_pubkey) -> <<"pp">>;
type2pfx(name) -> <<"nm">>; type2pfx(name) -> <<"nm">>;
type2pfx(native_token) -> <<"nt">>;
type2pfx(state) -> <<"st">>; type2pfx(state) -> <<"st">>;
type2pfx(poi) -> <<"pi">>; type2pfx(poi) -> <<"pi">>;
type2pfx(state_trees) -> <<"ss">>; type2pfx(state_trees) -> <<"ss">>;
type2pfx(call_state_tree) -> <<"cs">>; type2pfx(call_state_tree) -> <<"cs">>;
type2pfx(mp_tree_hash) -> <<"mt">>; type2pfx(mp_tree_hash) -> <<"mt">>;
type2pfx(hash) -> <<"hs">>; type2pfx(hash) -> <<"hs">>;
type2pfx(entry) -> <<"en">>;
type2pfx(bytearray) -> <<"ba">>. type2pfx(bytearray) -> <<"ba">>.
pfx2type(<<"kh">>) -> key_block_hash; pfx2type(<<"kh">>) -> key_block_hash;
@ -320,15 +247,17 @@ pfx2type(<<"mh">>) -> micro_block_hash;
pfx2type(<<"bf">>) -> block_pof_hash; pfx2type(<<"bf">>) -> block_pof_hash;
pfx2type(<<"bx">>) -> block_tx_hash; pfx2type(<<"bx">>) -> block_tx_hash;
pfx2type(<<"bs">>) -> block_state_hash; pfx2type(<<"bs">>) -> block_state_hash;
pfx2type(<<"ws">>) -> block_witness_hash;
pfx2type(<<"ch">>) -> channel; pfx2type(<<"ch">>) -> channel;
pfx2type(<<"cb">>) -> contract_bytearray; pfx2type(<<"cb">>) -> contract_bytearray;
pfx2type(<<"ck">>) -> contract_store_key; pfx2type(<<"ck">>) -> contract_store_key;
pfx2type(<<"cv">>) -> contract_store_value; pfx2type(<<"cv">>) -> contract_store_value;
pfx2type(<<"ct">>) -> contract_pubkey; pfx2type(<<"ct">>) -> contract_pubkey;
pfx2type(<<"cx">>) -> contract_source;
pfx2type(<<"tx">>) -> transaction; pfx2type(<<"tx">>) -> transaction;
pfx2type(<<"th">>) -> tx_hash; 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(<<"ak">>) -> account_pubkey;
pfx2type(<<"sk">>) -> account_seckey; pfx2type(<<"sk">>) -> account_seckey;
pfx2type(<<"ac">>) -> associate_chain; pfx2type(<<"ac">>) -> associate_chain;
@ -336,14 +265,12 @@ pfx2type(<<"sg">>) -> signature;
pfx2type(<<"cm">>) -> commitment; pfx2type(<<"cm">>) -> commitment;
pfx2type(<<"pp">>) -> peer_pubkey; pfx2type(<<"pp">>) -> peer_pubkey;
pfx2type(<<"nm">>) -> name; pfx2type(<<"nm">>) -> name;
pfx2type(<<"nt">>) -> native_token;
pfx2type(<<"st">>) -> state; pfx2type(<<"st">>) -> state;
pfx2type(<<"pi">>) -> poi; pfx2type(<<"pi">>) -> poi;
pfx2type(<<"ss">>) -> state_trees; pfx2type(<<"ss">>) -> state_trees;
pfx2type(<<"cs">>) -> call_state_tree; pfx2type(<<"cs">>) -> call_state_tree;
pfx2type(<<"mt">>) -> mp_tree_hash; pfx2type(<<"mt">>) -> mp_tree_hash;
pfx2type(<<"hs">>) -> hash; pfx2type(<<"hs">>) -> hash;
pfx2type(<<"en">>) -> entry;
pfx2type(<<"ba">>) -> bytearray. pfx2type(<<"ba">>) -> bytearray.
-spec byte_size_for_type(known_type()) -> non_neg_integer() | not_applicable. -spec byte_size_for_type(known_type()) -> non_neg_integer() | not_applicable.
@ -353,21 +280,22 @@ byte_size_for_type(micro_block_hash) -> 32;
byte_size_for_type(block_pof_hash) -> 32; byte_size_for_type(block_pof_hash) -> 32;
byte_size_for_type(block_tx_hash) -> 32; byte_size_for_type(block_tx_hash) -> 32;
byte_size_for_type(block_state_hash) -> 32; byte_size_for_type(block_state_hash) -> 32;
byte_size_for_type(block_witness_hash) -> 32;
byte_size_for_type(channel) -> 32; byte_size_for_type(channel) -> 32;
byte_size_for_type(contract_pubkey) -> 32; byte_size_for_type(contract_pubkey) -> 32;
byte_size_for_type(contract_bytearray) -> not_applicable; byte_size_for_type(contract_bytearray) -> not_applicable;
byte_size_for_type(contract_store_key) -> not_applicable; byte_size_for_type(contract_store_key) -> not_applicable;
byte_size_for_type(contract_store_value) -> not_applicable; byte_size_for_type(contract_store_value) -> not_applicable;
byte_size_for_type(contract_source) -> not_applicable;
byte_size_for_type(transaction) -> not_applicable; byte_size_for_type(transaction) -> not_applicable;
byte_size_for_type(tx_hash) -> 32; 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(account_pubkey) -> 32;
byte_size_for_type(account_seckey) -> 32; byte_size_for_type(account_seckey) -> 32;
byte_size_for_type(associate_chain) -> 32; byte_size_for_type(associate_chain) -> 32;
byte_size_for_type(signature) -> 64; byte_size_for_type(signature) -> 64;
byte_size_for_type(name) -> not_applicable; byte_size_for_type(name) -> not_applicable;
byte_size_for_type(native_token) -> 32;
byte_size_for_type(commitment) -> 32; byte_size_for_type(commitment) -> 32;
byte_size_for_type(peer_pubkey) -> 32; byte_size_for_type(peer_pubkey) -> 32;
byte_size_for_type(state) -> 32; byte_size_for_type(state) -> 32;
@ -376,7 +304,6 @@ byte_size_for_type(state_trees) -> not_applicable;
byte_size_for_type(call_state_tree) -> not_applicable; byte_size_for_type(call_state_tree) -> not_applicable;
byte_size_for_type(mp_tree_hash) -> 32; byte_size_for_type(mp_tree_hash) -> 32;
byte_size_for_type(hash) -> 32; byte_size_for_type(hash) -> 32;
byte_size_for_type(entry) -> not_applicable;
byte_size_for_type(bytearray) -> not_applicable. byte_size_for_type(bytearray) -> not_applicable.

View File

@ -1,22 +1,20 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% Functions for serializing chain objects to binary format. %%% Functions for serializing chain objects to binary format.
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_chain_objects). -module(aeser_chain_objects).
-vsn("0.1.2").
-export([ serialize/4 -export([ serialize/4
, deserialize/4 , deserialize/4
, deserialize_type_and_vsn/1 , deserialize_type_and_vsn/1
]). ]).
-type template() :: gmserialization:template(). -type template() :: aeserialization:template().
-type fields() :: gmserialization:fields(). -type fields() :: aeserialization:fields().
%%%=================================================================== %%%===================================================================
%%% API %%% API
@ -24,15 +22,15 @@
-spec serialize(atom(), non_neg_integer(), template(), fields()) -> binary(). -spec serialize(atom(), non_neg_integer(), template(), fields()) -> binary().
serialize(Type, Vsn, Template, Fields) -> serialize(Type, Vsn, Template, Fields) ->
gmserialization:serialize(tag(Type), Vsn, Template, Fields). aeserialization:serialize(tag(Type), Vsn, Template, Fields).
deserialize_type_and_vsn(Binary) -> deserialize_type_and_vsn(Binary) ->
{Tag, Vsn, Fields} = gmserialization:deserialize_tag_and_vsn(Binary), {Tag, Vsn, Fields} = aeserialization:deserialize_tag_and_vsn(Binary),
{rev_tag(Tag), Vsn, Fields}. {rev_tag(Tag), Vsn, Fields}.
-spec deserialize(atom(), non_neg_integer(), template(), binary()) -> fields(). -spec deserialize(atom(), non_neg_integer(), template(), binary()) -> fields().
deserialize(Type, Vsn, Template, Binary) -> deserialize(Type, Vsn, Template, Binary) ->
gmserialization:deserialize(Type, tag(Type), Vsn, Template, Binary). aeserialization:deserialize(Type, tag(Type), Vsn, Template, Binary).
%%%=================================================================== %%%===================================================================
%%% Internal functions %%% Internal functions
@ -41,7 +39,12 @@ deserialize(Type, Vsn, Template, Binary) ->
tag(account) -> 10; tag(account) -> 10;
tag(signed_tx) -> 11; tag(signed_tx) -> 11;
tag(spend_tx) -> 12; tag(spend_tx) -> 12;
tag(data_extend_tx) -> 13; 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) -> 30;
tag(name_commitment) -> 31; tag(name_commitment) -> 31;
tag(name_claim_tx) -> 32; tag(name_claim_tx) -> 32;
@ -54,10 +57,8 @@ tag(contract) -> 40;
tag(contract_call) -> 41; tag(contract_call) -> 41;
tag(contract_create_tx) -> 42; tag(contract_create_tx) -> 42;
tag(contract_call_tx) -> 43; tag(contract_call_tx) -> 43;
tag(contract_source) -> 44;
tag(channel_create_tx) -> 50; tag(channel_create_tx) -> 50;
tag(channel_set_delegates_tx) -> 501; tag(channel_set_delegates_tx) -> 501;
tag(channel_delegates) -> 502;
tag(channel_deposit_tx) -> 51; tag(channel_deposit_tx) -> 51;
tag(channel_withdraw_tx) -> 52; tag(channel_withdraw_tx) -> 52;
tag(channel_force_progress_tx) -> 521; tag(channel_force_progress_tx) -> 521;
@ -84,9 +85,9 @@ tag(contracts_mtree) -> 621;
tag(calls_mtree) -> 622; tag(calls_mtree) -> 622;
tag(channels_mtree) -> 623; tag(channels_mtree) -> 623;
tag(nameservice_mtree) -> 624; tag(nameservice_mtree) -> 624;
tag(oracles_mtree) -> 625;
tag(accounts_mtree) -> 626; tag(accounts_mtree) -> 626;
tag(acs_mtree) -> 627; tag(acs_mtree) -> 627;
tag(entries_mtree) -> 628;
tag(compiler_sophia) -> 70; tag(compiler_sophia) -> 70;
tag(ga_attach_tx) -> 80; tag(ga_attach_tx) -> 80;
tag(ga_meta_tx) -> 81; tag(ga_meta_tx) -> 81;
@ -99,28 +100,20 @@ tag(ac_create_tx) -> 93;
tag(ac_deposit_tx) -> 94; tag(ac_deposit_tx) -> 94;
tag(ac_update_cops_tx) -> 95; tag(ac_update_cops_tx) -> 95;
tag(ac_rollup_tx) -> 96; tag(ac_rollup_tx) -> 96;
tag(ac_proposal_tx) -> 97;
tag(key_block) -> 100; tag(key_block) -> 100;
tag(micro_block) -> 101; tag(micro_block) -> 101;
tag(light_micro_block) -> 102; tag(light_micro_block) -> 102;
tag(testimony) -> 110;
tag(testimony_tx) -> 111;
tag(nt_native_token) -> 120;
tag(nt_create_tx) -> 121;
tag(nt_mint_tx) -> 122;
tag(nt_finalize_tx) -> 123;
tag(nt_trade_tx) -> 124;
tag(nt_burn_tx) -> 125;
tag(entry) -> 140;
tag(entry_create_tx) -> 141;
tag(entry_transfer_tx) -> 142;
tag(entry_destroy_tx) -> 143;
tag(pof) -> 200. tag(pof) -> 200.
rev_tag(10) -> account; rev_tag(10) -> account;
rev_tag(11) -> signed_tx; rev_tag(11) -> signed_tx;
rev_tag(12) -> spend_tx; rev_tag(12) -> spend_tx;
rev_tag(13) -> data_extend_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(30) -> name;
rev_tag(31) -> name_commitment; rev_tag(31) -> name_commitment;
rev_tag(32) -> name_claim_tx; rev_tag(32) -> name_claim_tx;
@ -133,10 +126,8 @@ rev_tag(40) -> contract;
rev_tag(41) -> contract_call; rev_tag(41) -> contract_call;
rev_tag(42) -> contract_create_tx; rev_tag(42) -> contract_create_tx;
rev_tag(43) -> contract_call_tx; rev_tag(43) -> contract_call_tx;
rev_tag(44) -> contract_source;
rev_tag(50) -> channel_create_tx; rev_tag(50) -> channel_create_tx;
rev_tag(501) -> channel_set_delegates_tx; rev_tag(501) -> channel_set_delegates_tx;
rev_tag(502) -> channel_delegates;
rev_tag(51) -> channel_deposit_tx; rev_tag(51) -> channel_deposit_tx;
rev_tag(52) -> channel_withdraw_tx; rev_tag(52) -> channel_withdraw_tx;
rev_tag(521) -> channel_force_progress_tx; rev_tag(521) -> channel_force_progress_tx;
@ -163,9 +154,9 @@ rev_tag(621) -> contracts_mtree;
rev_tag(622) -> calls_mtree; rev_tag(622) -> calls_mtree;
rev_tag(623) -> channels_mtree; rev_tag(623) -> channels_mtree;
rev_tag(624) -> nameservice_mtree; rev_tag(624) -> nameservice_mtree;
rev_tag(625) -> oracles_mtree;
rev_tag(626) -> accounts_mtree; rev_tag(626) -> accounts_mtree;
rev_tag(627) -> acs_mtree; rev_tag(627) -> acs_mtree;
rev_tag(628) -> entries_mtree;
rev_tag(70) -> compiler_sophia; rev_tag(70) -> compiler_sophia;
rev_tag(80) -> ga_attach_tx; rev_tag(80) -> ga_attach_tx;
rev_tag(81) -> ga_meta_tx; rev_tag(81) -> ga_meta_tx;
@ -178,20 +169,7 @@ rev_tag(93) -> ac_create_tx;
rev_tag(94) -> ac_deposit_tx; rev_tag(94) -> ac_deposit_tx;
rev_tag(95) -> ac_update_cops_tx; rev_tag(95) -> ac_update_cops_tx;
rev_tag(96) -> ac_rollup_tx; rev_tag(96) -> ac_rollup_tx;
rev_tag(97) -> ac_proposal_tx;
rev_tag(100) -> key_block; rev_tag(100) -> key_block;
rev_tag(101) -> micro_block; rev_tag(101) -> micro_block;
rev_tag(102) -> light_micro_block; rev_tag(102) -> light_micro_block;
rev_tag(110) -> testimony;
rev_tag(111) -> testimony_tx;
rev_tag(120) -> nt_native_token;
rev_tag(121) -> nt_create_tx;
rev_tag(122) -> nt_mint_tx;
rev_tag(123) -> nt_finalize_tx;
rev_tag(124) -> nt_trade_tx;
rev_tag(125) -> nt_burn_tx;
rev_tag(140) -> entry;
rev_tag(141) -> entry_create_tx;
rev_tag(142) -> entry_transfer_tx;
rev_tag(143) -> entry_destroy_tx;
rev_tag(200) -> pof. rev_tag(200) -> pof.

View File

@ -1,14 +1,12 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt %%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc %%% @doc
%%% Serialization of contract code %%% Serialization of contract code
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_contract_code). -module(aeser_contract_code).
-vsn("0.1.2").
-include("gmser_contract_code.hrl"). -include("aeser_contract_code.hrl").
-export([ deserialize/1 -export([ deserialize/1
, serialize/1 , serialize/1
@ -18,16 +16,15 @@
serialize(CodeMap) -> serialize(CodeMap) ->
serialize(CodeMap, ?SOPHIA_CONTRACT_VSN_3). serialize(CodeMap, ?SOPHIA_CONTRACT_VSN_3).
-spec serialize(map(), non_neg_integer()) -> binary(). -spec serialize(map(), non_neg_integer()) -> binary().
serialize(CodeMap = #{ byte_code := ByteCode serialize(CodeMap = #{ byte_code := ByteCode
, type_info := TypeInfo }, SophiaContractVersion) -> , type_info := TypeInfo }, SophiaContractVersion) ->
%% Source hash %% Source hash
SourceHash = SourceHash = case CodeMap of
case CodeMap of #{ source_hash := SHash } -> SHash;
#{ source_hash := SHash } -> SHash; #{ contract_source := SrcStr } ->
#{ contract_source := SrcStr } -> blake2(32, list_to_binary(SrcStr)) enacl:generichash(32, list_to_binary(SrcStr))
end, end,
%% Compiler version %% Compiler version
Version = maps:get(compiler_version, CodeMap, <<"unknown">>), Version = maps:get(compiler_version, CodeMap, <<"unknown">>),
@ -45,35 +42,20 @@ serialize(CodeMap = #{ byte_code := ByteCode
|| SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_1 ] ++ || SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_1 ] ++
[ {payable, Payable} [ {payable, Payable}
|| SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_2 ], || SophiaContractVersion > ?SOPHIA_CONTRACT_VSN_2 ],
gmser_chain_objects:serialize(compiler_sophia, aeser_chain_objects:serialize(compiler_sophia,
SophiaContractVersion, SophiaContractVersion,
serialization_template(SophiaContractVersion), serialization_template(SophiaContractVersion),
Fields). 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(). -spec deserialize(binary()) -> map().
deserialize(Binary) -> deserialize(Binary) ->
case gmser_chain_objects:deserialize_type_and_vsn(Binary) of case aeser_chain_objects:deserialize_type_and_vsn(Binary) of
{compiler_sophia = Type, ?SOPHIA_CONTRACT_VSN_1 = Vsn, _Rest} -> {compiler_sophia = Type, ?SOPHIA_CONTRACT_VSN_1 = Vsn, _Rest} ->
Template = serialization_template(Vsn), Template = serialization_template(Vsn),
[ {source_hash, Hash} [ {source_hash, Hash}
, {type_info, TypeInfo} , {type_info, TypeInfo}
, {byte_code, ByteCode} , {byte_code, ByteCode}
] = gmser_chain_objects:deserialize(Type, Vsn, Template, Binary), ] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash #{ source_hash => Hash
, type_info => TypeInfo , type_info => TypeInfo
, byte_code => ByteCode , byte_code => ByteCode
@ -86,7 +68,7 @@ deserialize(Binary) ->
, {type_info, TypeInfo} , {type_info, TypeInfo}
, {byte_code, ByteCode} , {byte_code, ByteCode}
, {compiler_version, CompilerVersion} , {compiler_version, CompilerVersion}
] = gmser_chain_objects:deserialize(Type, Vsn, Template, Binary), ] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash #{ source_hash => Hash
, type_info => TypeInfo , type_info => TypeInfo
, byte_code => ByteCode , byte_code => ByteCode
@ -101,7 +83,7 @@ deserialize(Binary) ->
, {byte_code, ByteCode} , {byte_code, ByteCode}
, {compiler_version, CompilerVersion} , {compiler_version, CompilerVersion}
, {payable, Payable} , {payable, Payable}
] = gmser_chain_objects:deserialize(Type, Vsn, Template, Binary), ] = aeser_chain_objects:deserialize(Type, Vsn, Template, Binary),
#{ source_hash => Hash #{ source_hash => Hash
, type_info => TypeInfo , type_info => TypeInfo
, byte_code => ByteCode , byte_code => ByteCode

View File

@ -1,16 +1,17 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2023, Aeternity Anstalt %%% @copyright (C) 2023, Aeternity Anstalt
%%% @doc %%% @doc
%%% Serialization of delegation signatures %%% Serialization of delegation signatures
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_delegation). -module(aeser_delegation).
-vsn("0.1.2").
-export([ aens_preclaim_sig/3 -export([ aens_preclaim_sig/3
, aens_name_sig/4 , aens_name_sig/4
, aens_sig/3 , aens_sig/3
, oracle_sig/3
, oracle_response_sig/3
]). ]).
%% Delegation signatures are prefixed with a unique tag, ensuring not to %% Delegation signatures are prefixed with a unique tag, ensuring not to
@ -27,7 +28,7 @@
-type sig_data() :: binary(). -type sig_data() :: binary().
-spec aens_preclaim_sig(binary(), gmser_id:id(), gmser_id:id()) -> sig_data(). -spec aens_preclaim_sig(binary(), aeser_id:id(), aeser_id:id()) -> sig_data().
aens_preclaim_sig(NetworkId, Account, Contract) -> aens_preclaim_sig(NetworkId, Account, Contract) ->
assert_id(account, Account), assert_id(account, Account),
assert_id(contract, Contract), assert_id(contract, Contract),
@ -35,7 +36,7 @@ aens_preclaim_sig(NetworkId, Account, Contract) ->
Fields = [{account, Account}, {contract, Contract}], Fields = [{account, Account}, {contract, Contract}],
serialize(?TYPE_AENS_PRECLAIM, NetworkId, Template, Fields). serialize(?TYPE_AENS_PRECLAIM, NetworkId, Template, Fields).
-spec aens_name_sig(binary(), gmser_id:id(), gmser_id:id(), gmser_id:id()) -> sig_data(). -spec aens_name_sig(binary(), aeser_id:id(), aeser_id:id(), aeser_id:id()) -> sig_data().
aens_name_sig(NetworkId, Account, Name, Contract) -> aens_name_sig(NetworkId, Account, Name, Contract) ->
assert_id(account, Account), assert_id(account, Account),
assert_id(name, Name), assert_id(name, Name),
@ -44,7 +45,7 @@ aens_name_sig(NetworkId, Account, Name, Contract) ->
Fields = [{account, Account}, {name, Name}, {contract, Contract}], Fields = [{account, Account}, {name, Name}, {contract, Contract}],
serialize(?TYPE_AENS_NAME, NetworkId, Template, Fields). serialize(?TYPE_AENS_NAME, NetworkId, Template, Fields).
-spec aens_sig(binary(), gmser_id:id(), gmser_id:id()) -> sig_data(). -spec aens_sig(binary(), aeser_id:id(), aeser_id:id()) -> sig_data().
aens_sig(NetworkId, Account, Contract) -> aens_sig(NetworkId, Account, Contract) ->
assert_id(account, Account), assert_id(account, Account),
assert_id(contract, Contract), assert_id(contract, Contract),
@ -52,14 +53,29 @@ aens_sig(NetworkId, Account, Contract) ->
Fields = [{account, Account}, {contract, Contract}], Fields = [{account, Account}, {contract, Contract}],
serialize(?TYPE_AENS, NetworkId, Template, Fields). serialize(?TYPE_AENS, NetworkId, Template, Fields).
-spec oracle_sig(binary(), aeser_id:id(), aeser_id:id()) -> sig_data().
oracle_sig(NetworkId, Account, Contract) ->
assert_id(account, Account),
assert_id(contract, Contract),
Template = [{account, id}, {contract, id}],
Fields = [{account, Account}, {contract, Contract}],
serialize(?TYPE_ORACLE, NetworkId, Template, Fields).
-spec oracle_response_sig(binary(), aeser_id:id(), aeser_id:id()) -> sig_data().
oracle_response_sig(NetworkId, QueryId, Contract) ->
assert_id(oracle, QueryId),
assert_id(contract, Contract),
Template = [{query, id}, {contract, id}],
Fields = [{query, QueryId}, {contract, Contract}],
serialize(?TYPE_ORACLE_RESPONSE, NetworkId, Template, Fields).
%% ------------------------------------------------------------------------ %% ------------------------------------------------------------------------
%% -- Internal functions %% -- Internal functions
%% ------------------------------------------------------------------------ %% ------------------------------------------------------------------------
serialize(Type, NetworkId, Template, Fields) -> serialize(Type, NetworkId, Template, Fields) ->
Data = gmserialization:serialize(Type, ?VSN, Template, Fields), Data = aeserialization:serialize(Type, ?VSN, Template, Fields),
<<?DELEGATION_TAG:16, NetworkId/binary, Data/binary>>. <<?DELEGATION_TAG:16, NetworkId/binary, Data/binary>>.
assert_id(Type, AeserId) -> assert_id(Type, AeserId) ->
Type = gmser_id:specialize_type(AeserId). Type = aeser_id:specialize_type(AeserId).

View File

@ -1,14 +1,12 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% ADT for identifiers %%% ADT for identifiers
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_id). -module(aeser_id).
-vsn("0.1.2").
-export([ create/2 -export([ create/2
, specialize/1 , specialize/1
@ -17,9 +15,6 @@
, is_id/1 , is_id/1
]). ]).
-export([ t_id/1
]).
%% For aec_serialization %% For aec_serialization
-export([ encode/1 -export([ encode/1
, decode/1 , decode/1
@ -29,18 +24,11 @@
, val , val
}). }).
-type tag() :: 'account' -type tag() :: 'account' | 'oracle' | 'name'
| 'associate_chain' | 'commitment' | 'contract' | 'channel'
| 'channel' | 'associate_chain' .
| 'commitment'
| 'contract'
| 'contract_source'
| 'name'
| 'native_token'
| 'entry'.
-type val() :: <<_:256>>. -type val() :: <<_:256>>.
-type id() :: #id{}. -opaque(id() :: #id{}).
-export_type([ id/0 -export_type([ id/0
, tag/0 , tag/0
@ -52,12 +40,12 @@
-define(SERIALIZED_SIZE, 33). %% ?TAG_SIZE + ?PUB_SIZE -define(SERIALIZED_SIZE, 33). %% ?TAG_SIZE + ?PUB_SIZE
-define(IS_TAG(___TAG___), ___TAG___ =:= account; -define(IS_TAG(___TAG___), ___TAG___ =:= account;
___TAG___ =:= oracle;
___TAG___ =:= name; ___TAG___ =:= name;
___TAG___ =:= commitment; ___TAG___ =:= commitment;
___TAG___ =:= contract; ___TAG___ =:= contract;
___TAG___ =:= channel; ___TAG___ =:= channel;
___TAG___ =:= associate_chain; ___TAG___ =:= associate_chain
___TAG___ =:= entry
). ).
-define(IS_VAL(___VAL___), byte_size(___VAL___) =:= 32). -define(IS_VAL(___VAL___), byte_size(___VAL___) =:= 32).
@ -104,9 +92,6 @@ decode(<<Tag:?TAG_SIZE/unit:8, Val:?PUB_SIZE/binary>>) ->
#id{ tag = decode_tag(Tag) #id{ tag = decode_tag(Tag)
, val = Val}. , val = Val}.
-spec t_id(any()) -> id().
t_id(#id{} = Id) -> Id.
%%%=================================================================== %%%===================================================================
%%% Internal functions %%% Internal functions
%%%=================================================================== %%%===================================================================
@ -114,19 +99,17 @@ t_id(#id{} = Id) -> Id.
encode_tag(account) -> 1; encode_tag(account) -> 1;
encode_tag(name) -> 2; encode_tag(name) -> 2;
encode_tag(commitment) -> 3; encode_tag(commitment) -> 3;
encode_tag(oracle) -> 4;
encode_tag(contract) -> 5; encode_tag(contract) -> 5;
encode_tag(channel) -> 6; encode_tag(channel) -> 6;
encode_tag(associate_chain) -> 7; encode_tag(associate_chain) -> 7;
encode_tag(native_token) -> 8;
encode_tag(entry) -> 9;
encode_tag(Other) -> error({illegal_id_tag_name, Other}). encode_tag(Other) -> error({illegal_id_tag_name, Other}).
decode_tag(1) -> account; decode_tag(1) -> account;
decode_tag(2) -> name; decode_tag(2) -> name;
decode_tag(3) -> commitment; decode_tag(3) -> commitment;
decode_tag(4) -> oracle;
decode_tag(5) -> contract; decode_tag(5) -> contract;
decode_tag(6) -> channel; decode_tag(6) -> channel;
decode_tag(7) -> associate_chain; decode_tag(7) -> associate_chain;
decode_tag(8) -> native_token;
decode_tag(9) -> entry;
decode_tag(X) -> error({illegal_id_tag, X}). decode_tag(X) -> error({illegal_id_tag, X}).

View File

@ -1,18 +1,14 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Implementation of the Recursive Length Prefix. %%% Implementation of the Recursive Length Prefix.
%%% %%%
%%% https://zxq9.com/archives/2749
%%% https://github.com/ethereum/wiki/wiki/RLP %%% https://github.com/ethereum/wiki/wiki/RLP
%%% %%%
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_rlp). -module(aeser_rlp).
-vsn("0.1.2").
-export([ decode/1 -export([ decode/1
, decode_one/1 , decode_one/1
, encode/1 , encode/1

View File

@ -1,5 +1,5 @@
{application, gmserialization, {application, aeserialization,
[{description, "Serialization of data for the Gajumaru"}, [{description, "Serialization of data for Aeternity"},
{vsn, "0.1.0"}, {vsn, "0.1.0"},
{registered, []}, {registered, []},
{applications, {applications,

View File

@ -1,20 +1,16 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% Functions for serializing generic objects to/from binary format. %%% Functions for serializing generic objects to/from binary format.
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmserialization). -module(aeserialization).
-vsn("0.1.2").
-export([ decode_fields/2 -export([ decode_fields/2
, decode_field/2
, deserialize/5 , deserialize/5
, deserialize_tag_and_vsn/1 , deserialize_tag_and_vsn/1
, encode_fields/2 , encode_fields/2
, encode_field/2
, serialize/4 ]). , serialize/4 ]).
%%%=================================================================== %%%===================================================================
@ -25,8 +21,6 @@
, fields/0 , fields/0
]). ]).
-export_type([ encodable_term/0 ]).
-type template() :: [{field_name(), type()}]. -type template() :: [{field_name(), type()}].
-type field_name() :: atom(). -type field_name() :: atom().
-type type() :: 'int' -type type() :: 'int'
@ -41,9 +35,9 @@
| binary() | binary()
| boolean() | boolean()
| [encodable_term()] %% Of any length | [encodable_term()] %% Of any length
| #{atom() => encodable_term()} | #{atom() => encodable_term()}
| tuple() %% Any arity, containing encodable_term(). | tuple() %% Any arity, containing encodable_term().
| gmser_id:id(). | aeser_id:id().
-type fields() :: [{field_name(), encodable_term()}]. -type fields() :: [{field_name(), encodable_term()}].
@ -55,13 +49,13 @@
serialize(Tag, Vsn, Template, Fields) -> serialize(Tag, Vsn, Template, Fields) ->
List = encode_fields([{tag, int}, {vsn, int}|Template], List = encode_fields([{tag, int}, {vsn, int}|Template],
[{tag, Tag}, {vsn, Vsn}|Fields]), [{tag, Tag}, {vsn, Vsn}|Fields]),
gmser_rlp:encode(List). aeser_rlp:encode(List).
%% Type isn't strictly necessary, but will give a better error reason %% Type isn't strictly necessary, but will give a better error reason
-spec deserialize(atom(), non_neg_integer(), non_neg_integer(), -spec deserialize(atom(), non_neg_integer(), non_neg_integer(),
template(), binary()) -> fields(). template(), binary()) -> fields().
deserialize(Type, Tag, Vsn, Template0, Binary) -> deserialize(Type, Tag, Vsn, Template0, Binary) ->
Decoded = gmser_rlp:decode(Binary), Decoded = aeser_rlp:decode(Binary),
Template = [{tag, int}, {vsn, int}|Template0], Template = [{tag, int}, {vsn, int}|Template0],
case decode_fields(Template, Decoded) of case decode_fields(Template, Decoded) of
[{tag, Tag}, {vsn, Vsn}|Left] -> [{tag, Tag}, {vsn, Vsn}|Left] ->
@ -74,7 +68,7 @@ deserialize(Type, Tag, Vsn, Template0, Binary) ->
-spec deserialize_tag_and_vsn(binary()) -> -spec deserialize_tag_and_vsn(binary()) ->
{non_neg_integer(), non_neg_integer(), fields()}. {non_neg_integer(), non_neg_integer(), fields()}.
deserialize_tag_and_vsn(Binary) -> deserialize_tag_and_vsn(Binary) ->
[TagBin, VsnBin|Fields] = gmser_rlp:decode(Binary), [TagBin, VsnBin|Fields] = aeser_rlp:decode(Binary),
Template = [{tag, int}, {vsn, int}], Template = [{tag, int}, {vsn, int}],
[{tag, Tag}, {vsn, Vsn}] = decode_fields(Template, [TagBin, VsnBin]), [{tag, Tag}, {vsn, Vsn}] = decode_fields(Template, [TagBin, VsnBin]),
{Tag, Vsn, Fields}. {Tag, Vsn, Fields}.
@ -124,7 +118,7 @@ encode_field(binary, X) when is_binary(X) -> X;
encode_field(bool, true) -> <<1:8>>; encode_field(bool, true) -> <<1:8>>;
encode_field(bool, false) -> <<0:8>>; encode_field(bool, false) -> <<0:8>>;
encode_field(id, Val) -> encode_field(id, Val) ->
try gmser_id:encode(Val) try aeser_id:encode(Val)
catch _:_ -> error({illegal, id, Val}) catch _:_ -> error({illegal, id, Val})
end; end;
encode_field(Type, Val) -> error({illegal, Type, Val}). encode_field(Type, Val) -> error({illegal, Type, Val}).
@ -148,7 +142,7 @@ decode_field(binary, X) when is_binary(X) -> X;
decode_field(bool, <<1:8>>) -> true; decode_field(bool, <<1:8>>) -> true;
decode_field(bool, <<0:8>>) -> false; decode_field(bool, <<0:8>>) -> false;
decode_field(id, Val) -> decode_field(id, Val) ->
try gmser_id:decode(Val) try aeser_id:decode(Val)
catch _:_ -> error({illegal, id, Val}) catch _:_ -> error({illegal, id, Val})
end; end;
decode_field(Type, X) -> error({illegal, Type, X}). decode_field(Type, X) -> error({illegal, Type, X}).

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +0,0 @@
-module(gmser_dyn_types).
-export([ add_type/3 %% (Tag, Code, Template) -> Types1
, add_type/4 %% (Tag, Code, Template, Types) -> Types1
, from_list/2
, expand/1 ]).
-export([ next_code/1 ]).
next_code(#{codes := Codes}) ->
lists:max(maps:keys(Codes)) + 1.
-spec add_type(Tag, Code, Template) -> Types
when Tag :: gmser_dyn:tag()
, Code :: gmser_dyn:code()
, Template :: gmser_dyn:template()
, Types :: gmser_dyn:types().
add_type(Tag, Code, Template) ->
add_type(Tag, Code, Template, gmser_dyn:registered_types()).
add_type(Tag, Code, Template, Types) ->
elem_to_type({Tag, Code, Template}, Types).
from_list(L, Types) ->
lists:foldl(fun elem_to_type/2, Types, L).
expand(#{vsn := V, templates := Templates0} = Types) ->
Templates =
maps:map(
fun(_, F) when is_function(F, 0) ->
F();
(_, F) when is_function(F, 1) ->
F(V);
(_, T) ->
T
end, Templates0),
Types#{templates := Templates}.
elem_to_type({Tag, Code, Template}, Acc) when is_atom(Tag), is_integer(Code) ->
#{codes := Codes, rev := Rev, templates := Temps} = Acc,
case {is_map_key(Tag, Rev), is_map_key(Code, Codes)} of
{false, false} ->
Acc#{ codes := Codes#{Code => Tag}
, rev := Rev#{Tag => Code}
, templates => Temps#{Tag => Template}
};
{true, _} -> error({duplicate_tag, Tag});
{_, true} -> error({duplicate_code, Code})
end;
elem_to_type({modify, {Tag, Template}}, Acc) ->
#{codes := _, rev := Rev, templates := Templates} = Acc,
_ = maps:get(Tag, Rev),
Templates1 = Templates#{Tag := Template},
Acc#{templates := Templates1};
elem_to_type({labels, Lbls}, Acc) ->
lists:foldl(fun add_label/2, Acc, Lbls);
elem_to_type({vsn, V}, Acc) ->
Acc#{vsn => V};
elem_to_type(Elem, _) ->
error({invalid_type, Elem}).
add_label({L, Code}, #{labels := Lbls, rev_labels := RevLbls} = Acc)
when is_atom(L), is_integer(Code), Code > 0 ->
case {is_map_key(L, Lbls), is_map_key(Code, RevLbls)} of
{false, false} ->
Acc#{labels := Lbls#{L => Code},
rev_labels := RevLbls#{Code => L}};
{true, _} -> error({duplicate_label, L});
{_, true} -> error({duplicate_label_code, Code})
end;
add_label(Elem, _) ->
error({invalid_label, Elem}).

View File

@ -1,13 +1,12 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_api_encoder_tests). -module(aeser_api_encoder_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-define(TEST_MODULE, gmser_api_encoder). -define(TEST_MODULE, aeser_api_encoder).
-define(TYPES, [ {key_block_hash , 32} -define(TYPES, [ {key_block_hash , 32}
, {micro_block_hash , 32} , {micro_block_hash , 32}
, {block_tx_hash , 32} , {block_tx_hash , 32}
@ -16,45 +15,17 @@
, {contract_pubkey , 32} , {contract_pubkey , 32}
, {transaction , not_applicable} , {transaction , not_applicable}
, {tx_hash , 32} , {tx_hash , 32}
, {oracle_pubkey , 32}
, {oracle_query_id , 32}
, {account_pubkey , 32} , {account_pubkey , 32}
, {signature , 64} , {signature , 64}
, {name , not_applicable} , {name , not_applicable}
, {native_token , 32}
, {commitment , 32} , {commitment , 32}
, {peer_pubkey , 32} , {peer_pubkey , 32}
, {hash , 32}
, {state , 32} , {state , 32}
, {poi , not_applicable}]). , {poi , not_applicable}]).
encode_decode_test_() -> encode_decode_test_() ->
encode_decode_test_(?TYPES).
encode_decode_known_types_test_() ->
KnownTypes = known_types(),
SizedTypes = [{T, ?TEST_MODULE:byte_size_for_type(T)} || T <- KnownTypes],
encode_decode_test_(SizedTypes).
prefixes_are_known_types_test() ->
MappedPfxs = mapped_prefixes(),
KnownTypes = known_types(),
lists:foreach(
fun({Pfx, Type}) ->
case lists:member(Type, KnownTypes) of
true -> ok;
false ->
error({not_a_known_type, Pfx, Type})
end
end, MappedPfxs),
lists:foreach(
fun(Type) ->
case lists:keyfind(Type, 2, MappedPfxs) of
{_, _} -> ok;
false ->
error({has_no_mapped_prefix, Type})
end
end, KnownTypes).
encode_decode_test_(Types) ->
[{"Byte sizes are correct", [{"Byte sizes are correct",
fun() -> fun() ->
lists:foreach( lists:foreach(
@ -62,7 +33,7 @@ encode_decode_test_(Types) ->
{_Type, _, ByteSize} = {Type, ByteSize, {_Type, _, ByteSize} = {Type, ByteSize,
?TEST_MODULE:byte_size_for_type(Type)} ?TEST_MODULE:byte_size_for_type(Type)}
end, end,
Types) ?TYPES)
end end
}, },
{"Serialize/deserialize known types", {"Serialize/deserialize known types",
@ -79,7 +50,7 @@ encode_decode_test_(Types) ->
{Type, Key} = ?TEST_MODULE:decode(EncodedKey), {Type, Key} = ?TEST_MODULE:decode(EncodedKey),
{ok, Key} = ?TEST_MODULE:safe_decode(Type, EncodedKey) {ok, Key} = ?TEST_MODULE:safe_decode(Type, EncodedKey)
end, end,
Types) ?TYPES)
end end
}, },
{"Key size check works", {"Key size check works",
@ -87,18 +58,17 @@ encode_decode_test_(Types) ->
lists:foreach( lists:foreach(
fun({_Type, not_applicable}) -> ok; fun({_Type, not_applicable}) -> ok;
({Type, ByteSize}) -> ({Type, ByteSize}) ->
CheckIllegalSize = CheckIlligalSize =
fun(S) -> fun(S) ->
Key = <<42:S/unit:8>>, Key = <<42:S/unit:8>>,
?assertError(incorrect_size, ?TEST_MODULE:encode(Type, Key)), EncodedKey = ?TEST_MODULE:encode(Type, Key),
EncodedKey = ?TEST_MODULE:unsafe_encode(Type, Key), %% no size check
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, EncodedKey) {error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, EncodedKey)
end, end,
CheckIllegalSize(0), CheckIlligalSize(0),
CheckIllegalSize(ByteSize - 1), CheckIlligalSize(ByteSize - 1),
CheckIllegalSize(ByteSize + 1) CheckIlligalSize(ByteSize + 1)
end, end,
Types) ?TYPES)
end end
}, },
{"Missing prefix", {"Missing prefix",
@ -121,7 +91,7 @@ encode_decode_test_(Types) ->
<<_WholePrefix:3/unit:8, RestOfKey2/binary>> = EncodedKey, <<_WholePrefix:3/unit:8, RestOfKey2/binary>> = EncodedKey,
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey2) {error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey2)
end, end,
Types) ?TYPES)
end end
}, },
{"Piece of encoded key", {"Piece of encoded key",
@ -140,7 +110,7 @@ encode_decode_test_(Types) ->
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, HalfKey), {error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, HalfKey),
{error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey) {error, invalid_encoding} = ?TEST_MODULE:safe_decode(Type, RestOfKey)
end, end,
Types) ?TYPES)
end end
}, },
{"Encode/decode binary with only zeros", {"Encode/decode binary with only zeros",
@ -161,58 +131,8 @@ encode_decode_test_(Types) ->
Encoded1 = base58:binary_to_base58(Bin), Encoded1 = base58:binary_to_base58(Bin),
Decoded1 = base58:base58_to_binary(Encoded1), Decoded1 = base58:base58_to_binary(Encoded1),
?assertEqual(Bin, Decoded1) ?assertEqual(Bin, Decoded1)
end, Types) end, ?TYPES)
end, end,
Bins) Bins)
end}, end}
{"Encode/decode keypairs",
fun() ->
KP1 = enacl:sign_keypair(),
Enc1 = ?TEST_MODULE:encode_keypair(KP1),
{ok, KP1} = ?TEST_MODULE:safe_decode_keypair(Enc1),
KP2 = enacl:sign_keypair(),
Enc2 = ?TEST_MODULE:encode_keypair(KP2),
{ok, KP2} = ?TEST_MODULE:safe_decode_keypair(Enc2),
BadEnc = Enc1#{~"priv" => maps:get(~"priv", Enc2)},
{error, illegal_encoding} = ?TEST_MODULE:safe_decode_keypair(BadEnc)
end
},
{"Encode AND decode both 32-byte and 64-byte account_seckey",
fun() ->
%% Originally, we could encode a 64-byte seckey, but decode would fail.
#{public := Pub, secret := Sec} = enacl:sign_keypair(),
<<Seed:32/binary, Pub:32/binary>> = Sec,
EncSeed = ?TEST_MODULE:encode(account_seckey, Seed),
EncSec = ?TEST_MODULE:encode(account_seckey, Sec),
{ok, Seed} = ?TEST_MODULE:safe_decode(account_seckey, EncSeed),
{ok, Sec} = ?TEST_MODULE:safe_decode(account_seckey, EncSec)
end
}
]. ].
known_types() ->
Forms = get_forms(),
[{type, _, union, Types}] =
[Def || {attribute, _, type, {known_type, Def, []}} <- Forms],
[Name || {atom,_, Name} <- Types].
mapped_prefixes() ->
Forms = get_forms(),
[Clauses] = [Cs || {function,_,pfx2type,1,Cs} <- Forms],
Abst = [{B, A} || {clause,_,[B],[],[A]} <- Clauses],
lists:map(
fun({B, A}) ->
{eval_expr(B), eval_expr(A)}
end, Abst).
get_forms() ->
get_forms(code:which(?TEST_MODULE)).
get_forms(Beam) ->
{ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} =
beam_lib:chunks(Beam, [abstract_code]),
Forms.
eval_expr(Expr) ->
{value, Val, []} = erl_eval:expr(Expr, []),
Val.

View File

@ -1,12 +1,11 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% EUnit tests for gmser_chain_objects %%% EUnit tests for aeser_chain_objects
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_chain_objects_tests). -module(aeser_chain_objects_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -79,10 +78,10 @@ deserialize(Template, Bin) ->
deserialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION). deserialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION).
deserialize(Template, Bin, Tag, Vsn) -> deserialize(Template, Bin, Tag, Vsn) ->
gmser_chain_objects:deserialize(Tag, Vsn, Template, Bin). aeser_chain_objects:deserialize(Tag, Vsn, Template, Bin).
serialize(Template, Bin) -> serialize(Template, Bin) ->
serialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION). serialize(Template, Bin, ?DEFAULT_TAG, ?DEFAULT_VERSION).
serialize(Template, Bin, Tag, Vsn) -> serialize(Template, Bin, Tag, Vsn) ->
gmser_chain_objects:serialize(Tag, Vsn, Template, Bin). aeser_chain_objects:serialize(Tag, Vsn, Template, Bin).

View File

@ -1,11 +1,7 @@
%%%------------------------------------------------------------------- -module(aeser_contract_code_tests).
%%% @copyright (C) 2025, QPQ AG
%%%-------------------------------------------------------------------
-module(gmser_contract_code_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include("gmser_contract_code.hrl"). -include("aeser_contract_code.hrl").
-define(DUMMY_CODE_MAP_1, -define(DUMMY_CODE_MAP_1,
#{ byte_code => <<"DUMMY CODE">> #{ byte_code => <<"DUMMY CODE">>
@ -26,14 +22,14 @@
, payable => true} ). , payable => true} ).
vsn_1_test() -> vsn_1_test() ->
gmser_contract_code:deserialize( aeser_contract_code:deserialize(
gmser_contract_code:serialize(?DUMMY_CODE_MAP_1, ?SOPHIA_CONTRACT_VSN_1)). aeser_contract_code:serialize(?DUMMY_CODE_MAP_1, ?SOPHIA_CONTRACT_VSN_1)).
vsn_2_test() -> vsn_2_test() ->
gmser_contract_code:deserialize( aeser_contract_code:deserialize(
gmser_contract_code:serialize(?DUMMY_CODE_MAP_2, ?SOPHIA_CONTRACT_VSN_2)). aeser_contract_code:serialize(?DUMMY_CODE_MAP_2, ?SOPHIA_CONTRACT_VSN_2)).
vsn_3_test() -> vsn_3_test() ->
gmser_contract_code:deserialize( aeser_contract_code:deserialize(
gmser_contract_code:serialize(?DUMMY_CODE_MAP_3, ?SOPHIA_CONTRACT_VSN_3)). aeser_contract_code:serialize(?DUMMY_CODE_MAP_3, ?SOPHIA_CONTRACT_VSN_3)).

View File

@ -0,0 +1,67 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2023, Aeternity Anstalt
%%%-------------------------------------------------------------------
-module(aeser_delegation_tests).
-include_lib("eunit/include/eunit.hrl").
-define(TEST_MODULE, aeser_delegation).
-define(ACCOUNT, aeser_id:create(account, <<1:256>>)).
-define(CONTRACT, aeser_id:create(contract, <<2:256>>)).
-define(NAME, aeser_id:create(name, <<3:256>>)).
-define(ORACLE, aeser_id:create(oracle, <<3:256>>)).
-define(NETWORK_ID, <<"my_fancy_network"/utf8>>).
encode_correct_test_() ->
[{"Encode preclaim sig",
fun() ->
aeser_delegation:aens_preclaim_sig(?NETWORK_ID, ?ACCOUNT, ?CONTRACT)
end},
{"Encode name sig",
fun() ->
aeser_delegation:aens_name_sig(?NETWORK_ID, ?ACCOUNT, ?NAME, ?CONTRACT)
end},
{"Encode aens wildcard sig",
fun() ->
aeser_delegation:aens_sig(?NETWORK_ID, ?ACCOUNT, ?CONTRACT)
end},
{"Encode oracle sig",
fun() ->
aeser_delegation:oracle_sig(?NETWORK_ID, ?ACCOUNT, ?CONTRACT)
end},
{"Encode oracle response sig",
fun() ->
aeser_delegation:oracle_response_sig(?NETWORK_ID, ?ORACLE, ?CONTRACT)
end}
].
encode_fail_test_() ->
[{"Bad encoding preclaim sig",
fun() ->
?assertError(_, aeser_delegation:aens_preclaim_sig(?NETWORK_ID, <<42:256>>, ?CONTRACT)),
?assertError(_, aeser_delegation:aens_preclaim_sig(?NETWORK_ID, ?CONTRACT, ?ACCOUNT))
end},
{"Bad encoding name sig",
fun() ->
?assertError(_, aeser_delegation:aens_name_sig(?NETWORK_ID, ?ACCOUNT, <<42:256>>, ?CONTRACT)),
?assertError(_, aeser_delegation:aens_name_sig(?NETWORK_ID, ?NAME, ?ACCOUNT, ?CONTRACT))
end},
{"Bad encoding aens wildcard sig",
fun() ->
?assertError(_, aeser_delegation:aens_sig(?NETWORK_ID, ?ACCOUNT, <<42:256>>)),
?assertError(_, aeser_delegation:aens_sig(?NETWORK_ID, ?CONTRACT, ?CONTRACT))
end},
{"Bad encoding oracle sig",
fun() ->
?assertError(_, aeser_delegation:oracle_sig(?NETWORK_ID, <<42:256>>, ?CONTRACT)),
?assertError(_, aeser_delegation:oracle_sig(?NETWORK_ID, ?ACCOUNT, ?ACCOUNT))
end},
{"Bad encoding oracle response sig",
fun() ->
?assertError(_, aeser_delegation:oracle_response_sig(?NETWORK_ID, <<42:256>>, ?CONTRACT)),
?assertError(_, aeser_delegation:oracle_response_sig(?NETWORK_ID, ?ORACLE, ?ORACLE))
end}
].

View File

@ -1,11 +1,10 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc Tests for Recursive Length Prefix %%% @doc Tests for Recursive Length Prefix
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(gmser_rlp_tests). -module(aeser_rlp_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -14,7 +13,7 @@
-define(BYTE_ARRAY_OFFSET , 128). -define(BYTE_ARRAY_OFFSET , 128).
-define(LIST_OFFSET , 192). -define(LIST_OFFSET , 192).
-define(TEST_MODULE, gmser_rlp). -define(TEST_MODULE, aeser_rlp).
rlp_one_byte_test() -> rlp_one_byte_test() ->
B = <<42>>, B = <<42>>,

View File

@ -1,49 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2023, Aeternity Anstalt
%%%-------------------------------------------------------------------
-module(gmser_delegation_tests).
-include_lib("eunit/include/eunit.hrl").
-define(TEST_MODULE, gmser_delegation).
-define(ACCOUNT, gmser_id:create(account, <<1:256>>)).
-define(CONTRACT, gmser_id:create(contract, <<2:256>>)).
-define(NAME, gmser_id:create(name, <<3:256>>)).
-define(NETWORK_ID, <<"my_fancy_network"/utf8>>).
encode_correct_test_() ->
[{"Encode preclaim sig",
fun() ->
gmser_delegation:aens_preclaim_sig(?NETWORK_ID, ?ACCOUNT, ?CONTRACT)
end},
{"Encode name sig",
fun() ->
gmser_delegation:aens_name_sig(?NETWORK_ID, ?ACCOUNT, ?NAME, ?CONTRACT)
end},
{"Encode aens wildcard sig",
fun() ->
gmser_delegation:aens_sig(?NETWORK_ID, ?ACCOUNT, ?CONTRACT)
end}
].
encode_fail_test_() ->
[{"Bad encoding preclaim sig",
fun() ->
?assertError(_, gmser_delegation:aens_preclaim_sig(?NETWORK_ID, <<42:256>>, ?CONTRACT)),
?assertError(_, gmser_delegation:aens_preclaim_sig(?NETWORK_ID, ?CONTRACT, ?ACCOUNT))
end},
{"Bad encoding name sig",
fun() ->
?assertError(_, gmser_delegation:aens_name_sig(?NETWORK_ID, ?ACCOUNT, <<42:256>>, ?CONTRACT)),
?assertError(_, gmser_delegation:aens_name_sig(?NETWORK_ID, ?NAME, ?ACCOUNT, ?CONTRACT))
end},
{"Bad encoding aens wildcard sig",
fun() ->
?assertError(_, gmser_delegation:aens_sig(?NETWORK_ID, ?ACCOUNT, <<42:256>>)),
?assertError(_, gmser_delegation:aens_sig(?NETWORK_ID, ?CONTRACT, ?CONTRACT))
end}
].

View File

@ -1,17 +0,0 @@
{name,"Gajumaru Serialization"}.
{type,lib}.
{modules,[]}.
{prefix,none}.
{author,"Hans Svensson"}.
{desc,"Serialization helpers for the Gajumaru."}.
{package_id,{"otpr","gmserialization",{0,1,2}}}.
{deps,[{"otpr","eblake2",{1,0,1}},{"otpr","base58",{0,1,1}}]}.
{key_name,none}.
{a_email,[]}.
{c_email,[]}.
{copyright,"QPQ AG"}.
{file_exts,[]}.
{license,skip}.
{repo_url,"https://git.qpq.swiss/QPQ-AG/gmserialization"}.
{tags,["blockchain","crypto","gm","gajumaru"]}.
{ws_url,[]}.