Add readExtraAliasEntryOld (tag 20) and readExtraAliasEntry (tag 33) wire readers so the node can deserialise blocks containing alias registrations. Without these readers, mainnet sync would fail on any block with an alias transaction. Three round-trip tests validate the new readers. Also apply AX-2 (comments as usage examples) across 12 files: add concrete usage-example comments to exported functions in config/, types/, wire/, chain/, difficulty/, and consensus/. Fix stale doc in consensus/doc.go that incorrectly referenced *config.ChainConfig. Co-Authored-By: Virgil <virgil@lethean.io>
1264 lines
34 KiB
Go
1264 lines
34 KiB
Go
// 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 (
|
|
"fmt"
|
|
|
|
coreerr "dappco.re/go/core/log"
|
|
|
|
"dappco.re/go/core/blockchain/types"
|
|
)
|
|
|
|
// EncodeTransactionPrefix serialises the transaction prefix (without
|
|
// signatures, attachment, or proofs) in the consensus wire format.
|
|
//
|
|
// The version field determines the field ordering:
|
|
//
|
|
// v0/v1: version, vin, vout, extra
|
|
// v2+: version, vin, extra, vout, [hardfork_id]
|
|
func EncodeTransactionPrefix(enc *Encoder, tx *types.Transaction) {
|
|
enc.WriteVarint(tx.Version)
|
|
if tx.Version <= types.VersionPreHF4 {
|
|
encodePrefixV1(enc, tx)
|
|
} else {
|
|
encodePrefixV2(enc, tx)
|
|
}
|
|
}
|
|
|
|
// EncodeTransaction serialises a full transaction including suffix fields.
|
|
func EncodeTransaction(enc *Encoder, tx *types.Transaction) {
|
|
EncodeTransactionPrefix(enc, tx)
|
|
if tx.Version <= types.VersionPreHF4 {
|
|
encodeSuffixV1(enc, tx)
|
|
} else {
|
|
encodeSuffixV2(enc, tx)
|
|
}
|
|
}
|
|
|
|
// DecodeTransactionPrefix deserialises a transaction prefix.
|
|
func DecodeTransactionPrefix(dec *Decoder) types.Transaction {
|
|
var tx types.Transaction
|
|
tx.Version = dec.ReadVarint()
|
|
if dec.Err() != nil {
|
|
return tx
|
|
}
|
|
if tx.Version <= types.VersionPreHF4 {
|
|
decodePrefixV1(dec, &tx)
|
|
} else {
|
|
decodePrefixV2(dec, &tx)
|
|
}
|
|
return tx
|
|
}
|
|
|
|
// DecodeTransaction deserialises a full transaction.
|
|
func DecodeTransaction(dec *Decoder) types.Transaction {
|
|
tx := DecodeTransactionPrefix(dec)
|
|
if dec.Err() != nil {
|
|
return tx
|
|
}
|
|
if tx.Version <= types.VersionPreHF4 {
|
|
decodeSuffixV1(dec, &tx)
|
|
} else {
|
|
decodeSuffixV2(dec, &tx)
|
|
}
|
|
return tx
|
|
}
|
|
|
|
// --- v0/v1 prefix ---
|
|
|
|
func encodePrefixV1(enc *Encoder, tx *types.Transaction) {
|
|
encodeInputs(enc, tx.Vin)
|
|
encodeOutputsV1(enc, tx.Vout)
|
|
enc.WriteBytes(tx.Extra) // raw wire bytes including varint count prefix
|
|
}
|
|
|
|
func decodePrefixV1(dec *Decoder, tx *types.Transaction) {
|
|
tx.Vin = decodeInputs(dec)
|
|
tx.Vout = decodeOutputsV1(dec)
|
|
tx.Extra = decodeRawVariantVector(dec)
|
|
}
|
|
|
|
// --- v2+ prefix ---
|
|
|
|
func encodePrefixV2(enc *Encoder, tx *types.Transaction) {
|
|
encodeInputs(enc, tx.Vin)
|
|
enc.WriteBytes(tx.Extra)
|
|
encodeOutputsV2(enc, tx.Vout)
|
|
if tx.Version >= types.VersionPostHF5 {
|
|
enc.WriteUint8(tx.HardforkID)
|
|
}
|
|
}
|
|
|
|
func decodePrefixV2(dec *Decoder, tx *types.Transaction) {
|
|
tx.Vin = decodeInputs(dec)
|
|
tx.Extra = decodeRawVariantVector(dec)
|
|
tx.Vout = decodeOutputsV2(dec)
|
|
if tx.Version >= types.VersionPostHF5 {
|
|
tx.HardforkID = dec.ReadUint8()
|
|
}
|
|
}
|
|
|
|
// --- v0/v1 suffix (signatures + attachment) ---
|
|
|
|
func encodeSuffixV1(enc *Encoder, tx *types.Transaction) {
|
|
enc.WriteVarint(uint64(len(tx.Signatures)))
|
|
for _, ring := range tx.Signatures {
|
|
enc.WriteVarint(uint64(len(ring)))
|
|
for i := range ring {
|
|
enc.WriteBlob64((*[64]byte)(&ring[i]))
|
|
}
|
|
}
|
|
enc.WriteBytes(tx.Attachment)
|
|
}
|
|
|
|
func decodeSuffixV1(dec *Decoder, tx *types.Transaction) {
|
|
sigCount := dec.ReadVarint()
|
|
if sigCount > 0 && dec.Err() == nil {
|
|
tx.Signatures = make([][]types.Signature, sigCount)
|
|
for i := uint64(0); i < sigCount; i++ {
|
|
ringSize := dec.ReadVarint()
|
|
if ringSize > 0 && dec.Err() == nil {
|
|
tx.Signatures[i] = make([]types.Signature, ringSize)
|
|
for j := uint64(0); j < ringSize; j++ {
|
|
dec.ReadBlob64((*[64]byte)(&tx.Signatures[i][j]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tx.Attachment = decodeRawVariantVector(dec)
|
|
}
|
|
|
|
// --- v2+ suffix (attachment + signatures + proofs) ---
|
|
|
|
func encodeSuffixV2(enc *Encoder, tx *types.Transaction) {
|
|
enc.WriteBytes(tx.Attachment)
|
|
enc.WriteBytes(tx.SignaturesRaw)
|
|
enc.WriteBytes(tx.Proofs)
|
|
}
|
|
|
|
func decodeSuffixV2(dec *Decoder, tx *types.Transaction) {
|
|
tx.Attachment = decodeRawVariantVector(dec)
|
|
tx.SignaturesRaw = decodeRawVariantVector(dec)
|
|
tx.Proofs = decodeRawVariantVector(dec)
|
|
}
|
|
|
|
// --- inputs ---
|
|
|
|
func encodeInputs(enc *Encoder, vin []types.TxInput) {
|
|
enc.WriteVarint(uint64(len(vin)))
|
|
for _, in := range vin {
|
|
enc.WriteVariantTag(in.InputType())
|
|
switch v := in.(type) {
|
|
case types.TxInputGenesis:
|
|
enc.WriteVarint(v.Height)
|
|
case types.TxInputToKey:
|
|
enc.WriteVarint(v.Amount)
|
|
encodeKeyOffsets(enc, v.KeyOffsets)
|
|
enc.WriteBlob32((*[32]byte)(&v.KeyImage))
|
|
enc.WriteBytes(v.EtcDetails)
|
|
case types.TxInputHTLC:
|
|
// Wire order: HTLCOrigin (string) is serialised before parent fields.
|
|
encodeStringField(enc, v.HTLCOrigin)
|
|
enc.WriteVarint(v.Amount)
|
|
encodeKeyOffsets(enc, v.KeyOffsets)
|
|
enc.WriteBlob32((*[32]byte)(&v.KeyImage))
|
|
enc.WriteBytes(v.EtcDetails)
|
|
case types.TxInputMultisig:
|
|
enc.WriteVarint(v.Amount)
|
|
enc.WriteBlob32((*[32]byte)(&v.MultisigOutID))
|
|
enc.WriteVarint(v.SigsCount)
|
|
enc.WriteBytes(v.EtcDetails)
|
|
case types.TxInputZC:
|
|
encodeKeyOffsets(enc, v.KeyOffsets)
|
|
enc.WriteBlob32((*[32]byte)(&v.KeyImage))
|
|
enc.WriteBytes(v.EtcDetails)
|
|
default:
|
|
enc.err = coreerr.E("encodeInputs", fmt.Sprintf("wire: unsupported input type %T", in), nil)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func decodeInputs(dec *Decoder) []types.TxInput {
|
|
n := dec.ReadVarint()
|
|
if n == 0 || dec.Err() != nil {
|
|
return nil
|
|
}
|
|
vin := make([]types.TxInput, 0, n)
|
|
for i := uint64(0); i < n; i++ {
|
|
tag := dec.ReadVariantTag()
|
|
if dec.Err() != nil {
|
|
return vin
|
|
}
|
|
switch tag {
|
|
case types.InputTypeGenesis:
|
|
vin = append(vin, types.TxInputGenesis{Height: dec.ReadVarint()})
|
|
case types.InputTypeToKey:
|
|
var in types.TxInputToKey
|
|
in.Amount = dec.ReadVarint()
|
|
in.KeyOffsets = decodeKeyOffsets(dec)
|
|
dec.ReadBlob32((*[32]byte)(&in.KeyImage))
|
|
in.EtcDetails = decodeRawVariantVector(dec)
|
|
vin = append(vin, in)
|
|
case types.InputTypeHTLC:
|
|
var in types.TxInputHTLC
|
|
in.HTLCOrigin = decodeStringField(dec)
|
|
in.Amount = dec.ReadVarint()
|
|
in.KeyOffsets = decodeKeyOffsets(dec)
|
|
dec.ReadBlob32((*[32]byte)(&in.KeyImage))
|
|
in.EtcDetails = decodeRawVariantVector(dec)
|
|
vin = append(vin, in)
|
|
case types.InputTypeMultisig:
|
|
var in types.TxInputMultisig
|
|
in.Amount = dec.ReadVarint()
|
|
dec.ReadBlob32((*[32]byte)(&in.MultisigOutID))
|
|
in.SigsCount = dec.ReadVarint()
|
|
in.EtcDetails = decodeRawVariantVector(dec)
|
|
vin = append(vin, in)
|
|
case types.InputTypeZC:
|
|
var in types.TxInputZC
|
|
in.KeyOffsets = decodeKeyOffsets(dec)
|
|
dec.ReadBlob32((*[32]byte)(&in.KeyImage))
|
|
in.EtcDetails = decodeRawVariantVector(dec)
|
|
vin = append(vin, in)
|
|
default:
|
|
dec.err = coreerr.E("decodeInputs", fmt.Sprintf("wire: unsupported input tag 0x%02x", tag), nil)
|
|
return vin
|
|
}
|
|
}
|
|
return vin
|
|
}
|
|
|
|
// --- key offsets (txout_ref_v variant vector) ---
|
|
|
|
func encodeKeyOffsets(enc *Encoder, refs []types.TxOutRef) {
|
|
enc.WriteVarint(uint64(len(refs)))
|
|
for _, ref := range refs {
|
|
enc.WriteVariantTag(ref.Tag)
|
|
switch ref.Tag {
|
|
case types.RefTypeGlobalIndex:
|
|
enc.WriteUint64LE(ref.GlobalIndex)
|
|
case types.RefTypeByID:
|
|
enc.WriteBlob32((*[32]byte)(&ref.TxID))
|
|
enc.WriteVarint(ref.N)
|
|
}
|
|
}
|
|
}
|
|
|
|
func decodeKeyOffsets(dec *Decoder) []types.TxOutRef {
|
|
n := dec.ReadVarint()
|
|
if n == 0 || dec.Err() != nil {
|
|
return nil
|
|
}
|
|
refs := make([]types.TxOutRef, n)
|
|
for i := uint64(0); i < n; i++ {
|
|
refs[i].Tag = dec.ReadVariantTag()
|
|
switch refs[i].Tag {
|
|
case types.RefTypeGlobalIndex:
|
|
refs[i].GlobalIndex = dec.ReadUint64LE()
|
|
case types.RefTypeByID:
|
|
dec.ReadBlob32((*[32]byte)(&refs[i].TxID))
|
|
refs[i].N = dec.ReadVarint()
|
|
default:
|
|
dec.err = coreerr.E("decodeKeyOffsets", fmt.Sprintf("wire: unsupported ref tag 0x%02x", refs[i].Tag), nil)
|
|
return refs
|
|
}
|
|
}
|
|
return refs
|
|
}
|
|
|
|
func encodeTxOutTarget(enc *Encoder, target types.TxOutTarget, context string) bool {
|
|
switch t := target.(type) {
|
|
case types.TxOutToKey:
|
|
enc.WriteVariantTag(types.TargetTypeToKey)
|
|
enc.WriteBlob32((*[32]byte)(&t.Key))
|
|
enc.WriteUint8(t.MixAttr)
|
|
case types.TxOutMultisig:
|
|
enc.WriteVariantTag(types.TargetTypeMultisig)
|
|
enc.WriteVarint(t.MinimumSigs)
|
|
enc.WriteVarint(uint64(len(t.Keys)))
|
|
for i := range t.Keys {
|
|
enc.WriteBlob32((*[32]byte)(&t.Keys[i]))
|
|
}
|
|
case types.TxOutHTLC:
|
|
enc.WriteVariantTag(types.TargetTypeHTLC)
|
|
enc.WriteBlob32((*[32]byte)(&t.HTLCHash))
|
|
enc.WriteUint8(t.Flags)
|
|
enc.WriteVarint(t.Expiration)
|
|
enc.WriteBlob32((*[32]byte)(&t.PKRedeem))
|
|
enc.WriteBlob32((*[32]byte)(&t.PKRefund))
|
|
default:
|
|
enc.err = coreerr.E(context, fmt.Sprintf("wire: unsupported output target type %T", target), nil)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func decodeTxOutTarget(dec *Decoder, tag uint8, context string) types.TxOutTarget {
|
|
switch tag {
|
|
case types.TargetTypeToKey:
|
|
var t types.TxOutToKey
|
|
dec.ReadBlob32((*[32]byte)(&t.Key))
|
|
t.MixAttr = dec.ReadUint8()
|
|
return t
|
|
case types.TargetTypeMultisig:
|
|
var t types.TxOutMultisig
|
|
t.MinimumSigs = dec.ReadVarint()
|
|
keyCount := dec.ReadVarint()
|
|
t.Keys = make([]types.PublicKey, keyCount)
|
|
for i := uint64(0); i < keyCount; i++ {
|
|
dec.ReadBlob32((*[32]byte)(&t.Keys[i]))
|
|
}
|
|
return t
|
|
case types.TargetTypeHTLC:
|
|
var t types.TxOutHTLC
|
|
dec.ReadBlob32((*[32]byte)(&t.HTLCHash))
|
|
t.Flags = dec.ReadUint8()
|
|
t.Expiration = dec.ReadVarint()
|
|
dec.ReadBlob32((*[32]byte)(&t.PKRedeem))
|
|
dec.ReadBlob32((*[32]byte)(&t.PKRefund))
|
|
return t
|
|
default:
|
|
dec.err = coreerr.E(context, fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), nil)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// --- outputs ---
|
|
|
|
// encodeOutputsV1 serialises v0/v1 outputs. In v0/v1, outputs are tx_out_bare
|
|
// directly without an outer tx_out_v variant tag.
|
|
func encodeOutputsV1(enc *Encoder, vout []types.TxOutput) {
|
|
enc.WriteVarint(uint64(len(vout)))
|
|
for _, out := range vout {
|
|
switch v := out.(type) {
|
|
case types.TxOutputBare:
|
|
enc.WriteVarint(v.Amount)
|
|
// Target is a variant (txout_target_v)
|
|
if !encodeTxOutTarget(enc, v.Target, "encodeOutputsV1") {
|
|
return
|
|
}
|
|
default:
|
|
enc.err = coreerr.E("encodeOutputsV1", fmt.Sprintf("wire: unsupported output type %T", out), nil)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func decodeOutputsV1(dec *Decoder) []types.TxOutput {
|
|
n := dec.ReadVarint()
|
|
if n == 0 || dec.Err() != nil {
|
|
return nil
|
|
}
|
|
vout := make([]types.TxOutput, 0, n)
|
|
for i := uint64(0); i < n; i++ {
|
|
var out types.TxOutputBare
|
|
out.Amount = dec.ReadVarint()
|
|
tag := dec.ReadVariantTag()
|
|
if dec.Err() != nil {
|
|
return vout
|
|
}
|
|
out.Target = decodeTxOutTarget(dec, tag, "decodeOutputsV1")
|
|
if dec.Err() != nil {
|
|
return vout
|
|
}
|
|
vout = append(vout, out)
|
|
}
|
|
return vout
|
|
}
|
|
|
|
// encodeOutputsV2 serialises v2+ outputs with outer tx_out_v variant tags.
|
|
func encodeOutputsV2(enc *Encoder, vout []types.TxOutput) {
|
|
enc.WriteVarint(uint64(len(vout)))
|
|
for _, out := range vout {
|
|
enc.WriteVariantTag(out.OutputType())
|
|
switch v := out.(type) {
|
|
case types.TxOutputBare:
|
|
enc.WriteVarint(v.Amount)
|
|
if !encodeTxOutTarget(enc, v.Target, "encodeOutputsV2") {
|
|
return
|
|
}
|
|
case types.TxOutputZarcanum:
|
|
enc.WriteBlob32((*[32]byte)(&v.StealthAddress))
|
|
enc.WriteBlob32((*[32]byte)(&v.ConcealingPoint))
|
|
enc.WriteBlob32((*[32]byte)(&v.AmountCommitment))
|
|
enc.WriteBlob32((*[32]byte)(&v.BlindedAssetID))
|
|
enc.WriteUint64LE(v.EncryptedAmount)
|
|
enc.WriteUint8(v.MixAttr)
|
|
default:
|
|
enc.err = coreerr.E("encodeOutputsV2", fmt.Sprintf("wire: unsupported output type %T", out), nil)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func decodeOutputsV2(dec *Decoder) []types.TxOutput {
|
|
n := dec.ReadVarint()
|
|
if n == 0 || dec.Err() != nil {
|
|
return nil
|
|
}
|
|
vout := make([]types.TxOutput, 0, n)
|
|
for i := uint64(0); i < n; i++ {
|
|
tag := dec.ReadVariantTag()
|
|
if dec.Err() != nil {
|
|
return vout
|
|
}
|
|
switch tag {
|
|
case types.OutputTypeBare:
|
|
var out types.TxOutputBare
|
|
out.Amount = dec.ReadVarint()
|
|
targetTag := dec.ReadVariantTag()
|
|
out.Target = decodeTxOutTarget(dec, targetTag, "decodeOutputsV2")
|
|
if dec.Err() != nil {
|
|
return vout
|
|
}
|
|
vout = append(vout, out)
|
|
case types.OutputTypeZarcanum:
|
|
var out types.TxOutputZarcanum
|
|
dec.ReadBlob32((*[32]byte)(&out.StealthAddress))
|
|
dec.ReadBlob32((*[32]byte)(&out.ConcealingPoint))
|
|
dec.ReadBlob32((*[32]byte)(&out.AmountCommitment))
|
|
dec.ReadBlob32((*[32]byte)(&out.BlindedAssetID))
|
|
out.EncryptedAmount = dec.ReadUint64LE()
|
|
out.MixAttr = dec.ReadUint8()
|
|
vout = append(vout, out)
|
|
default:
|
|
dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported output tag 0x%02x", tag), nil)
|
|
return vout
|
|
}
|
|
}
|
|
return vout
|
|
}
|
|
|
|
// --- raw variant vector encoding ---
|
|
// These helpers handle variant vectors stored as opaque raw bytes.
|
|
// The raw bytes include the varint count prefix and all tagged elements.
|
|
|
|
// decodeRawVariantVector reads a variant vector from the decoder and returns
|
|
// the raw wire bytes (including the varint count prefix). This enables
|
|
// bit-identical round-tripping of extra, attachment, etc_details, and proofs
|
|
// without needing to understand every variant type.
|
|
//
|
|
// For each element, the tag byte determines how to find the element boundary.
|
|
// Known fixed-size tags are skipped by size; unknown tags cause an error.
|
|
func decodeRawVariantVector(dec *Decoder) []byte {
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Read the count and capture the varint bytes.
|
|
count := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
if count == 0 {
|
|
return EncodeVarint(0) // just the count prefix
|
|
}
|
|
|
|
// Build the raw bytes: start with the count varint.
|
|
raw := EncodeVarint(count)
|
|
|
|
for i := uint64(0); i < count; i++ {
|
|
tag := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, tag)
|
|
|
|
data := readVariantElementData(dec, tag)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, data...)
|
|
}
|
|
|
|
return raw
|
|
}
|
|
|
|
// Variant element tags from SET_VARIANT_TAGS (currency_basic.h:1249-1322).
|
|
// These are used by readVariantElementData to determine element boundaries
|
|
// when reading raw variant vectors (extra, attachment, etc_details).
|
|
const (
|
|
tagTxComment = 7 // tx_comment — string
|
|
tagTxPayerOld = 8 // tx_payer_old — 2 public keys
|
|
tagString = 9 // std::string — string
|
|
tagTxCryptoChecksum = 10 // tx_crypto_checksum — two uint32 LE
|
|
tagTxDerivationHint = 11 // tx_derivation_hint — string
|
|
tagTxServiceAttachment = 12 // tx_service_attachment — 3 strings + vector<key> + uint8
|
|
tagUnlockTime = 14 // etc_tx_details_unlock_time — varint
|
|
tagExpirationTime = 15 // etc_tx_details_expiration_time — varint
|
|
tagTxDetailsFlags = 16 // etc_tx_details_flags — varint
|
|
tagSignedParts = 17 // signed_parts — two varints (n_outs, n_extras)
|
|
tagExtraAttachmentInfo = 18 // extra_attachment_info — string + hash + varint
|
|
tagExtraUserData = 19 // extra_user_data — string
|
|
tagExtraAliasEntryOld = 20 // extra_alias_entry_old — complex
|
|
tagExtraPadding = 21 // extra_padding — vector<uint8>
|
|
tagPublicKey = 22 // crypto::public_key — 32 bytes
|
|
tagEtcTxFlags16 = 23 // etc_tx_flags16_t — uint16 LE
|
|
tagUint16 = 24 // uint16_t — uint16 LE
|
|
tagUint64 = 26 // uint64_t — varint
|
|
tagEtcTxTime = 27 // etc_tx_time — varint
|
|
tagUint32 = 28 // uint32_t — uint32 LE
|
|
tagTxReceiverOld = 29 // tx_receiver_old — 2 public keys
|
|
tagUnlockTime2 = 30 // etc_tx_details_unlock_time2 — vector of entries
|
|
tagTxPayer = 31 // tx_payer — 2 keys + optional flag
|
|
tagTxReceiver = 32 // tx_receiver — 2 keys + optional flag
|
|
tagExtraAliasEntry = 33 // extra_alias_entry — complex
|
|
tagZarcanumTxDataV1 = 39 // zarcanum_tx_data_v1 — varint (fee)
|
|
|
|
// Signature variant tags (signature_v).
|
|
tagNLSAGSig = 42 // NLSAG_sig — vector<signature>
|
|
tagZCSig = 43 // ZC_sig — 2 public_keys + CLSAG_GGX
|
|
tagVoidSig = 44 // void_sig — empty
|
|
tagZarcanumSig = 45 // zarcanum_sig — complex
|
|
|
|
// Asset operation tags (HF5 confidential assets).
|
|
tagAssetDescriptorOperation = types.AssetDescriptorOperationTag // asset_descriptor_operation
|
|
tagAssetOperationProof = 49 // asset_operation_proof
|
|
tagAssetOperationOwnershipProof = 50 // asset_operation_ownership_proof
|
|
tagAssetOperationOwnershipProofETH = 51 // asset_operation_ownership_proof_eth
|
|
|
|
// Proof variant tags (proof_v).
|
|
tagZCAssetSurjectionProof = 46 // vector<BGE_proof_s>
|
|
tagZCOutsRangeProof = 47 // bpp_serialized + aggregation_proof
|
|
tagZCBalanceProof = 48 // generic_double_schnorr_sig_s (96 bytes)
|
|
)
|
|
|
|
// readVariantElementData reads the data portion of a variant element (after the
|
|
// tag byte) and returns the raw bytes. The tag determines the expected size.
|
|
func readVariantElementData(dec *Decoder, tag uint8) []byte {
|
|
switch tag {
|
|
// 32-byte fixed blob
|
|
case tagPublicKey:
|
|
return dec.ReadBytes(32)
|
|
|
|
// String fields (varint length + bytes)
|
|
case tagTxComment, tagString, tagTxDerivationHint, tagExtraUserData:
|
|
return readStringBlob(dec)
|
|
|
|
// Varint fields (structs with VARINT_FIELD)
|
|
case tagUnlockTime, tagExpirationTime, tagTxDetailsFlags, tagEtcTxTime:
|
|
v := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
return EncodeVarint(v)
|
|
|
|
// Fixed-size integer fields
|
|
case tagUint64: // raw uint64_t — do_serialize → serialize_int → 8-byte LE
|
|
return dec.ReadBytes(8)
|
|
case tagTxCryptoChecksum: // two uint32 LE
|
|
return dec.ReadBytes(8)
|
|
case tagUint32: // uint32 LE
|
|
return dec.ReadBytes(4)
|
|
case tagSignedParts: // two varints: n_outs + n_extras
|
|
return readSignedParts(dec)
|
|
case tagEtcTxFlags16, tagUint16: // uint16 LE
|
|
return dec.ReadBytes(2)
|
|
|
|
// Vector of uint8 (varint count + bytes)
|
|
case tagExtraPadding:
|
|
return readVariantVectorFixed(dec, 1)
|
|
|
|
// Struct types: 2 public keys (64 bytes)
|
|
case tagTxPayerOld, tagTxReceiverOld:
|
|
return dec.ReadBytes(64)
|
|
|
|
// Struct types: 2 public keys + optional flag
|
|
case tagTxPayer, tagTxReceiver:
|
|
return readTxPayer(dec)
|
|
|
|
// Alias types
|
|
case tagExtraAliasEntryOld:
|
|
return readExtraAliasEntryOld(dec)
|
|
case tagExtraAliasEntry:
|
|
return readExtraAliasEntry(dec)
|
|
|
|
// Composite types
|
|
case tagExtraAttachmentInfo:
|
|
return readExtraAttachmentInfo(dec)
|
|
case tagUnlockTime2:
|
|
return readUnlockTime2(dec)
|
|
case tagTxServiceAttachment:
|
|
return readTxServiceAttachment(dec)
|
|
|
|
// Zarcanum extra variant
|
|
case tagZarcanumTxDataV1: // fee — FIELD(fee) → serialize_int → 8-byte LE
|
|
return dec.ReadBytes(8)
|
|
|
|
// Signature variants
|
|
case tagNLSAGSig: // vector<signature> (64 bytes each)
|
|
return readVariantVectorFixed(dec, 64)
|
|
case tagZCSig: // 2 public_keys + CLSAG_GGX_serialized
|
|
return readZCSig(dec)
|
|
case tagVoidSig: // empty struct
|
|
return []byte{}
|
|
case tagZarcanumSig: // complex: 10 scalars + bppe + public_key + CLSAG_GGXXG
|
|
return readZarcanumSig(dec)
|
|
|
|
// Asset operation variants (HF5)
|
|
case tagAssetDescriptorOperation:
|
|
return readAssetDescriptorOperation(dec)
|
|
case tagAssetOperationProof:
|
|
return readAssetOperationProof(dec)
|
|
case tagAssetOperationOwnershipProof:
|
|
return readAssetOperationOwnershipProof(dec)
|
|
case tagAssetOperationOwnershipProofETH:
|
|
return readAssetOperationOwnershipProofETH(dec)
|
|
|
|
// Proof variants
|
|
case tagZCAssetSurjectionProof: // vector<BGE_proof_s>
|
|
return readZCAssetSurjectionProof(dec)
|
|
case tagZCOutsRangeProof: // bpp_serialized + aggregation_proof
|
|
return readZCOutsRangeProof(dec)
|
|
case tagZCBalanceProof: // generic_double_schnorr_sig_s (3 scalars = 96 bytes)
|
|
return dec.ReadBytes(96)
|
|
|
|
default:
|
|
dec.err = coreerr.E("readVariantElementData", fmt.Sprintf("wire: unsupported variant tag 0x%02x (%d)", tag, tag), nil)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// encodeStringField writes a string as a varint length prefix followed by
|
|
// the UTF-8 bytes.
|
|
func encodeStringField(enc *Encoder, s string) {
|
|
enc.WriteVarint(uint64(len(s)))
|
|
if len(s) > 0 {
|
|
enc.WriteBytes([]byte(s))
|
|
}
|
|
}
|
|
|
|
// decodeStringField reads a varint-prefixed string and returns the Go string.
|
|
func decodeStringField(dec *Decoder) string {
|
|
length := dec.ReadVarint()
|
|
if dec.err != nil || length == 0 {
|
|
return ""
|
|
}
|
|
data := dec.ReadBytes(int(length))
|
|
if dec.err != nil {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
// readStringBlob reads a varint-prefixed string and returns the raw bytes
|
|
// including the length varint.
|
|
func readStringBlob(dec *Decoder) []byte {
|
|
length := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw := EncodeVarint(length)
|
|
if length > 0 {
|
|
data := dec.ReadBytes(int(length))
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, data...)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// readVariantVectorFixed reads a vector of fixed-size elements and returns
|
|
// the raw bytes including the count varint.
|
|
func readVariantVectorFixed(dec *Decoder, elemSize int) []byte {
|
|
count := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw := EncodeVarint(count)
|
|
if count > 0 {
|
|
data := dec.ReadBytes(int(count) * elemSize)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, data...)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// readExtraAttachmentInfo reads the extra_attachment_info variant (tag 18).
|
|
// Structure: cnt_type (string) + hash (32 bytes) + sz (varint).
|
|
func readExtraAttachmentInfo(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// cnt_type: string
|
|
str := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, str...)
|
|
// hash: 32 bytes
|
|
h := dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, h...)
|
|
// sz: varint
|
|
v := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, EncodeVarint(v)...)
|
|
return raw
|
|
}
|
|
|
|
// readUnlockTime2 reads etc_tx_details_unlock_time2 (tag 30).
|
|
// Structure: vector of {varint unlock_time, varint output_index}.
|
|
func readUnlockTime2(dec *Decoder) []byte {
|
|
count := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw := EncodeVarint(count)
|
|
for i := uint64(0); i < count; i++ {
|
|
v1 := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, EncodeVarint(v1)...)
|
|
v2 := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, EncodeVarint(v2)...)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// readTxPayer reads tx_payer / tx_receiver (tags 31 / 32).
|
|
// Structure: spend_public_key (32 bytes) + view_public_key (32 bytes)
|
|
// + optional_field marker. In the binary_archive, the optional is
|
|
// serialised as uint8(1)+data or uint8(0) for empty.
|
|
func readTxPayer(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// Two public keys
|
|
keys := dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, keys...)
|
|
// is_auditable flag (optional_field serialised as uint8 presence + data)
|
|
marker := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, marker)
|
|
if marker != 0 {
|
|
b := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// readTxServiceAttachment reads tx_service_attachment (tag 12).
|
|
// Structure: service_id (string) + instruction (string) + body (string)
|
|
// + security (vector<public_key>) + flags (uint8).
|
|
func readTxServiceAttachment(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// Three string fields
|
|
for range 3 {
|
|
s := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, s...)
|
|
}
|
|
// security: vector<crypto::public_key> (varint count + 32*N bytes)
|
|
v := readVariantVectorFixed(dec, 32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// flags: uint8
|
|
b := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b)
|
|
return raw
|
|
}
|
|
|
|
// readExtraAliasEntryOld reads extra_alias_entry_old (tag 20).
|
|
// Structure: alias(string) + address(spend_key(32) + view_key(32)) +
|
|
// text_comment(string) + sign(vector of generic_schnorr_sig_s, each 64 bytes).
|
|
func readExtraAliasEntryOld(dec *Decoder) []byte {
|
|
var raw []byte
|
|
|
|
// m_alias: string
|
|
alias := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, alias...)
|
|
|
|
// m_address: spend_public_key(32) + view_public_key(32) = 64 bytes
|
|
addr := dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, addr...)
|
|
|
|
// m_text_comment: string
|
|
comment := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, comment...)
|
|
|
|
// m_sign: vector<generic_schnorr_sig_s> (each is 2 scalars = 64 bytes)
|
|
v := readVariantVectorFixed(dec, 64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
|
|
return raw
|
|
}
|
|
|
|
// readExtraAliasEntry reads extra_alias_entry (tag 33).
|
|
// Structure: alias(string) + address(spend_key(32) + view_key(32) + optional flag) +
|
|
// text_comment(string) + sign(vector of generic_schnorr_sig_s, each 64 bytes) +
|
|
// view_key(optional secret_key, 32 bytes).
|
|
func readExtraAliasEntry(dec *Decoder) []byte {
|
|
var raw []byte
|
|
|
|
// m_alias: string
|
|
alias := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, alias...)
|
|
|
|
// m_address: account_public_address with optional is_auditable flag
|
|
// Same wire format as tx_payer (tag 31): spend_key(32) + view_key(32) + optional
|
|
addr := readTxPayer(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, addr...)
|
|
|
|
// m_text_comment: string
|
|
comment := readStringBlob(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, comment...)
|
|
|
|
// m_sign: vector<generic_schnorr_sig_s> (each is 2 scalars = 64 bytes)
|
|
v := readVariantVectorFixed(dec, 64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
|
|
// m_view_key: optional<crypto::secret_key> — uint8 marker + 32 bytes if present
|
|
marker := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, marker)
|
|
if marker != 0 {
|
|
key := dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, key...)
|
|
}
|
|
|
|
return raw
|
|
}
|
|
|
|
// readSignedParts reads signed_parts (tag 17).
|
|
// Structure: n_outs (varint) + n_extras (varint).
|
|
func readSignedParts(dec *Decoder) []byte {
|
|
v1 := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw := EncodeVarint(v1)
|
|
v2 := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, EncodeVarint(v2)...)
|
|
return raw
|
|
}
|
|
|
|
// --- crypto blob readers ---
|
|
// These read variable-length serialised crypto structures and return raw bytes.
|
|
// All vectors are varint(count) + 32*count bytes (scalars or points).
|
|
|
|
// readVectorOfPoints reads a vector of 32-byte points/scalars.
|
|
// Returns raw bytes including the varint count prefix.
|
|
func readVectorOfPoints(dec *Decoder) []byte {
|
|
return readVariantVectorFixed(dec, 32)
|
|
}
|
|
|
|
// readBPPSerialized reads a bpp_signature_serialized.
|
|
// Wire: vec(L) + vec(R) + A0(32) + A(32) + B(32) + r(32) + s(32) + delta(32).
|
|
func readBPPSerialized(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// L: vector of points
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// R: vector of points
|
|
v = readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// 6 fixed scalars: A0, A, B, r, s, delta
|
|
b := dec.ReadBytes(6 * 32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// readBPPESerialized reads a bppe_signature_serialized.
|
|
// Wire: vec(L) + vec(R) + A0(32) + A(32) + B(32) + r(32) + s(32) + delta_1(32) + delta_2(32).
|
|
func readBPPESerialized(dec *Decoder) []byte {
|
|
var raw []byte
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
v = readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// 7 fixed scalars: A0, A, B, r, s, delta_1, delta_2
|
|
b := dec.ReadBytes(7 * 32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// readBGEProof reads a BGE_proof_s.
|
|
// Wire: A(32) + B(32) + vec(Pk) + vec(f) + y(32) + z(32).
|
|
func readBGEProof(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// A + B: 2 fixed points
|
|
b := dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// Pk: vector of points
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// f: vector of scalars
|
|
v = readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// y + z: 2 fixed scalars
|
|
b = dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// readAggregationProof reads a vector_UG_aggregation_proof_serialized.
|
|
// Wire: vec(commitments) + vec(y0s) + vec(y1s) + c(32).
|
|
func readAggregationProof(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// 3 vectors of points/scalars
|
|
for range 3 {
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
}
|
|
// c: 1 fixed scalar
|
|
b := dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// readCLSAG_GGX reads a CLSAG_GGX_signature_serialized.
|
|
// Wire: c(32) + vec(r_g) + vec(r_x) + K1(32) + K2(32).
|
|
func readCLSAG_GGX(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// c: 1 fixed scalar
|
|
b := dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// r_g, r_x: 2 vectors
|
|
for range 2 {
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
}
|
|
// K1 + K2: 2 fixed points
|
|
b = dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// readCLSAG_GGXXG reads a CLSAG_GGXXG_signature_serialized.
|
|
// Wire: c(32) + vec(r_g) + vec(r_x) + K1(32) + K2(32) + K3(32) + K4(32).
|
|
func readCLSAG_GGXXG(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// c: 1 fixed scalar
|
|
b := dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// r_g, r_x: 2 vectors
|
|
for range 2 {
|
|
v := readVectorOfPoints(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
}
|
|
// K1 + K2 + K3 + K4: 4 fixed points
|
|
b = dec.ReadBytes(128)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
return raw
|
|
}
|
|
|
|
// --- signature variant readers ---
|
|
|
|
// readZCSig reads ZC_sig (tag 43).
|
|
// Wire: pseudo_out_amount_commitment(32) + pseudo_out_blinded_asset_id(32) + CLSAG_GGX.
|
|
func readZCSig(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// 2 public keys
|
|
b := dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// CLSAG_GGX_serialized
|
|
v := readCLSAG_GGX(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
return raw
|
|
}
|
|
|
|
// readZarcanumSig reads zarcanum_sig (tag 45).
|
|
// Wire: d(32) + C(32) + C'(32) + E(32) + c(32) + y0(32) + y1(32) + y2(32) + y3(32) + y4(32)
|
|
//
|
|
// - bppe_serialized + pseudo_out_amount_commitment(32) + CLSAG_GGXXG.
|
|
func readZarcanumSig(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// 10 fixed scalars/points
|
|
b := dec.ReadBytes(10 * 32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// E_range_proof: bppe_signature_serialized
|
|
v := readBPPESerialized(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// pseudo_out_amount_commitment: 1 public key
|
|
b = dec.ReadBytes(32)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
// clsag_ggxxg: CLSAG_GGXXG_signature_serialized
|
|
v = readCLSAG_GGXXG(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
return raw
|
|
}
|
|
|
|
// --- proof variant readers ---
|
|
|
|
// --- HF5 asset operation readers ---
|
|
|
|
// readAssetDescriptorOperation reads asset_descriptor_operation (tag 40).
|
|
// Wire: version(uint8) + operation_type(uint8) + opt_asset_id(uint8 marker
|
|
// + 32 bytes if present) + opt_descriptor(uint8 marker + descriptor if
|
|
// present) + amount_to_emit(uint64 LE) + amount_to_burn(uint64 LE) +
|
|
// etc(vector<uint8>).
|
|
//
|
|
// Descriptor (AssetDescriptorBase): ticker(string) + full_name(string) +
|
|
// total_max_supply(uint64 LE) + current_supply(uint64 LE) +
|
|
// decimal_point(uint8) + meta_info(string) + owner_key(32 bytes) +
|
|
// etc(vector<uint8>).
|
|
func readAssetDescriptorOperation(dec *Decoder) []byte {
|
|
raw, _ := parseAssetDescriptorOperation(dec)
|
|
return raw
|
|
}
|
|
|
|
// readAssetOperationProof reads asset_operation_proof (tag 49).
|
|
// Wire: version(uint8) + generic_schnorr_sig_s(64 bytes) + asset_id(32 bytes)
|
|
// + etc(vector<uint8>).
|
|
func readAssetOperationProof(dec *Decoder) []byte {
|
|
var raw []byte
|
|
|
|
// ver: uint8
|
|
ver := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, ver)
|
|
|
|
// gss: generic_schnorr_sig_s — 2 scalars (s, c) = 64 bytes
|
|
b := dec.ReadBytes(64)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
|
|
// asset_id: 32-byte hash
|
|
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...)
|
|
|
|
return raw
|
|
}
|
|
|
|
// readAssetOperationOwnershipProof reads asset_operation_ownership_proof (tag 50).
|
|
// Wire: version(uint8) + generic_schnorr_sig_s(64 bytes) + etc(vector<uint8>).
|
|
func readAssetOperationOwnershipProof(dec *Decoder) []byte {
|
|
var raw []byte
|
|
|
|
// ver: uint8
|
|
ver := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, ver)
|
|
|
|
// gss: generic_schnorr_sig_s — 2 scalars (s, c) = 64 bytes
|
|
b := dec.ReadBytes(64)
|
|
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...)
|
|
|
|
return raw
|
|
}
|
|
|
|
// readAssetOperationOwnershipProofETH reads asset_operation_ownership_proof_eth
|
|
// (tag 51). Wire: version(uint8) + eth_sig(65 bytes) + etc(vector<uint8>).
|
|
func readAssetOperationOwnershipProofETH(dec *Decoder) []byte {
|
|
var raw []byte
|
|
|
|
// ver: uint8
|
|
ver := dec.ReadUint8()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, ver)
|
|
|
|
// eth_sig: 65 bytes (r=32 + s=32 + v=1)
|
|
b := dec.ReadBytes(65)
|
|
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...)
|
|
|
|
return raw
|
|
}
|
|
|
|
// --- proof variant readers ---
|
|
|
|
// readZCAssetSurjectionProof reads zc_asset_surjection_proof (tag 46).
|
|
// Wire: varint(count) + count * BGE_proof_s.
|
|
func readZCAssetSurjectionProof(dec *Decoder) []byte {
|
|
count := dec.ReadVarint()
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw := EncodeVarint(count)
|
|
for i := uint64(0); i < count; i++ {
|
|
b := readBGEProof(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, b...)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// readZCOutsRangeProof reads zc_outs_range_proof (tag 47).
|
|
// Wire: bpp_signature_serialized + vector_UG_aggregation_proof_serialized.
|
|
func readZCOutsRangeProof(dec *Decoder) []byte {
|
|
var raw []byte
|
|
// bpp
|
|
v := readBPPSerialized(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
// aggregation_proof
|
|
v = readAggregationProof(dec)
|
|
if dec.err != nil {
|
|
return nil
|
|
}
|
|
raw = append(raw, v...)
|
|
return raw
|
|
}
|