Remove old type references, update naming, add license notices (#43)
Gajumaru Serialization Tests / tests (push) Successful in -4m21s
Gajumaru Serialization Tests / tests (push) Successful in -4m21s
Removed the oracle type references and updated the dependency list to point to git.qpq.swiss. Reviewed-on: #43 Co-authored-by: Craig Everett <zxq9@zxq9.com> Co-committed-by: Craig Everett <zxq9@zxq9.com>
This commit was merged in pull request #43.
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% API encoding for the Gajumaru
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(gmser_api_encoder).
|
||||
|
||||
-export([encode/2,
|
||||
decode/1,
|
||||
safe_decode/2,
|
||||
byte_size_for_type/1]).
|
||||
|
||||
-export_type([encoded/0]).
|
||||
|
||||
-type known_type() :: key_block_hash
|
||||
| micro_block_hash
|
||||
| block_pof_hash
|
||||
| block_tx_hash
|
||||
| block_state_hash
|
||||
| block_witness_hash
|
||||
| channel
|
||||
| contract_bytearray
|
||||
| contract_pubkey
|
||||
| contract_store_key
|
||||
| contract_store_value
|
||||
| contract_source
|
||||
| transaction
|
||||
| tx_hash
|
||||
| account_pubkey
|
||||
| account_seckey
|
||||
| associate_chain
|
||||
| entry
|
||||
| signature
|
||||
| name
|
||||
| native_token
|
||||
| commitment
|
||||
| peer_pubkey
|
||||
| state
|
||||
| poi
|
||||
| state_trees
|
||||
| call_state_tree
|
||||
| mp_tree_hash
|
||||
| bytearray.
|
||||
|
||||
-type extended_type() :: known_type() | block_hash | {id_hash, [known_type()]}.
|
||||
|
||||
|
||||
-type payload() :: binary().
|
||||
-type encoded() :: binary().
|
||||
|
||||
-define(BASE58, 1).
|
||||
-define(BASE64, 2).
|
||||
|
||||
-spec encode(known_type(), payload() | gmser_id:id()) -> encoded().
|
||||
encode(id_hash, Payload) ->
|
||||
{IdType, Val} = gmser_id:specialize(Payload),
|
||||
encode(id2type(IdType), Val);
|
||||
encode(Type, Payload) ->
|
||||
Pfx = type2pfx(Type),
|
||||
Enc = case type2enc(Type) of
|
||||
?BASE58 -> base58_check(Payload);
|
||||
?BASE64 -> base64_check(Payload)
|
||||
end,
|
||||
<<Pfx/binary, "_", Enc/binary>>.
|
||||
|
||||
-spec decode(binary()) -> {known_type(), payload()}.
|
||||
decode(Bin0) ->
|
||||
case split(Bin0) of
|
||||
[Pfx, Payload] ->
|
||||
Type = pfx2type(Pfx),
|
||||
Bin = decode_check(Type, Payload),
|
||||
case type_size_check(Type, Bin) of
|
||||
ok -> {Type, Bin};
|
||||
{error, Reason} -> erlang:error(Reason)
|
||||
end;
|
||||
_ ->
|
||||
%% {<<>>, decode_check(Bin)}
|
||||
erlang:error(missing_prefix)
|
||||
end.
|
||||
|
||||
type_size_check(Type, Bin) ->
|
||||
case byte_size_for_type(Type) of
|
||||
not_applicable -> ok;
|
||||
CorrectSize ->
|
||||
Size = byte_size(Bin),
|
||||
case Size =:= CorrectSize of
|
||||
true -> ok;
|
||||
false -> {error, incorrect_size}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec safe_decode(extended_type(), encoded()) -> {'ok', payload() | gmser_id:id()}
|
||||
| {'error', atom()}.
|
||||
safe_decode({id_hash, AllowedTypes}, Enc) ->
|
||||
try decode(Enc) of
|
||||
{ActualType, Dec} ->
|
||||
case lists:member(ActualType, AllowedTypes) of
|
||||
true ->
|
||||
try {ok, gmser_id:create(type2id(ActualType), Dec)}
|
||||
catch error:_ -> {error, invalid_prefix}
|
||||
end;
|
||||
false ->
|
||||
{error, invalid_prefix}
|
||||
end
|
||||
catch
|
||||
error:_ ->
|
||||
{error, invalid_encoding}
|
||||
end;
|
||||
safe_decode(block_hash, Enc) ->
|
||||
try decode(Enc) of
|
||||
{key_block_hash, Dec} ->
|
||||
{ok, Dec};
|
||||
{micro_block_hash, Dec} ->
|
||||
{ok, Dec};
|
||||
{_, _} ->
|
||||
{error, invalid_prefix}
|
||||
catch
|
||||
error:_ ->
|
||||
{error, invalid_encoding}
|
||||
end;
|
||||
safe_decode(Type, Enc) ->
|
||||
try decode(Enc) of
|
||||
{Type, Dec} ->
|
||||
{ok, Dec};
|
||||
{_, _} ->
|
||||
{error, invalid_prefix}
|
||||
catch
|
||||
error:_ ->
|
||||
{error, invalid_encoding}
|
||||
end.
|
||||
|
||||
decode_check(Type, Bin) ->
|
||||
Dec =
|
||||
case type2enc(Type) of
|
||||
?BASE58 -> base58_to_binary(Bin);
|
||||
?BASE64 -> base64_to_binary(Bin)
|
||||
end,
|
||||
Sz = byte_size(Dec),
|
||||
BSz = Sz - 4,
|
||||
<<Body:BSz/binary, C:4/binary>> = Dec,
|
||||
C = check_str(Body),
|
||||
Body.
|
||||
|
||||
base64_check(Bin) ->
|
||||
C = check_str(Bin),
|
||||
binary_to_base64(iolist_to_binary([Bin, C])).
|
||||
|
||||
%% modified from github.com/mbrix/lib_hd
|
||||
base58_check(Bin) ->
|
||||
C = check_str(Bin),
|
||||
binary_to_base58(iolist_to_binary([Bin, C])).
|
||||
|
||||
split(Bin) ->
|
||||
binary:split(Bin, [<<"_">>], []).
|
||||
|
||||
check_str(Bin) ->
|
||||
<<C:32/bitstring,_/binary>> =
|
||||
sha256_hash(sha256_hash(Bin)),
|
||||
C.
|
||||
|
||||
sha256_hash(Bin) ->
|
||||
crypto:hash(sha256, Bin).
|
||||
|
||||
|
||||
id2type(account) -> account_pubkey;
|
||||
id2type(associate_chain) -> associate_chain;
|
||||
id2type(channel) -> channel;
|
||||
id2type(commitment) -> commitment;
|
||||
id2type(contract) -> contract_pubkey;
|
||||
id2type(contract_source) -> contract_source;
|
||||
id2type(name) -> name;
|
||||
id2type(native_token) -> native_token;
|
||||
id2type(entry) -> entry.
|
||||
|
||||
type2id(account_pubkey) -> account;
|
||||
type2id(associate_chain) -> associate_chain;
|
||||
type2id(channel) -> channel;
|
||||
type2id(commitment) -> commitment;
|
||||
type2id(contract_pubkey) -> contract;
|
||||
type2id(contract_source) -> contract_source;
|
||||
type2id(name) -> name;
|
||||
type2id(native_token) -> native_token;
|
||||
type2id(entry) -> entry.
|
||||
|
||||
type2enc(key_block_hash) -> ?BASE58;
|
||||
type2enc(micro_block_hash) -> ?BASE58;
|
||||
type2enc(block_pof_hash) -> ?BASE58;
|
||||
type2enc(block_tx_hash) -> ?BASE58;
|
||||
type2enc(block_state_hash) -> ?BASE58;
|
||||
type2enc(block_witness_hash) -> ?BASE58;
|
||||
type2enc(channel) -> ?BASE58;
|
||||
type2enc(contract_pubkey) -> ?BASE58;
|
||||
type2enc(contract_bytearray) -> ?BASE64;
|
||||
type2enc(contract_store_key) -> ?BASE64;
|
||||
type2enc(contract_store_value) -> ?BASE64;
|
||||
type2enc(contract_source) -> ?BASE64;
|
||||
type2enc(transaction) -> ?BASE64;
|
||||
type2enc(tx_hash) -> ?BASE58;
|
||||
type2enc(account_pubkey) -> ?BASE58;
|
||||
type2enc(account_seckey) -> ?BASE58;
|
||||
type2enc(associate_chain) -> ?BASE58;
|
||||
type2enc(signature) -> ?BASE58;
|
||||
type2enc(commitment) -> ?BASE58;
|
||||
type2enc(peer_pubkey) -> ?BASE58;
|
||||
type2enc(name) -> ?BASE58;
|
||||
type2enc(native_token) -> ?BASE58;
|
||||
type2enc(state) -> ?BASE64;
|
||||
type2enc(poi) -> ?BASE64;
|
||||
type2enc(state_trees) -> ?BASE64;
|
||||
type2enc(call_state_tree) -> ?BASE64;
|
||||
type2enc(mp_tree_hash) -> ?BASE58;
|
||||
type2enc(hash) -> ?BASE58;
|
||||
type2enc(entry) -> ?BASE64;
|
||||
type2enc(bytearray) -> ?BASE64.
|
||||
|
||||
|
||||
type2pfx(key_block_hash) -> <<"kh">>;
|
||||
type2pfx(micro_block_hash) -> <<"mh">>;
|
||||
type2pfx(block_pof_hash) -> <<"bf">>;
|
||||
type2pfx(block_tx_hash) -> <<"bx">>;
|
||||
type2pfx(block_state_hash) -> <<"bs">>;
|
||||
type2pfx(block_witness_hash) -> <<"ws">>;
|
||||
type2pfx(channel) -> <<"ch">>;
|
||||
type2pfx(contract_pubkey) -> <<"ct">>;
|
||||
type2pfx(contract_bytearray) -> <<"cb">>;
|
||||
type2pfx(contract_store_key) -> <<"ck">>;
|
||||
type2pfx(contract_store_value) -> <<"cv">>;
|
||||
type2pfx(contract_source) -> <<"cx">>;
|
||||
type2pfx(transaction) -> <<"tx">>;
|
||||
type2pfx(tx_hash) -> <<"th">>;
|
||||
type2pfx(account_pubkey) -> <<"ak">>;
|
||||
type2pfx(account_seckey) -> <<"sk">>;
|
||||
type2pfx(associate_chain) -> <<"ac">>;
|
||||
type2pfx(signature) -> <<"sg">>;
|
||||
type2pfx(commitment) -> <<"cm">>;
|
||||
type2pfx(peer_pubkey) -> <<"pp">>;
|
||||
type2pfx(name) -> <<"nm">>;
|
||||
type2pfx(native_token) -> <<"nt">>;
|
||||
type2pfx(state) -> <<"st">>;
|
||||
type2pfx(poi) -> <<"pi">>;
|
||||
type2pfx(state_trees) -> <<"ss">>;
|
||||
type2pfx(call_state_tree) -> <<"cs">>;
|
||||
type2pfx(mp_tree_hash) -> <<"mt">>;
|
||||
type2pfx(hash) -> <<"hs">>;
|
||||
type2pfx(entry) -> <<"en">>;
|
||||
type2pfx(bytearray) -> <<"ba">>.
|
||||
|
||||
pfx2type(<<"kh">>) -> key_block_hash;
|
||||
pfx2type(<<"mh">>) -> micro_block_hash;
|
||||
pfx2type(<<"bf">>) -> block_pof_hash;
|
||||
pfx2type(<<"bx">>) -> block_tx_hash;
|
||||
pfx2type(<<"bs">>) -> block_state_hash;
|
||||
pfx2type(<<"ws">>) -> block_witness_hash;
|
||||
pfx2type(<<"ch">>) -> channel;
|
||||
pfx2type(<<"cb">>) -> contract_bytearray;
|
||||
pfx2type(<<"ck">>) -> contract_store_key;
|
||||
pfx2type(<<"cv">>) -> contract_store_value;
|
||||
pfx2type(<<"ct">>) -> contract_pubkey;
|
||||
pfx2type(<<"cx">>) -> contract_source;
|
||||
pfx2type(<<"tx">>) -> transaction;
|
||||
pfx2type(<<"th">>) -> tx_hash;
|
||||
pfx2type(<<"ak">>) -> account_pubkey;
|
||||
pfx2type(<<"sk">>) -> account_seckey;
|
||||
pfx2type(<<"ac">>) -> associate_chain;
|
||||
pfx2type(<<"sg">>) -> signature;
|
||||
pfx2type(<<"cm">>) -> commitment;
|
||||
pfx2type(<<"pp">>) -> peer_pubkey;
|
||||
pfx2type(<<"nm">>) -> name;
|
||||
pfx2type(<<"nt">>) -> native_token;
|
||||
pfx2type(<<"st">>) -> state;
|
||||
pfx2type(<<"pi">>) -> poi;
|
||||
pfx2type(<<"ss">>) -> state_trees;
|
||||
pfx2type(<<"cs">>) -> call_state_tree;
|
||||
pfx2type(<<"mt">>) -> mp_tree_hash;
|
||||
pfx2type(<<"hs">>) -> hash;
|
||||
pfx2type(<<"en">>) -> entry;
|
||||
pfx2type(<<"ba">>) -> bytearray.
|
||||
|
||||
-spec byte_size_for_type(known_type()) -> non_neg_integer() | not_applicable.
|
||||
|
||||
byte_size_for_type(key_block_hash) -> 32;
|
||||
byte_size_for_type(micro_block_hash) -> 32;
|
||||
byte_size_for_type(block_pof_hash) -> 32;
|
||||
byte_size_for_type(block_tx_hash) -> 32;
|
||||
byte_size_for_type(block_state_hash) -> 32;
|
||||
byte_size_for_type(block_witness_hash) -> 32;
|
||||
byte_size_for_type(channel) -> 32;
|
||||
byte_size_for_type(contract_pubkey) -> 32;
|
||||
byte_size_for_type(contract_bytearray) -> not_applicable;
|
||||
byte_size_for_type(contract_store_key) -> 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(tx_hash) -> 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(name) -> not_applicable;
|
||||
byte_size_for_type(native_token) -> 32;
|
||||
byte_size_for_type(commitment) -> 32;
|
||||
byte_size_for_type(peer_pubkey) -> 32;
|
||||
byte_size_for_type(state) -> 32;
|
||||
byte_size_for_type(poi) -> not_applicable;
|
||||
byte_size_for_type(state_trees) -> not_applicable;
|
||||
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.
|
||||
|
||||
|
||||
%% TODO: Fix the base58 module so that it consistently uses binaries instead
|
||||
%%
|
||||
binary_to_base58(Bin) ->
|
||||
iolist_to_binary(base58:binary_to_base58(Bin)).
|
||||
|
||||
base58_to_binary(Bin) when is_binary(Bin) ->
|
||||
base58:base58_to_binary(binary_to_list(Bin)).
|
||||
|
||||
binary_to_base64(Bin) ->
|
||||
base64:encode(Bin).
|
||||
|
||||
base64_to_binary(Bin) when is_binary(Bin) ->
|
||||
base64:decode(Bin).
|
||||
Reference in New Issue
Block a user