// Copyright (c) 2017-2026 Lethean (https://lt.hn) // // Licensed under the European Union Public Licence (EUPL) version 1.2. // SPDX-License-Identifier: EUPL-1.2 package wire import ( "bytes" "fmt" coreerr "dappco.re/go/core/log" "dappco.re/go/core/blockchain/types" ) // VariantElement is one tagged element from a raw variant vector. // Data contains the raw wire bytes for the element payload, without the tag. type VariantElement struct { Tag uint8 Data []byte } // DecodeVariantVector decodes a raw variant vector into tagged raw elements. // It is useful for higher-level validation of raw transaction fields such as // extra, attachment, signatures, and proofs. func DecodeVariantVector(raw []byte) ([]VariantElement, error) { dec := NewDecoder(bytes.NewReader(raw)) count := dec.ReadVarint() if dec.Err() != nil { return nil, dec.Err() } elems := make([]VariantElement, 0, int(count)) for i := uint64(0); i < count; i++ { tag := dec.ReadUint8() if dec.Err() != nil { return nil, coreerr.E("DecodeVariantVector", fmt.Sprintf("read tag %d", i), dec.Err()) } data := readVariantElementData(dec, tag) if dec.Err() != nil { return nil, coreerr.E("DecodeVariantVector", fmt.Sprintf("read element %d", i), dec.Err()) } elems = append(elems, VariantElement{Tag: tag, Data: data}) } return elems, nil } // DecodeAssetDescriptorOperation decodes a raw asset_descriptor_operation // payload into its typed representation. func DecodeAssetDescriptorOperation(raw []byte) (types.AssetDescriptorOperation, error) { dec := NewDecoder(bytes.NewReader(raw)) _, op := parseAssetDescriptorOperation(dec) if dec.Err() != nil { return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "decode asset descriptor operation", dec.Err()) } 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), } }