Add explicit errors for unknown input/output variants in the wire encoder and tighten transparent output validation in consensus. Cover the new failure paths with unit tests. Co-Authored-By: Charon <charon@lethean.io>
1297 lines
34 KiB
Go
1297 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
|
|
}
|
|
|
|
// --- 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)
|
|
switch t := v.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 k := range t.Keys {
|
|
enc.WriteBlob32((*[32]byte)(&t.Keys[k]))
|
|
}
|
|
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("encodeOutputsV1", fmt.Sprintf("wire: unsupported output target type %T", v.Target), 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
|
|
}
|
|
switch tag {
|
|
case types.TargetTypeToKey:
|
|
var t types.TxOutToKey
|
|
dec.ReadBlob32((*[32]byte)(&t.Key))
|
|
t.MixAttr = dec.ReadUint8()
|
|
out.Target = t
|
|
case types.TargetTypeMultisig:
|
|
var t types.TxOutMultisig
|
|
t.MinimumSigs = dec.ReadVarint()
|
|
keyCount := dec.ReadVarint()
|
|
t.Keys = make([]types.PublicKey, keyCount)
|
|
for k := uint64(0); k < keyCount; k++ {
|
|
dec.ReadBlob32((*[32]byte)(&t.Keys[k]))
|
|
}
|
|
out.Target = 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))
|
|
out.Target = t
|
|
default:
|
|
dec.err = coreerr.E("decodeOutputsV1", fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), 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)
|
|
switch t := v.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 k := range t.Keys {
|
|
enc.WriteBlob32((*[32]byte)(&t.Keys[k]))
|
|
}
|
|
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("encodeOutputsV2", fmt.Sprintf("wire: unsupported output target type %T", v.Target), nil)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
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()
|
|
switch targetTag {
|
|
case types.TargetTypeToKey:
|
|
var t types.TxOutToKey
|
|
dec.ReadBlob32((*[32]byte)(&t.Key))
|
|
t.MixAttr = dec.ReadUint8()
|
|
out.Target = t
|
|
case types.TargetTypeMultisig:
|
|
var t types.TxOutMultisig
|
|
t.MinimumSigs = dec.ReadVarint()
|
|
keyCount := dec.ReadVarint()
|
|
t.Keys = make([]types.PublicKey, keyCount)
|
|
for k := uint64(0); k < keyCount; k++ {
|
|
dec.ReadBlob32((*[32]byte)(&t.Keys[k]))
|
|
}
|
|
out.Target = 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))
|
|
out.Target = t
|
|
default:
|
|
dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported target tag 0x%02x", targetTag), 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 = 40 // 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)
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 {
|
|
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...)
|
|
|
|
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
|
|
}
|