refactor(wire): unify HF5 asset operation parsing
Some checks are pending
Security Scan / security (push) Waiting to run
Test / Test (push) Waiting to run

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Virgil 2026-04-04 23:09:26 +00:00
parent 330ee2a146
commit 123047bebd
3 changed files with 170 additions and 146 deletions

View file

@ -1029,113 +1029,7 @@ func readZarcanumSig(dec *Decoder) []byte {
// decimal_point(uint8) + meta_info(string) + owner_key(32 bytes) +
// etc(vector<uint8>).
func readAssetDescriptorOperation(dec *Decoder) []byte {
var raw []byte
// ver: uint8
ver := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, ver)
// operation_type: uint8
opType := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, opType)
// opt_asset_id: uint8 presence marker + 32 bytes if present
assetMarker := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, assetMarker)
if assetMarker != 0 {
b := dec.ReadBytes(32)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
}
// opt_descriptor: uint8 presence marker + descriptor if present
descMarker := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, descMarker)
if descMarker != 0 {
// AssetDescriptorBase
// ticker: string
s := readStringBlob(dec)
if dec.err != nil {
return nil
}
raw = append(raw, s...)
// full_name: string
s = readStringBlob(dec)
if dec.err != nil {
return nil
}
raw = append(raw, s...)
// total_max_supply: uint64 LE
b := dec.ReadBytes(8)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// current_supply: uint64 LE
b = dec.ReadBytes(8)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// decimal_point: uint8
dp := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, dp)
// meta_info: string
s = readStringBlob(dec)
if dec.err != nil {
return nil
}
raw = append(raw, s...)
// owner_key: 32 bytes
b = dec.ReadBytes(32)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// etc: vector<uint8>
v := readVariantVectorFixed(dec, 1)
if dec.err != nil {
return nil
}
raw = append(raw, v...)
}
// amount_to_emit: uint64 LE
b := dec.ReadBytes(8)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// amount_to_burn: uint64 LE
b = dec.ReadBytes(8)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// etc: vector<uint8>
v := readVariantVectorFixed(dec, 1)
if dec.err != nil {
return nil
}
raw = append(raw, v...)
raw, _ := parseAssetDescriptorOperation(dec)
return raw
}

View file

@ -127,6 +127,23 @@ func TestReadAssetDescriptorOperationEmit_Good(t *testing.T) {
if !bytes.Equal(got, blob) {
t.Fatalf("round-trip mismatch: got %d bytes, want %d bytes", len(got), len(blob))
}
op, err := DecodeAssetDescriptorOperation(blob)
if err != nil {
t.Fatalf("DecodeAssetDescriptorOperation (emit) failed: %v", err)
}
if op.Version != 1 || op.OperationType != 1 {
t.Fatalf("unexpected operation header: %+v", op)
}
if op.Descriptor != nil {
t.Fatalf("emit operation should not carry descriptor: %+v", op)
}
if op.AmountToEmit != 500000 || op.AmountToBurn != 0 {
t.Fatalf("unexpected emit amounts: %+v", op)
}
if op.AssetID[0] != 0xAB || op.AssetID[31] != 0xAB {
t.Fatalf("unexpected asset id: %x", op.AssetID)
}
}
func TestVariantVectorWithTag40_Good(t *testing.T) {

View file

@ -53,45 +53,7 @@ func DecodeVariantVector(raw []byte) ([]VariantElement, error) {
// payload into its typed representation.
func DecodeAssetDescriptorOperation(raw []byte) (types.AssetDescriptorOperation, error) {
dec := NewDecoder(bytes.NewReader(raw))
var op types.AssetDescriptorOperation
op.Version = dec.ReadUint8()
op.OperationType = dec.ReadUint8()
if dec.ReadUint8() != 0 {
assetID := dec.ReadBytes(32)
if dec.Err() != nil {
return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read asset id", dec.Err())
}
copy(op.AssetID[:], assetID)
}
if dec.ReadUint8() != 0 {
desc := &types.AssetDescriptorBase{}
desc.Ticker = decodeStringField(dec)
desc.FullName = decodeStringField(dec)
desc.TotalMaxSupply = dec.ReadUint64LE()
desc.CurrentSupply = dec.ReadUint64LE()
desc.DecimalPoint = dec.ReadUint8()
desc.MetaInfo = decodeStringField(dec)
ownerKey := dec.ReadBytes(32)
if dec.Err() != nil {
return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read owner key", dec.Err())
}
copy(desc.OwnerKey[:], ownerKey)
desc.Etc = readVariantVectorFixed(dec, 1)
if dec.Err() != nil {
return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read descriptor etc", dec.Err())
}
op.Descriptor = desc
}
op.AmountToEmit = dec.ReadUint64LE()
op.AmountToBurn = dec.ReadUint64LE()
op.Etc = readVariantVectorFixed(dec, 1)
if dec.Err() != nil {
return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read trailing etc", dec.Err())
}
_, op := parseAssetDescriptorOperation(dec)
if dec.Err() != nil {
return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "decode asset descriptor operation", dec.Err())
@ -99,3 +61,154 @@ func DecodeAssetDescriptorOperation(raw []byte) (types.AssetDescriptorOperation,
return op, nil
}
// parseAssetDescriptorOperation is the single source of truth for both raw
// wire preservation and typed HF5 asset operation decoding.
func parseAssetDescriptorOperation(dec *Decoder) ([]byte, types.AssetDescriptorOperation) {
var raw []byte
var op types.AssetDescriptorOperation
appendByte := func(v uint8) {
raw = append(raw, v)
}
appendBytes := func(v []byte) {
raw = append(raw, v...)
}
op.Version = dec.ReadUint8()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendByte(op.Version)
op.OperationType = dec.ReadUint8()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendByte(op.OperationType)
assetMarker := dec.ReadUint8()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendByte(assetMarker)
if assetMarker != 0 {
assetID := dec.ReadBytes(32)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
copy(op.AssetID[:], assetID)
appendBytes(assetID)
}
descMarker := dec.ReadUint8()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendByte(descMarker)
if descMarker != 0 {
desc := &types.AssetDescriptorBase{}
tickerRaw := readStringBlob(dec)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
desc.Ticker = decodeStringBlob(tickerRaw)
appendBytes(tickerRaw)
fullNameRaw := readStringBlob(dec)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
desc.FullName = decodeStringBlob(fullNameRaw)
appendBytes(fullNameRaw)
desc.TotalMaxSupply = dec.ReadUint64LE()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(uint64LEBytes(desc.TotalMaxSupply))
desc.CurrentSupply = dec.ReadUint64LE()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(uint64LEBytes(desc.CurrentSupply))
desc.DecimalPoint = dec.ReadUint8()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendByte(desc.DecimalPoint)
metaInfoRaw := readStringBlob(dec)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
desc.MetaInfo = decodeStringBlob(metaInfoRaw)
appendBytes(metaInfoRaw)
ownerKey := dec.ReadBytes(32)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
copy(desc.OwnerKey[:], ownerKey)
appendBytes(ownerKey)
desc.Etc = readVariantVectorFixed(dec, 1)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(desc.Etc)
op.Descriptor = desc
}
op.AmountToEmit = dec.ReadUint64LE()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(uint64LEBytes(op.AmountToEmit))
op.AmountToBurn = dec.ReadUint64LE()
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(uint64LEBytes(op.AmountToBurn))
op.Etc = readVariantVectorFixed(dec, 1)
if dec.Err() != nil {
return nil, types.AssetDescriptorOperation{}
}
appendBytes(op.Etc)
return raw, op
}
func decodeStringBlob(raw []byte) string {
return string(raw[varintPrefixLen(raw):])
}
func varintPrefixLen(raw []byte) int {
n := 0
for n < len(raw) {
n++
if raw[n-1] < 0x80 {
return n
}
}
return len(raw)
}
func uint64LEBytes(v uint64) []byte {
return []byte{
byte(v),
byte(v >> 8),
byte(v >> 16),
byte(v >> 24),
byte(v >> 32),
byte(v >> 40),
byte(v >> 48),
byte(v >> 56),
}
}