From 6d7ab1e4ad53a434de01279a0fe17953e0d3dc7c Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Tue, 7 Jul 2026 10:05:30 +0200 Subject: [PATCH] PoC: ASN.1 / DER serialization experiment This branch contains a proof-of-concept exploration of representing the RLP (gmser_rlp) and gmserialization layers using ASN.1 with DER encoding. Goals of the experiment: - Model the static serialization templates and dynamic types in ASN.1. - Demonstrate reliable detection of legacy RLP data vs. new DER data (first byte >= 0xC0 for RLP lists vs. 0x30 for DER SEQUENCE). - Evaluate wire-size overhead of standard DER TLV encoding compared to the extremely compact RLP prefix encoding. - Verify that the DER path produces deterministic output suitable for hashing (idempotent serialization). Key files: - GajumaruSerialization.asn -- the ASN.1 schema - GajumaruSerialization.{erl,hrl} -- generated encoder/decoder - detect_demo.erl, size_comparison.erl -- supporting test code Findings (high level): - Detection via first byte works cleanly. - DER is fully deterministic for hashing when the same value is given. - Small objects pay a significant size overhead (often 3-6x) due to TLV tags/lengths and field names in the generic path. - Concrete SEQUENCE definitions are much more compact than the generic templateFields representation. - Large payloads have acceptable relative overhead. This is NOT production code. It was created to answer feasibility questions around ASN.1 modeling, format detection, determinism, and size characteristics. Branch created from uw-new-acs for isolated experimentation. --- asn1/GajumaruSerialization.asn | 225 +++++ asn1/GajumaruSerialization.erl | 1402 ++++++++++++++++++++++++++++++++ asn1/GajumaruSerialization.hrl | 76 ++ asn1/detect_demo.erl | 27 + asn1/size_comparison.erl | 114 +++ 5 files changed, 1844 insertions(+) create mode 100644 asn1/GajumaruSerialization.asn create mode 100644 asn1/GajumaruSerialization.erl create mode 100644 asn1/GajumaruSerialization.hrl create mode 100644 asn1/detect_demo.erl create mode 100644 asn1/size_comparison.erl diff --git a/asn1/GajumaruSerialization.asn b/asn1/GajumaruSerialization.asn new file mode 100644 index 0000000..43607e9 --- /dev/null +++ b/asn1/GajumaruSerialization.asn @@ -0,0 +1,225 @@ +-- GajumaruSerialization.asn +-- +-- ASN.1 model for the structures serialized by gmserialization.erl +-- (the layer on top of RLP). +-- +-- Purpose: +-- * Provide a formal, toolable description of the data. +-- * Enable migration from the legacy RLP-based format to DER. +-- +-- Detection of legacy vs new: +-- Legacy data (produced by gmserialization:serialize / gmser_chain_objects:serialize +-- or gmser_dyn:serialize) is always an RLP *list* at the top level. +-- This means the first byte is in the range 0xC0 .. 0xFF. +-- +-- DER-encoded data using the types below will start with 0x30 (SEQUENCE, +-- constructed, universal tag) for the outermost GajumaruData (or any +-- top-level SEQUENCE we define). 0x30 < 0xC0, so a single-byte prefix +-- check reliably distinguishes the two formats. +-- +-- Usage in Erlang (after compiling with asn1ct): +-- {ok, Mod} = asn1ct:compile(GajumaruSerialization.asn, [ber, der]). +-- {ok, Term} = 'GajumaruSerialization':decode('GajumaruData', DerBinary). +-- +-- Notes on type mapping from gmserialization.erl: +-- - int -> INTEGER (new data uses canonical DER INTEGER) +-- - binary -> OCTET STRING +-- - bool -> BOOLEAN +-- - id -> Id (SEQUENCE) (cleaner than the legacy packed 33-byte form) +-- - [T] -> SEQUENCE OF T +-- - {T1,...} -> SEQUENCE (fixed-size, order matters) +-- - #{items := [{K,T},...]} -> SEQUENCE with the fields in template order +-- (static maps carry *values only*, no keys on the wire) +-- - tag + vsn are preserved as the first two fields for compatibility +-- with existing dispatch logic. +-- +-- Legacy integers had strict unsigned-minimal no-leading-zero (except for 0) +-- and RLP-level rules. New DER data does not need to follow those rules. +-- +-- WARNING: +-- Any cryptographic hash or signature computed over a serialized object +-- will change when switching from RLP to DER for that object. Plan a +-- coordinated upgrade. + +GajumaruSerialization DEFINITIONS + AUTOMATIC TAGS ::= +BEGIN + +EXPORTS ALL; + +-- ============================================================ +-- Top-level wrapper used for new DER data. +-- This is what a decoder will see first. +-- ============================================================ + +GajumaruData ::= SEQUENCE { + tag INTEGER, + vsn INTEGER, + content Content +} + +-- Content can be a specific structured type (preferred) or a generic +-- representation of a template-driven object. +Content ::= CHOICE { + -- Generic fallback that can represent any [{Field, Type}] template + -- without having a pre-defined SEQUENCE for every object. + templateFields [0] TemplateFields, + + -- Examples of concrete versioned types (extend as needed) + account [1] Account, + signedTx [2] SignedTx, + contract [3] ContractCode + -- Add more alternatives for other tags from gmser_chain_objects +} + +-- ============================================================ +-- Generic template-driven representation +-- (useful during transition or for unregistered types) +-- ============================================================ + +TemplateFields ::= SEQUENCE OF TemplateField + +TemplateField ::= SEQUENCE { + -- Field name is included for debuggability / generic processing. + -- In the original static encoding the name is NOT on the wire; + -- only position and type matter. We include it here for convenience. + name IA5String OPTIONAL, + value Value +} + +Value ::= CHOICE { + intValue [0] INTEGER, + boolValue [1] BOOLEAN, + binaryValue [2] OCTET STRING, + idValue [3] Id, + listValue [4] SEQUENCE OF Value, + tupleValue [5] SEQUENCE OF Value, + mapValue [6] SEQUENCE OF KeyValue -- only needed if you want to + -- represent dynamic-style maps +} + +KeyValue ::= SEQUENCE { + key Value, + val Value +} + +-- ============================================================ +-- Common types +-- ============================================================ + +Id ::= SEQUENCE { + -- Corresponds to the simple tags in gmser_id (account=1, name=2, etc.) + -- and the extended account subtype (high bit in legacy). + type INTEGER (0..255), + value OCTET STRING (SIZE (32)) +} + +-- ============================================================ +-- Concrete object examples (derived from usage in the codebase) +-- Add / evolve per version as you introduce new vsns. +-- ============================================================ + +-- Example: a very simple account-like object used in tests +Account ::= SEQUENCE { + foo INTEGER, + bar OCTET STRING +} + +-- Simplified signed transaction +SignedTx ::= SEQUENCE { + signatures SEQUENCE OF OCTET STRING, + transaction OCTET STRING +} + +-- Contract code objects (see gmser_contract_code.erl) +-- We model the three versions that exist today. +ContractCode ::= CHOICE { + v1 [0] ContractV1, + v2 [1] ContractV2, + v3 [2] ContractV3 +} + +ContractV1 ::= SEQUENCE { + sourceHash OCTET STRING, + -- typeInfo is a list of 4-tuples in legacy: + -- {typeHash, name, argType, outType} + typeInfo SEQUENCE OF TypeInfoV1, + byteCode OCTET STRING +} + +ContractV2 ::= SEQUENCE { + sourceHash OCTET STRING, + typeInfo SEQUENCE OF TypeInfoV1, + byteCode OCTET STRING, + compilerVersion OCTET STRING +} + +ContractV3 ::= SEQUENCE { + sourceHash OCTET STRING, + typeInfo SEQUENCE OF TypeInfoV3, + byteCode OCTET STRING, + compilerVersion OCTET STRING, + payable BOOLEAN +} + +TypeInfoV1 ::= SEQUENCE { + typeHash OCTET STRING, + name OCTET STRING, + argType OCTET STRING, + outType OCTET STRING +} + +TypeInfoV3 ::= SEQUENCE { + typeHash OCTET STRING, + name OCTET STRING, + payable BOOLEAN, + argType OCTET STRING, + outType OCTET STRING +} + +-- ============================================================ +-- Notes for implementers +-- ============================================================ +-- 1. Detection (recommended decoder entry point): +-- +-- decode(Binary) -> +-- case Binary of +-- <> when B >= 16#C0 -> +-- decode_legacy_rlp(Binary); % existing gmser_* path +-- _ -> +-- {ok, Term} = +-- 'GajumaruSerialization':decode('GajumaruData', Binary), +-- Term +-- end. +-- +-- This works because: +-- - All current top-level output from serialize() is an RLP list +-- (first byte 0xC0-0xFF). +-- - GajumaruData and the concrete choices above are SEQUENCEs +-- (first byte 0x30 for short form, or 0x30 0x81/0x82... for long). +-- +-- 2. When you add a new object type or version, prefer adding a +-- concrete SEQUENCE alternative in the Content CHOICE rather than +-- always falling back to TemplateFields. This gives you better +-- validation and generated types. +-- +-- 3. INTEGER in DER is signed and uses a different minimal encoding +-- than the legacy unsigned big-endian no leading zero form. This is +-- fine for new data. +-- +-- 4. If you need to preserve exact legacy integer wire bytes inside +-- the new format (e.g. for some hash preimage reason), you can +-- carry selected integers as OCTET STRING (legacy unsigned minimal bytes). +-- +-- 5. Dynamic encoding (gmser_dyn) is intentionally not modeled here +-- in full, because it is runtime-schema driven (type codes 246-255, +-- labels, alt/switch, etc.). You can still use the generic +-- TemplateFields + Value for some dynamic cases, or model specific +-- message schemas as additional CHOICE arms. +-- +-- 6. Compile with DER for canonical output: +-- asn1ct:compile(GajumaruSerialization, [der]). +-- +-- The ber option is also accepted; der implies the stricter rules. + +END diff --git a/asn1/GajumaruSerialization.erl b/asn1/GajumaruSerialization.erl new file mode 100644 index 0000000..1fed3af --- /dev/null +++ b/asn1/GajumaruSerialization.erl @@ -0,0 +1,1402 @@ +%% Generated by the Erlang ASN.1 BER compiler. Version: 5.4.3 +%% Purpose: Encoding and decoding of the types in GajumaruSerialization. + +-module('GajumaruSerialization'). +-moduledoc false. +-compile(nowarn_unused_vars). +-dialyzer(no_improper_lists). +-dialyzer(no_match). +-include("GajumaruSerialization.hrl"). +-asn1_info([{vsn,'5.4.3'}, + {module,'GajumaruSerialization'}, + {options,[der,{outdir,"asn1"},{i,"."},{i,"asn1"}]}]). + +-export([encoding_rule/0,maps/0,bit_string_format/0, + legacy_erlang_types/0]). +-export(['dialyzer-suppressions'/1]). +-export([ +enc_GajumaruData/2, +enc_Content/2, +enc_TemplateFields/2, +enc_TemplateField/2, +enc_Value/2, +enc_KeyValue/2, +enc_Id/2, +enc_Account/2, +enc_SignedTx/2, +enc_ContractCode/2, +enc_ContractV1/2, +enc_ContractV2/2, +enc_ContractV3/2, +enc_TypeInfoV1/2, +enc_TypeInfoV3/2 +]). + +-export([ +dec_GajumaruData/2, +dec_Content/2, +dec_TemplateFields/2, +dec_TemplateField/2, +dec_Value/2, +dec_KeyValue/2, +dec_Id/2, +dec_Account/2, +dec_SignedTx/2, +dec_ContractCode/2, +dec_ContractV1/2, +dec_ContractV2/2, +dec_ContractV3/2, +dec_TypeInfoV1/2, +dec_TypeInfoV3/2 +]). + +-export([info/0]). + +-export([encode/2,decode/2]). + +encoding_rule() -> ber. + +maps() -> false. + +bit_string_format() -> bitstring. + +legacy_erlang_types() -> false. + +encode(Type, Data) -> +try iolist_to_binary(element(1, encode_disp(Type, Data))) of + Bytes -> + {ok,Bytes} + catch + Class:Exception:Stk when Class =:= error; Class =:= exit -> + case Exception of + {error,{asn1,Reason}} -> + {error,{asn1,{Reason,Stk}}}; + Reason -> + {error,{asn1,{Reason,Stk}}} + end +end. + + +decode(Type, Data) -> +try + Result = decode_disp(Type, element(1, ber_decode_nif(Data))), + {ok,Result} + catch + Class:Exception:Stk when Class =:= error; Class =:= exit -> + case Exception of + {error,{asn1,Reason}} -> + {error,{asn1,{Reason,Stk}}}; + Reason -> + {error,{asn1,{Reason,Stk}}} + end +end. + +encode_disp('GajumaruData', Data) -> enc_GajumaruData(Data); +encode_disp('Content', Data) -> enc_Content(Data); +encode_disp('TemplateFields', Data) -> enc_TemplateFields(Data); +encode_disp('TemplateField', Data) -> enc_TemplateField(Data); +encode_disp('Value', Data) -> enc_Value(Data); +encode_disp('KeyValue', Data) -> enc_KeyValue(Data); +encode_disp('Id', Data) -> enc_Id(Data); +encode_disp('Account', Data) -> enc_Account(Data); +encode_disp('SignedTx', Data) -> enc_SignedTx(Data); +encode_disp('ContractCode', Data) -> enc_ContractCode(Data); +encode_disp('ContractV1', Data) -> enc_ContractV1(Data); +encode_disp('ContractV2', Data) -> enc_ContractV2(Data); +encode_disp('ContractV3', Data) -> enc_ContractV3(Data); +encode_disp('TypeInfoV1', Data) -> enc_TypeInfoV1(Data); +encode_disp('TypeInfoV3', Data) -> enc_TypeInfoV3(Data); +encode_disp(Type, _Data) -> exit({error,{asn1,{undefined_type,Type}}}). + +decode_disp('GajumaruData', Data) -> dec_GajumaruData(Data); +decode_disp('Content', Data) -> dec_Content(Data); +decode_disp('TemplateFields', Data) -> dec_TemplateFields(Data); +decode_disp('TemplateField', Data) -> dec_TemplateField(Data); +decode_disp('Value', Data) -> dec_Value(Data); +decode_disp('KeyValue', Data) -> dec_KeyValue(Data); +decode_disp('Id', Data) -> dec_Id(Data); +decode_disp('Account', Data) -> dec_Account(Data); +decode_disp('SignedTx', Data) -> dec_SignedTx(Data); +decode_disp('ContractCode', Data) -> dec_ContractCode(Data); +decode_disp('ContractV1', Data) -> dec_ContractV1(Data); +decode_disp('ContractV2', Data) -> dec_ContractV2(Data); +decode_disp('ContractV3', Data) -> dec_ContractV3(Data); +decode_disp('TypeInfoV1', Data) -> dec_TypeInfoV1(Data); +decode_disp('TypeInfoV3', Data) -> dec_TypeInfoV3(Data); +decode_disp(Type, _Data) -> exit({error,{asn1,{undefined_type,Type}}}). + +info() -> + case ?MODULE:module_info(attributes) of + Attributes when is_list(Attributes) -> + case lists:keyfind(asn1_info, 1, Attributes) of + {_,Info} when is_list(Info) -> + Info; + _ -> + [] + end; + _ -> + [] + end. + + +%%================================ +%% GajumaruData +%%================================ +enc_GajumaruData(Val) -> + enc_GajumaruData(Val, [<<48>>]). + +enc_GajumaruData(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3} = Val, + +%%------------------------------------------------- +%% attribute tag(1) with type INTEGER +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_integer(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute vsn(2) with type INTEGER +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_integer(Cindex2, [<<129>>]), + +%%------------------------------------------------- +%% attribute content(3) External GajumaruSerialization:Content +%%------------------------------------------------- + {EncBytes3,EncLen3} = 'enc_Content'(Cindex3, [<<162>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], +LenSoFar = EncLen1 + EncLen2 + EncLen3, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_GajumaruData(Tlv) -> + dec_GajumaruData(Tlv, [16]). + +dec_GajumaruData(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute tag(1) with type INTEGER +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_integer(V1, [131072]), + +%%------------------------------------------------- +%% attribute vsn(2) with type INTEGER +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = decode_integer(V2, [131073]), + +%%------------------------------------------------- +%% attribute content(3) External GajumaruSerialization:Content +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = 'dec_Content'(V3, [131074]), + +case Tlv4 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed +end, +Res1 = {'GajumaruData',Term1,Term2,Term3}, +Res1. + + +%%================================ +%% Content +%%================================ +enc_Content(Val) -> + enc_Content(Val, []). + +enc_Content(Val, TagIn) -> + {EncBytes,EncLen} = case element(1,Val) of + templateFields -> + 'enc_TemplateFields'(element(2,Val), [<<160>>]); + account -> + 'enc_Account'(element(2,Val), [<<161>>]); + signedTx -> + 'enc_SignedTx'(element(2,Val), [<<162>>]); + contract -> + 'enc_ContractCode'(element(2,Val), [<<163>>]); + Else -> + exit({error,{asn1,{invalid_choice_type,Else}}}) + end, + +encode_tags(TagIn, EncBytes, EncLen). + + + + +dec_Content(Tlv) -> + dec_Content(Tlv, []). + +dec_Content(Tlv, TagIn) -> +Tlv1 = match_tags(Tlv, TagIn), +case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of + +%% 'templateFields' + {131072, V1} -> + {templateFields, 'dec_TemplateFields'(V1, [])}; + + +%% 'account' + {131073, V1} -> + {account, 'dec_Account'(V1, [])}; + + +%% 'signedTx' + {131074, V1} -> + {signedTx, 'dec_SignedTx'(V1, [])}; + + +%% 'contract' + {131075, V1} -> + {contract, 'dec_ContractCode'(V1, [])}; + + Else -> + exit({error,{asn1,{invalid_choice_tag,Else}}}) + end +. + + +%%================================ +%% TemplateFields +%%================================ +enc_TemplateFields(Val) -> + enc_TemplateFields(Val, [<<48>>]). + +enc_TemplateFields(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_TemplateFields_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_TemplateFields_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_TemplateFields_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_TemplateField'(H, [<<48>>]), + 'enc_TemplateFields_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_TemplateFields(Tlv) -> + dec_TemplateFields(Tlv, [16]). + +dec_TemplateFields(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_TemplateField'(V1, [16]) || V1 <- Tlv1]. + + + + +%%================================ +%% TemplateField +%%================================ +enc_TemplateField(Val) -> + enc_TemplateField(Val, [<<48>>]). + +enc_TemplateField(Val, TagIn) -> +{_,Cindex1,Cindex2} = Val, + +%%------------------------------------------------- +%% attribute name(1) with type IA5String OPTIONAL +%%------------------------------------------------- + {EncBytes1,EncLen1} = case Cindex1 of + asn1_NOVALUE -> {<<>>,0}; + _ -> + encode_restricted_string(Cindex1, [<<128>>]) + end, + +%%------------------------------------------------- +%% attribute value(2) External GajumaruSerialization:Value +%%------------------------------------------------- + {EncBytes2,EncLen2} = 'enc_Value'(Cindex2, [<<161>>]), + + BytesSoFar = [EncBytes1, EncBytes2], +LenSoFar = EncLen1 + EncLen2, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_TemplateField(Tlv) -> + dec_TemplateField(Tlv, [16]). + +dec_TemplateField(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute name(1) with type IA5String OPTIONAL +%%------------------------------------------------- +{Term1,Tlv2} = case Tlv1 of +[{131072,V1}|TempTlv2] -> + {begin +binary_to_list(decode_restricted_string(V1, [])) +end +, TempTlv2}; + _ -> + { asn1_NOVALUE, Tlv1} +end, + +%%------------------------------------------------- +%% attribute value(2) External GajumaruSerialization:Value +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = 'dec_Value'(V2, [131073]), + +case Tlv3 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed +end, +Res1 = {'TemplateField',Term1,Term2}, +Res1. + + +%%================================ +%% Value +%%================================ +enc_Value(Val) -> + enc_Value(Val, []). + +enc_Value(Val, TagIn) -> + {EncBytes,EncLen} = case element(1,Val) of + intValue -> + encode_integer(element(2,Val), [<<128>>]); + boolValue -> + encode_boolean(element(2,Val), [<<129>>]); + binaryValue -> + encode_restricted_string(element(2,Val), [<<130>>]); + idValue -> + 'enc_Id'(element(2,Val), [<<163>>]); + listValue -> + 'enc_Value_listValue'(element(2,Val), [<<164>>]); + tupleValue -> + 'enc_Value_tupleValue'(element(2,Val), [<<165>>]); + mapValue -> + 'enc_Value_mapValue'(element(2,Val), [<<166>>]); + Else -> + exit({error,{asn1,{invalid_choice_type,Else}}}) + end, + +encode_tags(TagIn, EncBytes, EncLen). + + + + + +%%================================ +%% Value_listValue +%%================================ +enc_Value_listValue(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_Value_listValue_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_Value_listValue_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_Value_listValue_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_Value'(H, []), + 'enc_Value_listValue_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + + +%%================================ +%% Value_tupleValue +%%================================ +enc_Value_tupleValue(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_Value_tupleValue_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_Value_tupleValue_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_Value_tupleValue_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_Value'(H, []), + 'enc_Value_tupleValue_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + + +%%================================ +%% Value_mapValue +%%================================ +enc_Value_mapValue(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_Value_mapValue_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_Value_mapValue_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_Value_mapValue_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_KeyValue'(H, [<<48>>]), + 'enc_Value_mapValue_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_Value(Tlv) -> + dec_Value(Tlv, []). + +dec_Value(Tlv, TagIn) -> +Tlv1 = match_tags(Tlv, TagIn), +case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of + +%% 'intValue' + {131072, V1} -> + {intValue, decode_integer(V1, [])}; + + +%% 'boolValue' + {131073, V1} -> + {boolValue, decode_boolean(V1, [])}; + + +%% 'binaryValue' + {131074, V1} -> + {binaryValue, decode_octet_string(V1, [])}; + + +%% 'idValue' + {131075, V1} -> + {idValue, 'dec_Id'(V1, [])}; + + +%% 'listValue' + {131076, V1} -> + {listValue, 'dec_Value_listValue'(V1, [])}; + + +%% 'tupleValue' + {131077, V1} -> + {tupleValue, 'dec_Value_tupleValue'(V1, [])}; + + +%% 'mapValue' + {131078, V1} -> + {mapValue, 'dec_Value_mapValue'(V1, [])}; + + Else -> + exit({error,{asn1,{invalid_choice_tag,Else}}}) + end +. +'dec_Value_listValue'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_Value'(V1, []) || V1 <- Tlv1]. + + +'dec_Value_tupleValue'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_Value'(V1, []) || V1 <- Tlv1]. + + +'dec_Value_mapValue'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_KeyValue'(V1, [16]) || V1 <- Tlv1]. + + + + +%%================================ +%% KeyValue +%%================================ +enc_KeyValue(Val) -> + enc_KeyValue(Val, [<<48>>]). + +enc_KeyValue(Val, TagIn) -> +{_,Cindex1,Cindex2} = Val, + +%%------------------------------------------------- +%% attribute key(1) External GajumaruSerialization:Value +%%------------------------------------------------- + {EncBytes1,EncLen1} = 'enc_Value'(Cindex1, [<<160>>]), + +%%------------------------------------------------- +%% attribute val(2) External GajumaruSerialization:Value +%%------------------------------------------------- + {EncBytes2,EncLen2} = 'enc_Value'(Cindex2, [<<161>>]), + + BytesSoFar = [EncBytes1, EncBytes2], +LenSoFar = EncLen1 + EncLen2, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_KeyValue(Tlv) -> + dec_KeyValue(Tlv, [16]). + +dec_KeyValue(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute key(1) External GajumaruSerialization:Value +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = 'dec_Value'(V1, [131072]), + +%%------------------------------------------------- +%% attribute val(2) External GajumaruSerialization:Value +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = 'dec_Value'(V2, [131073]), + +case Tlv3 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed +end, +Res1 = {'KeyValue',Term1,Term2}, +Res1. + + +%%================================ +%% Id +%%================================ +enc_Id(Val) -> + enc_Id(Val, [<<48>>]). + +enc_Id(Val, TagIn) -> +{_,Cindex1,Cindex2} = Val, + +%%------------------------------------------------- +%% attribute type(1) with type INTEGER +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_integer(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute value(2) with type OCTET STRING +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<129>>]), + + BytesSoFar = [EncBytes1, EncBytes2], +LenSoFar = EncLen1 + EncLen2, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_Id(Tlv) -> + dec_Id(Tlv, [16]). + +dec_Id(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute type(1) with type INTEGER +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = begin +Val1 = decode_integer(V1, [131072]), +if 0 =< Val1, Val1 =< 255 -> +Val1; +true -> +exit({error,{asn1,bad_range}}) +end +end, + +%%------------------------------------------------- +%% attribute value(2) with type OCTET STRING +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = begin +Val2 = decode_octet_string(V2, [131073]), +C1 = byte_size(Val2), +if C1 =:= 32 -> +Val2; +true -> +exit({error,{asn1,bad_range}}) +end +end, + +case Tlv3 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed +end, +Res1 = {'Id',Term1,Term2}, +Res1. + + +%%================================ +%% Account +%%================================ +enc_Account(Val) -> + enc_Account(Val, [<<48>>]). + +enc_Account(Val, TagIn) -> +{_,Cindex1,Cindex2} = Val, + +%%------------------------------------------------- +%% attribute foo(1) with type INTEGER +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_integer(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute bar(2) with type OCTET STRING +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<129>>]), + + BytesSoFar = [EncBytes1, EncBytes2], +LenSoFar = EncLen1 + EncLen2, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_Account(Tlv) -> + dec_Account(Tlv, [16]). + +dec_Account(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute foo(1) with type INTEGER +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_integer(V1, [131072]), + +%%------------------------------------------------- +%% attribute bar(2) with type OCTET STRING +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = decode_octet_string(V2, [131073]), + +case Tlv3 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed +end, +Res1 = {'Account',Term1,Term2}, +Res1. + + +%%================================ +%% SignedTx +%%================================ +enc_SignedTx(Val) -> + enc_SignedTx(Val, [<<48>>]). + +enc_SignedTx(Val, TagIn) -> +{_,Cindex1,Cindex2} = Val, + +%%------------------------------------------------- +%% attribute signatures(1) with type SEQUENCE OF +%%------------------------------------------------- + {EncBytes1,EncLen1} = 'enc_SignedTx_signatures'(Cindex1, [<<160>>]), + +%%------------------------------------------------- +%% attribute transaction(2) with type OCTET STRING +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<129>>]), + + BytesSoFar = [EncBytes1, EncBytes2], +LenSoFar = EncLen1 + EncLen2, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + + +%%================================ +%% SignedTx_signatures +%%================================ +enc_SignedTx_signatures(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_SignedTx_signatures_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_SignedTx_signatures_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_SignedTx_signatures_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = encode_restricted_string(H, [<<4>>]), + 'enc_SignedTx_signatures_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_SignedTx(Tlv) -> + dec_SignedTx(Tlv, [16]). + +dec_SignedTx(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute signatures(1) with type SEQUENCE OF +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = 'dec_SignedTx_signatures'(V1, [131072]), + +%%------------------------------------------------- +%% attribute transaction(2) with type OCTET STRING +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = decode_octet_string(V2, [131073]), + +case Tlv3 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv3}}}) % extra fields not allowed +end, +Res1 = {'SignedTx',Term1,Term2}, +Res1. +'dec_SignedTx_signatures'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +[decode_octet_string(V1, [4]) || V1 <- Tlv1]. + + + + +%%================================ +%% ContractCode +%%================================ +enc_ContractCode(Val) -> + enc_ContractCode(Val, []). + +enc_ContractCode(Val, TagIn) -> + {EncBytes,EncLen} = case element(1,Val) of + v1 -> + 'enc_ContractV1'(element(2,Val), [<<160>>]); + v2 -> + 'enc_ContractV2'(element(2,Val), [<<161>>]); + v3 -> + 'enc_ContractV3'(element(2,Val), [<<162>>]); + Else -> + exit({error,{asn1,{invalid_choice_type,Else}}}) + end, + +encode_tags(TagIn, EncBytes, EncLen). + + + + +dec_ContractCode(Tlv) -> + dec_ContractCode(Tlv, []). + +dec_ContractCode(Tlv, TagIn) -> +Tlv1 = match_tags(Tlv, TagIn), +case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of + +%% 'v1' + {131072, V1} -> + {v1, 'dec_ContractV1'(V1, [])}; + + +%% 'v2' + {131073, V1} -> + {v2, 'dec_ContractV2'(V1, [])}; + + +%% 'v3' + {131074, V1} -> + {v3, 'dec_ContractV3'(V1, [])}; + + Else -> + exit({error,{asn1,{invalid_choice_tag,Else}}}) + end +. + + +%%================================ +%% ContractV1 +%%================================ +enc_ContractV1(Val) -> + enc_ContractV1(Val, [<<48>>]). + +enc_ContractV1(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3} = Val, + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- + {EncBytes2,EncLen2} = 'enc_ContractV1_typeInfo'(Cindex2, [<<161>>]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- + {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<130>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3], +LenSoFar = EncLen1 + EncLen2 + EncLen3, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + + +%%================================ +%% ContractV1_typeInfo +%%================================ +enc_ContractV1_typeInfo(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_ContractV1_typeInfo_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_ContractV1_typeInfo_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_ContractV1_typeInfo_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_TypeInfoV1'(H, [<<48>>]), + 'enc_ContractV1_typeInfo_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_ContractV1(Tlv) -> + dec_ContractV1(Tlv, [16]). + +dec_ContractV1(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_octet_string(V1, [131072]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = 'dec_ContractV1_typeInfo'(V2, [131073]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = decode_octet_string(V3, [131074]), + +case Tlv4 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv4}}}) % extra fields not allowed +end, +Res1 = {'ContractV1',Term1,Term2,Term3}, +Res1. +'dec_ContractV1_typeInfo'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_TypeInfoV1'(V1, [16]) || V1 <- Tlv1]. + + + + +%%================================ +%% ContractV2 +%%================================ +enc_ContractV2(Val) -> + enc_ContractV2(Val, [<<48>>]). + +enc_ContractV2(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3,Cindex4} = Val, + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- + {EncBytes2,EncLen2} = 'enc_ContractV2_typeInfo'(Cindex2, [<<161>>]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- + {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<130>>]), + +%%------------------------------------------------- +%% attribute compilerVersion(4) with type OCTET STRING +%%------------------------------------------------- + {EncBytes4,EncLen4} = encode_restricted_string(Cindex4, [<<131>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4], +LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + + +%%================================ +%% ContractV2_typeInfo +%%================================ +enc_ContractV2_typeInfo(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_ContractV2_typeInfo_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_ContractV2_typeInfo_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_ContractV2_typeInfo_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_TypeInfoV1'(H, [<<48>>]), + 'enc_ContractV2_typeInfo_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_ContractV2(Tlv) -> + dec_ContractV2(Tlv, [16]). + +dec_ContractV2(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_octet_string(V1, [131072]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = 'dec_ContractV2_typeInfo'(V2, [131073]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = decode_octet_string(V3, [131074]), + +%%------------------------------------------------- +%% attribute compilerVersion(4) with type OCTET STRING +%%------------------------------------------------- +[V4|Tlv5] = Tlv4, +Term4 = decode_octet_string(V4, [131075]), + +case Tlv5 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv5}}}) % extra fields not allowed +end, +Res1 = {'ContractV2',Term1,Term2,Term3,Term4}, +Res1. +'dec_ContractV2_typeInfo'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_TypeInfoV1'(V1, [16]) || V1 <- Tlv1]. + + + + +%%================================ +%% ContractV3 +%%================================ +enc_ContractV3(Val) -> + enc_ContractV3(Val, [<<48>>]). + +enc_ContractV3(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3,Cindex4,Cindex5} = Val, + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- + {EncBytes2,EncLen2} = 'enc_ContractV3_typeInfo'(Cindex2, [<<161>>]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- + {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<130>>]), + +%%------------------------------------------------- +%% attribute compilerVersion(4) with type OCTET STRING +%%------------------------------------------------- + {EncBytes4,EncLen4} = encode_restricted_string(Cindex4, [<<131>>]), + +%%------------------------------------------------- +%% attribute payable(5) with type BOOLEAN +%%------------------------------------------------- + {EncBytes5,EncLen5} = encode_boolean(Cindex5, [<<132>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5], +LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + + +%%================================ +%% ContractV3_typeInfo +%%================================ +enc_ContractV3_typeInfo(Val, TagIn) -> + {EncBytes,EncLen} = 'enc_ContractV3_typeInfo_components'(Val,[],0), + encode_tags(TagIn, EncBytes, EncLen). + +'enc_ContractV3_typeInfo_components'([], AccBytes, AccLen) -> + {lists:reverse(AccBytes),AccLen}; + +'enc_ContractV3_typeInfo_components'([H|T],AccBytes, AccLen) -> + {EncBytes,EncLen} = 'enc_TypeInfoV3'(H, [<<48>>]), + 'enc_ContractV3_typeInfo_components'(T,[EncBytes|AccBytes], AccLen + EncLen). + + + +dec_ContractV3(Tlv) -> + dec_ContractV3(Tlv, [16]). + +dec_ContractV3(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute sourceHash(1) with type OCTET STRING +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_octet_string(V1, [131072]), + +%%------------------------------------------------- +%% attribute typeInfo(2) with type SEQUENCE OF +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = 'dec_ContractV3_typeInfo'(V2, [131073]), + +%%------------------------------------------------- +%% attribute byteCode(3) with type OCTET STRING +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = decode_octet_string(V3, [131074]), + +%%------------------------------------------------- +%% attribute compilerVersion(4) with type OCTET STRING +%%------------------------------------------------- +[V4|Tlv5] = Tlv4, +Term4 = decode_octet_string(V4, [131075]), + +%%------------------------------------------------- +%% attribute payable(5) with type BOOLEAN +%%------------------------------------------------- +[V5|Tlv6] = Tlv5, +Term5 = decode_boolean(V5, [131076]), + +case Tlv6 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv6}}}) % extra fields not allowed +end, +Res1 = {'ContractV3',Term1,Term2,Term3,Term4,Term5}, +Res1. +'dec_ContractV3_typeInfo'(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), +['dec_TypeInfoV3'(V1, [16]) || V1 <- Tlv1]. + + + + +%%================================ +%% TypeInfoV1 +%%================================ +enc_TypeInfoV1(Val) -> + enc_TypeInfoV1(Val, [<<48>>]). + +enc_TypeInfoV1(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3,Cindex4} = Val, + +%%------------------------------------------------- +%% attribute typeHash(1) with type OCTET STRING +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute name(2) with type OCTET STRING +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<129>>]), + +%%------------------------------------------------- +%% attribute argType(3) with type OCTET STRING +%%------------------------------------------------- + {EncBytes3,EncLen3} = encode_restricted_string(Cindex3, [<<130>>]), + +%%------------------------------------------------- +%% attribute outType(4) with type OCTET STRING +%%------------------------------------------------- + {EncBytes4,EncLen4} = encode_restricted_string(Cindex4, [<<131>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4], +LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_TypeInfoV1(Tlv) -> + dec_TypeInfoV1(Tlv, [16]). + +dec_TypeInfoV1(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute typeHash(1) with type OCTET STRING +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_octet_string(V1, [131072]), + +%%------------------------------------------------- +%% attribute name(2) with type OCTET STRING +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = decode_octet_string(V2, [131073]), + +%%------------------------------------------------- +%% attribute argType(3) with type OCTET STRING +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = decode_octet_string(V3, [131074]), + +%%------------------------------------------------- +%% attribute outType(4) with type OCTET STRING +%%------------------------------------------------- +[V4|Tlv5] = Tlv4, +Term4 = decode_octet_string(V4, [131075]), + +case Tlv5 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv5}}}) % extra fields not allowed +end, +Res1 = {'TypeInfoV1',Term1,Term2,Term3,Term4}, +Res1. + + +%%================================ +%% TypeInfoV3 +%%================================ +enc_TypeInfoV3(Val) -> + enc_TypeInfoV3(Val, [<<48>>]). + +enc_TypeInfoV3(Val, TagIn) -> +{_,Cindex1,Cindex2,Cindex3,Cindex4,Cindex5} = Val, + +%%------------------------------------------------- +%% attribute typeHash(1) with type OCTET STRING +%%------------------------------------------------- + {EncBytes1,EncLen1} = encode_restricted_string(Cindex1, [<<128>>]), + +%%------------------------------------------------- +%% attribute name(2) with type OCTET STRING +%%------------------------------------------------- + {EncBytes2,EncLen2} = encode_restricted_string(Cindex2, [<<129>>]), + +%%------------------------------------------------- +%% attribute payable(3) with type BOOLEAN +%%------------------------------------------------- + {EncBytes3,EncLen3} = encode_boolean(Cindex3, [<<130>>]), + +%%------------------------------------------------- +%% attribute argType(4) with type OCTET STRING +%%------------------------------------------------- + {EncBytes4,EncLen4} = encode_restricted_string(Cindex4, [<<131>>]), + +%%------------------------------------------------- +%% attribute outType(5) with type OCTET STRING +%%------------------------------------------------- + {EncBytes5,EncLen5} = encode_restricted_string(Cindex5, [<<132>>]), + + BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5], +LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5, +encode_tags(TagIn, BytesSoFar, LenSoFar). + + +dec_TypeInfoV3(Tlv) -> + dec_TypeInfoV3(Tlv, [16]). + +dec_TypeInfoV3(Tlv, TagIn) -> + %%------------------------------------------------- + %% decode tag and length + %%------------------------------------------------- +Tlv1 = match_tags(Tlv, TagIn), + +%%------------------------------------------------- +%% attribute typeHash(1) with type OCTET STRING +%%------------------------------------------------- +[V1|Tlv2] = Tlv1, +Term1 = decode_octet_string(V1, [131072]), + +%%------------------------------------------------- +%% attribute name(2) with type OCTET STRING +%%------------------------------------------------- +[V2|Tlv3] = Tlv2, +Term2 = decode_octet_string(V2, [131073]), + +%%------------------------------------------------- +%% attribute payable(3) with type BOOLEAN +%%------------------------------------------------- +[V3|Tlv4] = Tlv3, +Term3 = decode_boolean(V3, [131074]), + +%%------------------------------------------------- +%% attribute argType(4) with type OCTET STRING +%%------------------------------------------------- +[V4|Tlv5] = Tlv4, +Term4 = decode_octet_string(V4, [131075]), + +%%------------------------------------------------- +%% attribute outType(5) with type OCTET STRING +%%------------------------------------------------- +[V5|Tlv6] = Tlv5, +Term5 = decode_octet_string(V5, [131076]), + +case Tlv6 of +[] -> true;_ -> exit({error,{asn1, {unexpected,Tlv6}}}) % extra fields not allowed +end, +Res1 = {'TypeInfoV3',Term1,Term2,Term3,Term4,Term5}, +Res1. + +%%% +%%% Run-time functions. +%%% + +'dialyzer-suppressions'(Arg) -> + ok. + +ber_decode_nif(B) -> + asn1rt_nif:decode_ber_tlv(B). + +collect_parts(TlvList) -> + collect_parts(TlvList, []). + +collect_parts([{_, L} | Rest], Acc) when is_list(L) -> + collect_parts(Rest, [collect_parts(L) | Acc]); +collect_parts([{3, <>} | Rest], _Acc) -> + collect_parts_bit(Rest, [Bits], Unused); +collect_parts([{_T, V} | Rest], Acc) -> + collect_parts(Rest, [V | Acc]); +collect_parts([], Acc) -> + list_to_binary(lists:reverse(Acc)). + +collect_parts_bit([{3, <>} | Rest], Acc, Uacc) -> + collect_parts_bit(Rest, [Bits | Acc], Unused + Uacc); +collect_parts_bit([], Acc, Uacc) -> + list_to_binary([Uacc | lists:reverse(Acc)]). + +decode_boolean(Tlv, TagIn) -> + Val = match_tags(Tlv, TagIn), + case Val of + <<0:8>> -> + false; + <<_:8>> -> + true; + _ -> + exit({error, {asn1, {decode_boolean, Val}}}) + end. + +decode_integer(Tlv, TagIn) -> + Bin = match_tags(Tlv, TagIn), + Len = byte_size(Bin), + <> = Bin, + Int. + +decode_octet_string(Tlv, TagsIn) -> + Bin = match_and_collect(Tlv, TagsIn), + binary:copy(Bin). + +decode_restricted_string(Tlv, TagsIn) -> + match_and_collect(Tlv, TagsIn). + +encode_boolean(true, TagIn) -> + encode_tags(TagIn, [255], 1); +encode_boolean(false, TagIn) -> + encode_tags(TagIn, [0], 1); +encode_boolean(X, _) -> + exit({error, {asn1, {encode_boolean, X}}}). + +encode_integer(Val) -> + Bytes = + if + Val >= 0 -> + encode_integer_pos(Val, []); + true -> + encode_integer_neg(Val, []) + end, + {Bytes, length(Bytes)}. + +encode_integer(Val, Tag) when is_integer(Val) -> + encode_tags(Tag, encode_integer(Val)); +encode_integer(Val, _Tag) -> + exit({error, {asn1, {encode_integer, Val}}}). + +encode_integer_neg(-1, [B1 | _T] = L) when B1 > 127 -> + L; +encode_integer_neg(N, Acc) -> + encode_integer_neg(N bsr 8, [N band 255 | Acc]). + +encode_integer_pos(0, [B | _Acc] = L) when B < 128 -> + L; +encode_integer_pos(N, Acc) -> + encode_integer_pos(N bsr 8, [N band 255 | Acc]). + +encode_length(L) when L =< 127 -> + {[L], 1}; +encode_length(L) -> + Oct = minimum_octets(L), + Len = length(Oct), + if + Len =< 126 -> + {[128 bor Len | Oct], Len + 1}; + true -> + exit({error, {asn1, too_long_length_oct, Len}}) + end. + +encode_restricted_string(OctetList, TagIn) when is_binary(OctetList) -> + encode_tags(TagIn, OctetList, byte_size(OctetList)); +encode_restricted_string(OctetList, TagIn) when is_list(OctetList) -> + encode_tags(TagIn, OctetList, length(OctetList)). + +encode_tags(TagIn, {BytesSoFar, LenSoFar}) -> + encode_tags(TagIn, BytesSoFar, LenSoFar). + +encode_tags([Tag | Trest], BytesSoFar, LenSoFar) -> + {Bytes2, L2} = encode_length(LenSoFar), + encode_tags(Trest, + [Tag, Bytes2 | BytesSoFar], + LenSoFar + byte_size(Tag) + L2); +encode_tags([], BytesSoFar, LenSoFar) -> + {BytesSoFar, LenSoFar}. + +match_and_collect(Tlv, TagsIn) -> + Val = match_tags(Tlv, TagsIn), + case Val of + [_ | _] = PartList -> + collect_parts(PartList); + Bin when is_binary(Bin) -> + Bin + end. + +match_tags({T, V}, [T]) -> + V; +match_tags({T, V}, [T | Tt]) -> + match_tags(V, Tt); +match_tags([{T, V}], [T | Tt]) -> + match_tags(V, Tt); +match_tags([{T, _V} | _] = Vlist, [T]) -> + Vlist; +match_tags(Tlv, []) -> + Tlv; +match_tags({Tag, _V} = Tlv, [T | _Tt]) -> + exit({error, {asn1, {wrong_tag, {{expected, T}, {got, Tag, Tlv}}}}}). + +minimum_octets(0, Acc) -> + Acc; +minimum_octets(Val, Acc) -> + minimum_octets(Val bsr 8, [Val band 255 | Acc]). + +minimum_octets(Val) -> + minimum_octets(Val, []). diff --git a/asn1/GajumaruSerialization.hrl b/asn1/GajumaruSerialization.hrl new file mode 100644 index 0000000..4c5d52e --- /dev/null +++ b/asn1/GajumaruSerialization.hrl @@ -0,0 +1,76 @@ +%% Generated by the Erlang ASN.1 compiler. Version: 5.4.3 +%% Purpose: Erlang record definitions for each named and unnamed +%% SEQUENCE and SET, and macro definitions for each value +%% definition in module GajumaruSerialization. + +-ifndef(_GAJUMARUSERIALIZATION_HRL_). +-define(_GAJUMARUSERIALIZATION_HRL_, true). + +-record('GajumaruData', { + tag, + vsn, + content +}). + +-record('TemplateField', { + name = asn1_NOVALUE, + value +}). + +-record('KeyValue', { + key, + val +}). + +-record('Id', { + type, + value +}). + +-record('Account', { + foo, + bar +}). + +-record('SignedTx', { + signatures, + transaction +}). + +-record('ContractV1', { + sourceHash, + typeInfo, + byteCode +}). + +-record('ContractV2', { + sourceHash, + typeInfo, + byteCode, + compilerVersion +}). + +-record('ContractV3', { + sourceHash, + typeInfo, + byteCode, + compilerVersion, + payable +}). + +-record('TypeInfoV1', { + typeHash, + name, + argType, + outType +}). + +-record('TypeInfoV3', { + typeHash, + name, + payable, + argType, + outType +}). + +-endif. %% _GAJUMARUSERIALIZATION_HRL_ diff --git a/asn1/detect_demo.erl b/asn1/detect_demo.erl new file mode 100644 index 0000000..c938688 --- /dev/null +++ b/asn1/detect_demo.erl @@ -0,0 +1,27 @@ +-module(detect_demo). +-export([run/0]). + +run() -> + application:ensure_all_started(gmserialization), + code:add_path("asn1"), + + Sample = {'GajumaruData', 10, 1, + {templateFields, [ + {'TemplateField', <<"foo">>, {intValue, 42}}, + {'TemplateField', <<"bar">>, {binaryValue, <<"hello">>}} + ]}}, + + {ok, Der} = 'GajumaruSerialization':encode('GajumaruData', Sample), + io:format("DER first byte: ~p (0x~2.16.0B)~n", [binary:at(Der,0), binary:at(Der,0)]), + + {ok, _Dec} = 'GajumaruSerialization':decode('GajumaruData', Der), + io:format("DER roundtrip OK~n"), + + Legacy = gmser_chain_objects:serialize(account, 1, + [{foo,int},{bar,binary}], + [{foo,42},{bar,<<"hello">>}]), + <> = binary:part(Legacy,0,1), + <> = binary:part(Der,0,1), + io:format("Legacy 0x~2.16.0B (>= 0xC0 -> legacy: ~p)~n", [L0, L0 >= 16#C0]), + io:format("DER 0x~2.16.0B (< 0xC0 -> new DER: ~p)~n", [D0, D0 < 16#C0]), + ok. diff --git a/asn1/size_comparison.erl b/asn1/size_comparison.erl new file mode 100644 index 0000000..17d3969 --- /dev/null +++ b/asn1/size_comparison.erl @@ -0,0 +1,114 @@ +-module(size_comparison). +-export([run/0]). + +run() -> + application:ensure_all_started(gmserialization), + code:add_path("asn1"), + + io:format("=== Size Comparison: Legacy RLP vs DER ===~n~n"), + + Compare = fun(Desc, LegacyBin, DerBin) -> + L = byte_size(LegacyBin), + D = byte_size(DerBin), + Overhead = D - L, + Pct = case L of 0 -> 0; _ -> round(Overhead * 100 / L) end, + io:format("~s~n", [Desc]), + io:format(" Legacy: ~p bytes ~w~n", [L, LegacyBin]), + Show = binary:part(DerBin, 0, min(18, D)), + io:format(" DER: ~p bytes ~w~n", [D, Show]), + io:format(" Overhead: +~p bytes (~p%)~n~n", [Overhead, Pct]) + end, + + %% Case 1: tag+vsn + small int + small binary + T1 = [{foo,int},{bar,binary}], + V1 = [{foo,1},{bar,<<2>>}], + Leg1 = gmser_chain_objects:serialize(account, 1, T1, V1), + DerVal1 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"foo">>, {intValue, 1}}, + {'TemplateField', <<"bar">>, {binaryValue, <<2>>}} + ]}}, + {ok, Der1} = 'GajumaruSerialization':encode('GajumaruData', DerVal1), + Compare("Case 1: tag+vsn + small int + tiny binary (2 fields)", Leg1, Der1), + + %% Case 2: zero + empty binary + V2 = [{foo,0},{bar,<<>>}], + Leg2 = gmser_chain_objects:serialize(account, 1, T1, V2), + DerVal2 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"foo">>, {intValue, 0}}, + {'TemplateField', <<"bar">>, {binaryValue, <<>>}} + ]}}, + {ok, Der2} = 'GajumaruSerialization':encode('GajumaruData', DerVal2), + Compare("Case 2: zero int + empty binary", Leg2, Der2), + + %% Case 3: list of ints + T3 = [{xs,[int]}], + V3 = [{xs,[1,2,3]}], + Leg3 = gmser_chain_objects:serialize(account, 1, T3, V3), + DerVal3 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"xs">>, {listValue, [ + {intValue,1},{intValue,2},{intValue,3} + ]}} + ]}}, + {ok, Der3} = 'GajumaruSerialization':encode('GajumaruData', DerVal3), + Compare("Case 3: list of 3 small ints", Leg3, Der3), + + %% Case 4: tuple (int, binary) + T4 = [{p,{int,binary}}], + V4 = [{p,{42,<<"hi">>}}], + Leg4 = gmser_chain_objects:serialize(account, 1, T4, V4), + DerVal4 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"p">>, {tupleValue, [ + {intValue,42}, {binaryValue,<<"hi">>} + ]}} + ]}}, + {ok, Der4} = 'GajumaruSerialization':encode('GajumaruData', DerVal4), + Compare("Case 4: fixed tuple (int + 2-byte binary)", Leg4, Der4), + + %% Case 5: 256-byte payload + Bin5 = crypto:strong_rand_bytes(256), + T5 = [{data,binary}], + V5 = [{data,Bin5}], + Leg5 = gmser_chain_objects:serialize(account, 1, T5, V5), + DerVal5 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"data">>, {binaryValue, Bin5}} + ]}}, + {ok, Der5} = 'GajumaruSerialization':encode('GajumaruData', DerVal5), + Compare("Case 5: 256-byte binary payload only", Leg5, Der5), + + %% Case 6: Concrete SignedTx style (no field names in DER) + T6 = [{signatures,[binary]},{tx,binary}], + V6 = [{signatures,[<<"sig1">>,<<"sig2">>]},{tx,<<"txbody123">>}], + Leg6 = gmser_chain_objects:serialize(signed_tx, 1, T6, V6), + DerVal6 = {'GajumaruData', 11, 1, {signedTx, {'SignedTx', + [<<"sig1">>, <<"sig2">>], <<"txbody123">>}}}, + {ok, Der6} = 'GajumaruSerialization':encode('GajumaruData', DerVal6), + Compare("Case 6: SignedTx-like (concrete DER, no names)", Leg6, Der6), + + %% Case 7: 33-byte id-like value + IdBin = <<1, 0:256>>, + T7 = [{owner,binary}], + V7 = [{owner,IdBin}], + Leg7 = gmser_chain_objects:serialize(account, 1, T7, V7), + DerVal7 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"owner">>, {binaryValue, IdBin}} + ]}}, + {ok, Der7} = 'GajumaruSerialization':encode('GajumaruData', DerVal7), + Compare("Case 7: 33-byte value (id-like)", Leg7, Der7), + + %% Case 8: Deeper nesting / more fields + T8 = [{a,int},{b,binary},{c,[int]},{d,{int,int}}], + V8 = [{a,123456},{b,<<"abcdef">>},{c,[10,20,30]},{d,{7,8}}], + Leg8 = gmser_chain_objects:serialize(account, 1, T8, V8), + DerVal8 = {'GajumaruData', 10, 1, {templateFields, [ + {'TemplateField', <<"a">>, {intValue, 123456}}, + {'TemplateField', <<"b">>, {binaryValue, <<"abcdef">>}}, + {'TemplateField', <<"c">>, {listValue, [{intValue,10},{intValue,20},{intValue,30}]}}, + {'TemplateField', <<"d">>, {tupleValue, [{intValue,7},{intValue,8}]}} + ]}}, + {ok, Der8} = 'GajumaruSerialization':encode('GajumaruData', DerVal8), + Compare("Case 8: 4 fields mixed (int, bin, list, tuple)", Leg8, Der8), + + io:format("=== Analysis ===~n"), + io:format("Note: Generic templateFields path includes IA5String field names.~n"), + io:format("Concrete types (e.g. SignedTx) avoid name overhead.~n"), + ok.