Compare commits
3 Commits
master
...
add-subnam
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81d0daa9d5 | ||
|
|
7994428e56 | ||
|
|
91b83cfb89 |
37
.circleci/config.yml
Normal file
37
.circleci/config.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
executors:
|
||||||
|
aebuilder:
|
||||||
|
docker:
|
||||||
|
- image: aeternity/builder
|
||||||
|
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
|
||||||
@ -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
2
.gitignore
vendored
@ -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
|
||||||
|
|||||||
1
LICENSE
1
LICENSE
@ -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
268
README.md
@ -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}`.)
|
|
||||||
|
|||||||
@ -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.
|
|
||||||
@ -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,[]}]}.
|
|
||||||
19
rebar.config
19
rebar.config
@ -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, "26180f4"}}}
|
||||||
{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]}
|
|
||||||
]}.
|
|
||||||
|
|||||||
16
rebar.lock
16
rebar.lock
@ -1,16 +0,0 @@
|
|||||||
{"1.2.0",
|
|
||||||
[{<<"base58">>,
|
|
||||||
{git,"https://git.qpq.swiss/QPQ-AG/erl-base58.git",
|
|
||||||
{ref,"e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}},
|
|
||||||
0},
|
|
||||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
|
||||||
{<<"enacl">>,
|
|
||||||
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git",
|
|
||||||
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
|
|
||||||
0}]}.
|
|
||||||
[
|
|
||||||
{pkg_hash,[
|
|
||||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>}]},
|
|
||||||
{pkg_hash_ext,[
|
|
||||||
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>}]}
|
|
||||||
].
|
|
||||||
@ -1,55 +1,44 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
%%% @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
|
|
||||||
| associate_chain
|
|
||||||
| entry
|
|
||||||
| signature
|
| signature
|
||||||
| name
|
| name
|
||||||
| native_token
|
| subname
|
||||||
| commitment
|
| commitment
|
||||||
| peer_pubkey
|
| peer_pubkey
|
||||||
| hash
|
|
||||||
| state
|
| state
|
||||||
| poi
|
| poi
|
||||||
| state_trees
|
| state_trees
|
||||||
| call_state_tree
|
| call_state_tree
|
||||||
| mp_tree_hash
|
|
||||||
| bytearray.
|
| bytearray.
|
||||||
|
|
||||||
-type extended_type() :: known_type() | block_hash | {id_hash, [known_type()]}.
|
-type extended_type() :: known_type() | block_hash | {id_hash, [known_type()]}.
|
||||||
@ -58,66 +47,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 +62,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 +77,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 +88,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 ->
|
||||||
@ -232,55 +161,46 @@ sha256_hash(Bin) ->
|
|||||||
crypto:hash(sha256, Bin).
|
crypto:hash(sha256, Bin).
|
||||||
|
|
||||||
|
|
||||||
id2type(account) -> account_pubkey;
|
id2type(account) -> account_pubkey;
|
||||||
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(name) -> name;
|
||||||
id2type(contract_source) -> contract_source;
|
id2type(oracle) -> oracle_pubkey.
|
||||||
id2type(name) -> name;
|
|
||||||
id2type(native_token) -> native_token;
|
|
||||||
id2type(entry) -> entry.
|
|
||||||
|
|
||||||
type2id(account_pubkey) -> account;
|
type2id(account_pubkey) -> account;
|
||||||
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(associate_chain) -> ?BASE58;
|
|
||||||
type2enc(signature) -> ?BASE58;
|
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(subname) -> ?BASE64;
|
||||||
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(hash) -> ?BASE58;
|
|
||||||
type2enc(entry) -> ?BASE64;
|
|
||||||
type2enc(bytearray) -> ?BASE64.
|
type2enc(bytearray) -> ?BASE64.
|
||||||
|
|
||||||
|
|
||||||
@ -289,30 +209,27 @@ 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(associate_chain) -> <<"ac">>;
|
|
||||||
type2pfx(signature) -> <<"sg">>;
|
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(subname) -> <<"sn">>;
|
||||||
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(hash) -> <<"hs">>;
|
|
||||||
type2pfx(entry) -> <<"en">>;
|
|
||||||
type2pfx(bytearray) -> <<"ba">>.
|
type2pfx(bytearray) -> <<"ba">>.
|
||||||
|
|
||||||
pfx2type(<<"kh">>) -> key_block_hash;
|
pfx2type(<<"kh">>) -> key_block_hash;
|
||||||
@ -320,30 +237,27 @@ 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(<<"ac">>) -> associate_chain;
|
|
||||||
pfx2type(<<"sg">>) -> signature;
|
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(<<"sn">>) -> subname;
|
||||||
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(<<"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,30 +267,27 @@ 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(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(subname) -> not_applicable;
|
||||||
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;
|
||||||
byte_size_for_type(poi) -> not_applicable;
|
byte_size_for_type(poi) -> not_applicable;
|
||||||
byte_size_for_type(state_trees) -> not_applicable;
|
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(hash) -> 32;
|
|
||||||
byte_size_for_type(entry) -> not_applicable;
|
|
||||||
byte_size_for_type(bytearray) -> not_applicable.
|
byte_size_for_type(bytearray) -> not_applicable.
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
||||||
@ -49,15 +52,13 @@ tag(name_preclaim_tx) -> 33;
|
|||||||
tag(name_update_tx) -> 34;
|
tag(name_update_tx) -> 34;
|
||||||
tag(name_revoke_tx) -> 35;
|
tag(name_revoke_tx) -> 35;
|
||||||
tag(name_transfer_tx) -> 36;
|
tag(name_transfer_tx) -> 36;
|
||||||
tag(name_auction) -> 37;
|
tag(subname) -> 38;
|
||||||
|
tag(subname_tx) -> 39;
|
||||||
tag(contract) -> 40;
|
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_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,43 +85,25 @@ 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(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;
|
||||||
tag(paying_for_tx) -> 82;
|
|
||||||
tag(ga_meta_tx_auth_data) -> 810;
|
|
||||||
tag(associate_chain) -> 90;
|
|
||||||
tag(ac_state) -> 91;
|
|
||||||
tag(ac_burn) -> 92;
|
|
||||||
tag(ac_create_tx) -> 93;
|
|
||||||
tag(ac_deposit_tx) -> 94;
|
|
||||||
tag(ac_update_cops_tx) -> 95;
|
|
||||||
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;
|
||||||
@ -128,15 +111,13 @@ rev_tag(33) -> name_preclaim_tx;
|
|||||||
rev_tag(34) -> name_update_tx;
|
rev_tag(34) -> name_update_tx;
|
||||||
rev_tag(35) -> name_revoke_tx;
|
rev_tag(35) -> name_revoke_tx;
|
||||||
rev_tag(36) -> name_transfer_tx;
|
rev_tag(36) -> name_transfer_tx;
|
||||||
rev_tag(37) -> name_auction;
|
rev_tag(38) -> subname;
|
||||||
|
rev_tag(39) -> subname_tx;
|
||||||
rev_tag(40) -> contract;
|
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(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,35 +144,12 @@ 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(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;
|
||||||
rev_tag(82) -> paying_for_tx;
|
|
||||||
rev_tag(810) -> ga_meta_tx_auth_data;
|
|
||||||
rev_tag(90) -> associate_chain;
|
|
||||||
rev_tag(91) -> ac_state;
|
|
||||||
rev_tag(92) -> ac_burn;
|
|
||||||
rev_tag(93) -> ac_create_tx;
|
|
||||||
rev_tag(94) -> ac_deposit_tx;
|
|
||||||
rev_tag(95) -> ac_update_cops_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.
|
||||||
@ -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,16 @@
|
|||||||
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))
|
{ok, SHash} = enacl:generichash(32, list_to_binary(SrcStr)),
|
||||||
end,
|
SHash
|
||||||
|
end,
|
||||||
|
|
||||||
%% Compiler version
|
%% Compiler version
|
||||||
Version = maps:get(compiler_version, CodeMap, <<"unknown">>),
|
Version = maps:get(compiler_version, CodeMap, <<"unknown">>),
|
||||||
@ -45,35 +43,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 +69,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 +84,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
|
||||||
@ -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,10 @@
|
|||||||
, val
|
, val
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type tag() :: 'account'
|
-type tag() :: 'account' | 'oracle' | 'name' | 'subname'
|
||||||
| 'associate_chain'
|
| 'commitment' | 'contract' | 'channel'.
|
||||||
| 'channel'
|
|
||||||
| '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
|
||||||
@ -50,14 +37,15 @@
|
|||||||
-define(PUB_SIZE, 32).
|
-define(PUB_SIZE, 32).
|
||||||
-define(TAG_SIZE, 1).
|
-define(TAG_SIZE, 1).
|
||||||
-define(SERIALIZED_SIZE, 33). %% ?TAG_SIZE + ?PUB_SIZE
|
-define(SERIALIZED_SIZE, 33). %% ?TAG_SIZE + ?PUB_SIZE
|
||||||
|
-define(SUBNAME_SERIALIZED_SIZE, 65). %% ?TAG_SIZE + ?PUB_SIZE + ?PUB_SIZE
|
||||||
|
|
||||||
-define(IS_TAG(___TAG___), ___TAG___ =:= account;
|
-define(IS_TAG(___TAG___), ___TAG___ =:= account;
|
||||||
|
___TAG___ =:= oracle;
|
||||||
___TAG___ =:= name;
|
___TAG___ =:= name;
|
||||||
|
___TAG___ =:= subname;
|
||||||
___TAG___ =:= commitment;
|
___TAG___ =:= commitment;
|
||||||
___TAG___ =:= contract;
|
___TAG___ =:= contract;
|
||||||
___TAG___ =:= channel;
|
___TAG___ =:= channel
|
||||||
___TAG___ =:= associate_chain;
|
|
||||||
___TAG___ =:= entry
|
|
||||||
).
|
).
|
||||||
-define(IS_VAL(___VAL___), byte_size(___VAL___) =:= 32).
|
-define(IS_VAL(___VAL___), byte_size(___VAL___) =:= 32).
|
||||||
|
|
||||||
@ -96,37 +84,40 @@ is_id(_) -> false.
|
|||||||
-spec encode(id()) -> binary().
|
-spec encode(id()) -> binary().
|
||||||
encode(#id{tag = Tag, val = Val}) ->
|
encode(#id{tag = Tag, val = Val}) ->
|
||||||
Res = <<(encode_tag(Tag)):?TAG_SIZE/unit:8, Val/binary>>,
|
Res = <<(encode_tag(Tag)):?TAG_SIZE/unit:8, Val/binary>>,
|
||||||
true = ?SERIALIZED_SIZE =:= byte_size(Res),
|
true = serialized_size(Tag) =:= byte_size(Res),
|
||||||
Res.
|
Res.
|
||||||
|
|
||||||
-spec decode(binary()) -> id().
|
-spec decode(binary()) -> id().
|
||||||
decode(<<Tag:?TAG_SIZE/unit:8, Val:?PUB_SIZE/binary>>) ->
|
decode(<<Tag:?TAG_SIZE/unit:8, Val:?PUB_SIZE/binary>>) ->
|
||||||
#id{ tag = decode_tag(Tag)
|
#id{ tag = decode_tag(Tag)
|
||||||
|
, val = Val};
|
||||||
|
decode(<<SubnameTag:?TAG_SIZE/unit:8, Val:(?PUB_SIZE+?PUB_SIZE)/binary>>) ->
|
||||||
|
#id{ tag = subname = decode_tag(SubnameTag)
|
||||||
, val = Val}.
|
, val = Val}.
|
||||||
|
|
||||||
-spec t_id(any()) -> id().
|
|
||||||
t_id(#id{} = Id) -> Id.
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
encode_tag(account) -> 1;
|
serialized_size(subname) -> ?SUBNAME_SERIALIZED_SIZE;
|
||||||
encode_tag(name) -> 2;
|
serialized_size(_) -> ?SERIALIZED_SIZE.
|
||||||
encode_tag(commitment) -> 3;
|
|
||||||
encode_tag(contract) -> 5;
|
|
||||||
encode_tag(channel) -> 6;
|
encode_tag(account) -> 1;
|
||||||
encode_tag(associate_chain) -> 7;
|
encode_tag(name) -> 2;
|
||||||
encode_tag(native_token) -> 8;
|
encode_tag(commitment) -> 3;
|
||||||
encode_tag(entry) -> 9;
|
encode_tag(oracle) -> 4;
|
||||||
encode_tag(Other) -> error({illegal_id_tag_name, Other}).
|
encode_tag(contract) -> 5;
|
||||||
|
encode_tag(channel) -> 6;
|
||||||
|
encode_tag(subname) -> 7;
|
||||||
|
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) -> subname;
|
||||||
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}).
|
||||||
@ -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
|
||||||
@ -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,
|
||||||
@ -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'
|
||||||
@ -34,16 +28,14 @@
|
|||||||
| 'binary'
|
| 'binary'
|
||||||
| 'id' %% As defined in aec_id.erl
|
| 'id' %% As defined in aec_id.erl
|
||||||
| [type()] %% Length one in the type. This means a list of any length.
|
| [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.
|
| tuple(). %% Any arity, containing type(). This means a static size array.
|
||||||
|
|
||||||
-type encodable_term() :: non_neg_integer()
|
-type encodable_term() :: non_neg_integer()
|
||||||
| binary()
|
| binary()
|
||||||
| boolean()
|
| boolean()
|
||||||
| [encodable_term()] %% Of any length
|
| [encodable_term()] %% Of any length
|
||||||
| #{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 +47,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 +66,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}.
|
||||||
@ -109,12 +101,6 @@ decode_fields(Template, Values) ->
|
|||||||
|
|
||||||
encode_field([Type], L) when is_list(L) ->
|
encode_field([Type], L) when is_list(L) ->
|
||||||
[encode_field(Type, X) || X <- L];
|
[encode_field(Type, X) || X <- L];
|
||||||
encode_field(#{items := Items}, Map) ->
|
|
||||||
lists:map(
|
|
||||||
fun({K, Type}) ->
|
|
||||||
V = maps:get(K, Map),
|
|
||||||
encode_field(Type, V)
|
|
||||||
end, Items);
|
|
||||||
encode_field(Type, T) when tuple_size(Type) =:= tuple_size(T) ->
|
encode_field(Type, T) when tuple_size(Type) =:= tuple_size(T) ->
|
||||||
Zipped = lists:zip(tuple_to_list(Type), tuple_to_list(T)),
|
Zipped = lists:zip(tuple_to_list(Type), tuple_to_list(T)),
|
||||||
[encode_field(X, Y) || {X, Y} <- Zipped];
|
[encode_field(X, Y) || {X, Y} <- Zipped];
|
||||||
@ -124,20 +110,13 @@ 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}).
|
||||||
|
|
||||||
decode_field([Type], List) when is_list(List) ->
|
decode_field([Type], List) when is_list(List) ->
|
||||||
[decode_field(Type, X) || X <- List];
|
[decode_field(Type, X) || X <- List];
|
||||||
decode_field(#{items := Items}, List) when length(List) =:= length(Items) ->
|
|
||||||
Zipped = lists:zip(Items, List),
|
|
||||||
lists:foldl(
|
|
||||||
fun({{K, Type}, V}, Map) ->
|
|
||||||
maps:is_key(K, Map) andalso error(badarg, duplicate_field),
|
|
||||||
Map#{ K => decode_field(Type, V) }
|
|
||||||
end, #{}, Zipped);
|
|
||||||
decode_field(Type, List) when length(List) =:= tuple_size(Type) ->
|
decode_field(Type, List) when length(List) =:= tuple_size(Type) ->
|
||||||
Zipped = lists:zip(tuple_to_list(Type), List),
|
Zipped = lists:zip(tuple_to_list(Type), List),
|
||||||
list_to_tuple([decode_field(X, Y) || {X, Y} <- Zipped]);
|
list_to_tuple([decode_field(X, Y) || {X, Y} <- Zipped]);
|
||||||
@ -148,7 +127,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}).
|
||||||
@ -1,65 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @copyright (C) 2025, QPQ AG
|
|
||||||
%%% @copyright (C) 2023, Aeternity Anstalt
|
|
||||||
%%% @doc
|
|
||||||
%%% Serialization of delegation signatures
|
|
||||||
%%% @end
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(gmser_delegation).
|
|
||||||
-vsn("0.1.2").
|
|
||||||
|
|
||||||
-export([ aens_preclaim_sig/3
|
|
||||||
, aens_name_sig/4
|
|
||||||
, aens_sig/3
|
|
||||||
]).
|
|
||||||
|
|
||||||
%% Delegation signatures are prefixed with a unique tag, ensuring not to
|
|
||||||
%% collide with serialized transactions.
|
|
||||||
-define(DELEGATION_TAG, 16#1a01).
|
|
||||||
|
|
||||||
-define(TYPE_AENS, 1).
|
|
||||||
-define(TYPE_AENS_NAME, 2).
|
|
||||||
-define(TYPE_AENS_PRECLAIM, 3).
|
|
||||||
-define(TYPE_ORACLE, 4).
|
|
||||||
-define(TYPE_ORACLE_RESPONSE, 5).
|
|
||||||
|
|
||||||
-define(VSN, 1).
|
|
||||||
|
|
||||||
-type sig_data() :: binary().
|
|
||||||
|
|
||||||
-spec aens_preclaim_sig(binary(), gmser_id:id(), gmser_id:id()) -> sig_data().
|
|
||||||
aens_preclaim_sig(NetworkId, Account, Contract) ->
|
|
||||||
assert_id(account, Account),
|
|
||||||
assert_id(contract, Contract),
|
|
||||||
Template = [{account, id}, {contract, id}],
|
|
||||||
Fields = [{account, Account}, {contract, Contract}],
|
|
||||||
serialize(?TYPE_AENS_PRECLAIM, NetworkId, Template, Fields).
|
|
||||||
|
|
||||||
-spec aens_name_sig(binary(), gmser_id:id(), gmser_id:id(), gmser_id:id()) -> sig_data().
|
|
||||||
aens_name_sig(NetworkId, Account, Name, Contract) ->
|
|
||||||
assert_id(account, Account),
|
|
||||||
assert_id(name, Name),
|
|
||||||
assert_id(contract, Contract),
|
|
||||||
Template = [{account, id}, {name, id}, {contract, id}],
|
|
||||||
Fields = [{account, Account}, {name, Name}, {contract, Contract}],
|
|
||||||
serialize(?TYPE_AENS_NAME, NetworkId, Template, Fields).
|
|
||||||
|
|
||||||
-spec aens_sig(binary(), gmser_id:id(), gmser_id:id()) -> sig_data().
|
|
||||||
aens_sig(NetworkId, Account, Contract) ->
|
|
||||||
assert_id(account, Account),
|
|
||||||
assert_id(contract, Contract),
|
|
||||||
Template = [{account, id}, {contract, id}],
|
|
||||||
Fields = [{account, Account}, {contract, Contract}],
|
|
||||||
serialize(?TYPE_AENS, NetworkId, Template, Fields).
|
|
||||||
|
|
||||||
|
|
||||||
%% ------------------------------------------------------------------------
|
|
||||||
%% -- Internal functions
|
|
||||||
%% ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
serialize(Type, NetworkId, Template, Fields) ->
|
|
||||||
Data = gmserialization:serialize(Type, ?VSN, Template, Fields),
|
|
||||||
<<?DELEGATION_TAG:16, NetworkId/binary, Data/binary>>.
|
|
||||||
|
|
||||||
assert_id(Type, AeserId) ->
|
|
||||||
Type = gmser_id:specialize_type(AeserId).
|
|
||||||
1317
src/gmser_dyn.erl
1317
src/gmser_dyn.erl
File diff suppressed because it is too large
Load Diff
@ -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}).
|
|
||||||
@ -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.
|
|
||||||
@ -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).
|
||||||
@ -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)).
|
||||||
|
|
||||||
@ -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>>,
|
||||||
@ -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}
|
|
||||||
].
|
|
||||||
17
zomp.meta
17
zomp.meta
@ -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,[]}.
|
|
||||||
Loading…
x
Reference in New Issue
Block a user