Updated asn1 experiment, now exploring PER and OER
Gajumaru Serialization Tests / tests (push) Successful in 12s
Gajumaru Serialization Tests / tests (push) Successful in 12s
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
-- 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 for portability
|
||||
-- (other languages use ASN.1 compilers to get types/parsers).
|
||||
-- * Define compact canonical wire format using standard ASN.1 techniques
|
||||
-- (primarily Unaligned PER / UPER with constraints for packing).
|
||||
-- * Static encoding only (templates from gmserialization).
|
||||
-- * Legacy RLP compatibility is achieved via separate translation layer
|
||||
-- (deferred in current focus).
|
||||
--
|
||||
-- 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 for compact wire):
|
||||
-- {ok, Mod} = asn1ct:compile(GajumaruSerialization.asn, [uper]).
|
||||
-- {ok, Bytes} = 'GajumaruSerialization':encode('GajumaruData', Asn1Value).
|
||||
-- Bytes is the compact UPER wire format (deterministic with this schema).
|
||||
-- For legacy RLP, use the model-to-RLP layer instead (see gmser_asn1_rlp).
|
||||
--
|
||||
-- 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 {
|
||||
-- Constrained for better PER packing
|
||||
tag INTEGER (0..65535),
|
||||
vsn INTEGER (0..255),
|
||||
content Content
|
||||
}
|
||||
|
||||
-- Preferred top-level for the compact static wire format.
|
||||
-- Avoids the extra Content CHOICE tag when the structure is known to be static.
|
||||
CompactStatic ::= SEQUENCE {
|
||||
tag INTEGER (0..65535),
|
||||
vsn INTEGER (0..255),
|
||||
fields StaticFields
|
||||
}
|
||||
|
||||
-- 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,
|
||||
|
||||
-- Static-optimized: no field names on wire (matches legacy static behavior exactly)
|
||||
-- Preferred for compact wire format of known templates.
|
||||
staticFields [10] StaticFields,
|
||||
|
||||
-- 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
|
||||
}
|
||||
|
||||
-- Optimized for static wire format: just the values in order, no names.
|
||||
-- This matches the legacy static encoding where maps/records are positional only.
|
||||
StaticFields ::= SEQUENCE OF 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
|
||||
-- <<B, _/binary>> 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. For the compact wire format we use UPER (unaligned PER).
|
||||
-- It is stable/deterministic for a given schema (no extensibility
|
||||
-- markers on these types, fixed order).
|
||||
--
|
||||
-- 4. INTEGER uses ASN.1 PER encoding (canonical for the constraints).
|
||||
-- This may differ from legacy RLP minimal unsigned; new format
|
||||
-- will have different hashes (expected when introducing new encoding).
|
||||
--
|
||||
-- 5. Dynamic encoding (gmser_dyn) is not in scope here.
|
||||
--
|
||||
-- 6. To generate the compact wire:
|
||||
-- asn1ct:compile(GajumaruSerialization, [uper]).
|
||||
-- {ok, CompactBytes} = 'GajumaruSerialization':encode('GajumaruData', Value).
|
||||
--
|
||||
-- Use staticFields (not templateFields) for best compactness on static data.
|
||||
|
||||
END
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
%% 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('CompactStatic', {
|
||||
tag,
|
||||
vsn,
|
||||
fields
|
||||
}).
|
||||
|
||||
-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_
|
||||
Reference in New Issue
Block a user