fix(dx): audit and fix error handling, file I/O, wire compilation
Some checks failed
Security Scan / security (pull_request) Successful in 9s
Test / Test (pull_request) Failing after 23s

- CLAUDE.md: update error handling guidance from fmt.Errorf to coreerr.E()
  and document go-io convention for file I/O
- wire/transaction.go: fix TxOutTarget interface compilation — add
  encodeTarget/decodeTarget helpers with support for TxOutToKey,
  TxOutMultisig, and TxOutHTLC target types
- wire/transaction.go: add HTLC and multisig input encode/decode
  (TxInputHTLC, TxInputMultisig) with string encoding helpers
- wire/transaction.go: add asset operation tag constants (40, 49-51)
  and reader functions for HF5 confidential asset operations
- consensus/block.go: replace fmt.Errorf with coreerr.E() for
  checkBlockVersion and ValidateTransactionInBlock
- chain/ring.go: replace fmt.Errorf with coreerr.E() in GetRingOutputs
- consensus/v2sig_test.go: replace os.ReadFile with coreio.Read
- crypto/*.go: replace all fmt.Errorf and errors.New with coreerr.E()
  across keygen, pow, keyimage, signature, and clsag packages
- types/types_test.go: add tests for HashFromHex, PublicKeyFromHex,
  IsZero, and String methods (types coverage 74.5% -> 89.1%)

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-17 08:34:04 +00:00
parent f19054f7b1
commit f27825cfc9
11 changed files with 458 additions and 42 deletions

View file

@ -40,8 +40,8 @@ go test -tags integration ./... # integration tests (need C++ te
- `go test -race ./...`, `go vet ./...`, and `go mod tidy` must all pass before commit
- Conventional commits: `type(scope): description`
- Co-Author trailer: `Co-Authored-By: Charon <charon@lethean.io>`
- Error strings: `package: description` format (e.g. `types: invalid hex for hash`)
- Error wrapping: `fmt.Errorf("package: description: %w", err)`
- Error handling: `coreerr.E("Caller", "description", err)` via `coreerr "forge.lthn.ai/core/go-log"` — not `fmt.Errorf` or `errors.New`
- File I/O: `coreio "forge.lthn.ai/core/go-io"` — not `os.ReadFile`/`os.WriteFile`
- Import order: stdlib, then `golang.org/x`, then `forge.lthn.ai`, blank lines between groups
- No emojis in code or comments

View file

@ -38,7 +38,7 @@ func (c *Chain) GetRingOutputs(amount uint64, offsets []uint64) ([]types.PublicK
case types.TxOutputBare:
toKey, ok := out.Target.(types.TxOutToKey)
if !ok {
return nil, fmt.Errorf("ring output %d: unsupported target type %T", i, out.Target)
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: unsupported target type %T", i, out.Target), nil)
}
pubs[i] = toKey.Key
default:

View file

@ -152,8 +152,8 @@ func expectedBlockMajorVersion(forks []config.HardFork, height uint64) uint8 {
func checkBlockVersion(blk *types.Block, forks []config.HardFork, height uint64) error {
expected := expectedBlockMajorVersion(forks, height)
if blk.MajorVersion != expected {
return fmt.Errorf("%w: got %d, want %d at height %d",
ErrBlockMajorVersion, blk.MajorVersion, expected, height)
return coreerr.E("checkBlockVersion", fmt.Sprintf("got %d, want %d at height %d",
blk.MajorVersion, expected, height), ErrBlockMajorVersion)
}
return nil
}
@ -226,7 +226,7 @@ func IsPreHardforkFreeze(forks []config.HardFork, version uint8, height uint64)
func ValidateTransactionInBlock(tx *types.Transaction, txBlob []byte, forks []config.HardFork, height uint64) error {
// Pre-hardfork freeze: reject non-coinbase transactions in the freeze window.
if !isCoinbase(tx) && IsPreHardforkFreeze(forks, config.HF5, height) {
return fmt.Errorf("%w: height %d is within HF5 freeze window", ErrPreHardforkFreeze, height)
return coreerr.E("ValidateTransactionInBlock", fmt.Sprintf("height %d is within HF5 freeze window", height), ErrPreHardforkFreeze)
}
return ValidateTransaction(tx, txBlob, forks, height)

View file

@ -8,9 +8,11 @@ package consensus
import (
"bytes"
"encoding/hex"
"os"
"strings"
"testing"
coreio "forge.lthn.ai/core/go-io"
"forge.lthn.ai/core/go-blockchain/config"
"forge.lthn.ai/core/go-blockchain/types"
"forge.lthn.ai/core/go-blockchain/wire"
@ -21,10 +23,10 @@ import (
// loadTestTx loads and decodes a hex-encoded transaction from testdata.
func loadTestTx(t *testing.T, filename string) *types.Transaction {
t.Helper()
hexData, err := os.ReadFile(filename)
hexStr, err := coreio.Read(coreio.Local, filename)
require.NoError(t, err, "read %s", filename)
blob, err := hex.DecodeString(string(bytes.TrimSpace(hexData)))
blob, err := hex.DecodeString(strings.TrimSpace(hexStr))
require.NoError(t, err, "decode hex")
dec := wire.NewDecoder(bytes.NewReader(blob))

View file

@ -8,8 +8,9 @@ package crypto
import "C"
import (
"errors"
"unsafe"
coreerr "forge.lthn.ai/core/go-log"
)
// PointMul8 multiplies a curve point by the cofactor 8.
@ -20,7 +21,7 @@ func PointMul8(pk [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&result[0])),
)
if rc != 0 {
return result, errors.New("crypto: point_mul8 failed")
return result, coreerr.E("PointMul8", "point_mul8 failed", nil)
}
return result, nil
}
@ -34,7 +35,7 @@ func PointDiv8(pk [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&result[0])),
)
if rc != 0 {
return result, errors.New("crypto: point_div8 failed")
return result, coreerr.E("PointDiv8", "point_div8 failed", nil)
}
return result, nil
}
@ -48,7 +49,7 @@ func PointSub(a, b [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&result[0])),
)
if rc != 0 {
return result, errors.New("crypto: point_sub failed")
return result, coreerr.E("PointSub", "point_sub failed", nil)
}
return result, nil
}
@ -81,7 +82,7 @@ func GenerateCLSAGGG(hash [32]byte, ring []byte, ringSize int,
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
)
if rc != 0 {
return nil, errors.New("crypto: generate_CLSAG_GG failed")
return nil, coreerr.E("GenerateCLSAGGG", "generate_CLSAG_GG failed", nil)
}
return sig, nil
}

View file

@ -8,9 +8,10 @@ package crypto
import "C"
import (
"errors"
"fmt"
"unsafe"
coreerr "forge.lthn.ai/core/go-log"
)
// GenerateKeys creates a new random key pair.
@ -20,7 +21,7 @@ func GenerateKeys() (pub [32]byte, sec [32]byte, err error) {
(*C.uint8_t)(unsafe.Pointer(&sec[0])),
)
if rc != 0 {
err = fmt.Errorf("crypto: generate_keys failed (rc=%d)", rc)
err = coreerr.E("GenerateKeys", fmt.Sprintf("generate_keys failed (rc=%d)", rc), nil)
}
return
}
@ -33,7 +34,7 @@ func SecretToPublic(sec [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&pub[0])),
)
if rc != 0 {
return pub, fmt.Errorf("crypto: secret_to_public failed (rc=%d)", rc)
return pub, coreerr.E("SecretToPublic", fmt.Sprintf("secret_to_public failed (rc=%d)", rc), nil)
}
return pub, nil
}
@ -52,7 +53,7 @@ func GenerateKeyDerivation(pub [32]byte, sec [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&d[0])),
)
if rc != 0 {
return d, errors.New("crypto: generate_key_derivation failed")
return d, coreerr.E("GenerateKeyDerivation", "generate_key_derivation failed", nil)
}
return d, nil
}
@ -67,7 +68,7 @@ func DerivePublicKey(derivation [32]byte, index uint64, base [32]byte) ([32]byte
(*C.uint8_t)(unsafe.Pointer(&derived[0])),
)
if rc != 0 {
return derived, errors.New("crypto: derive_public_key failed")
return derived, coreerr.E("DerivePublicKey", "derive_public_key failed", nil)
}
return derived, nil
}
@ -82,7 +83,7 @@ func DeriveSecretKey(derivation [32]byte, index uint64, base [32]byte) ([32]byte
(*C.uint8_t)(unsafe.Pointer(&derived[0])),
)
if rc != 0 {
return derived, errors.New("crypto: derive_secret_key failed")
return derived, coreerr.E("DeriveSecretKey", "derive_secret_key failed", nil)
}
return derived, nil
}

View file

@ -8,8 +8,9 @@ package crypto
import "C"
import (
"errors"
"unsafe"
coreerr "forge.lthn.ai/core/go-log"
)
// GenerateKeyImage computes the key image for a public/secret key pair.
@ -22,7 +23,7 @@ func GenerateKeyImage(pub [32]byte, sec [32]byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&ki[0])),
)
if rc != 0 {
return ki, errors.New("crypto: generate_key_image failed")
return ki, coreerr.E("GenerateKeyImage", "generate_key_image failed", nil)
}
return ki, nil
}

View file

@ -10,6 +10,8 @@ import "C"
import (
"fmt"
"unsafe"
coreerr "forge.lthn.ai/core/go-log"
)
// RandomXHash computes the RandomX PoW hash. The key is the cache
@ -23,7 +25,7 @@ func RandomXHash(key, input []byte) ([32]byte, error) {
(*C.uint8_t)(unsafe.Pointer(&output[0])),
)
if ret != 0 {
return output, fmt.Errorf("crypto: RandomX hash failed with code %d", ret)
return output, coreerr.E("RandomXHash", fmt.Sprintf("RandomX hash failed with code %d", ret), nil)
}
return output, nil
}

View file

@ -8,8 +8,9 @@ package crypto
import "C"
import (
"errors"
"unsafe"
coreerr "forge.lthn.ai/core/go-log"
)
// GenerateSignature creates a standard (non-ring) signature.
@ -22,7 +23,7 @@ func GenerateSignature(hash [32]byte, pub [32]byte, sec [32]byte) ([64]byte, err
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
)
if rc != 0 {
return sig, errors.New("crypto: generate_signature failed")
return sig, coreerr.E("GenerateSignature", "generate_signature failed", nil)
}
return sig, nil
}
@ -60,7 +61,7 @@ func GenerateRingSignature(hash [32]byte, image [32]byte, pubs [][32]byte,
(*C.uint8_t)(unsafe.Pointer(&flatSigs[0])),
)
if rc != 0 {
return nil, errors.New("crypto: generate_ring_signature failed")
return nil, coreerr.E("GenerateRingSignature", "generate_ring_signature failed", nil)
}
sigs := make([][64]byte, n)

106
types/types_test.go Normal file
View file

@ -0,0 +1,106 @@
// 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 types
import (
"strings"
"testing"
)
func TestHashFromHex_Good(t *testing.T) {
hexStr := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
h, err := HashFromHex(hexStr)
if err != nil {
t.Fatalf("HashFromHex: unexpected error: %v", err)
}
if h[0] != 0x01 || h[1] != 0x23 {
t.Errorf("HashFromHex: got [0]=%02x [1]=%02x, want 01 23", h[0], h[1])
}
if h.String() != hexStr {
t.Errorf("String: got %q, want %q", h.String(), hexStr)
}
}
func TestHashFromHex_Bad(t *testing.T) {
tests := []struct {
name string
input string
}{
{"short", "0123"},
{"invalid_chars", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"},
{"odd_length", "012"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := HashFromHex(tt.input)
if err == nil {
t.Error("expected error")
}
})
}
}
func TestHash_IsZero_Good(t *testing.T) {
var zero Hash
if !zero.IsZero() {
t.Error("zero hash: IsZero() should be true")
}
nonZero := Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
if nonZero.IsZero() {
t.Error("non-zero hash: IsZero() should be false")
}
}
func TestPublicKeyFromHex_Good(t *testing.T) {
hexStr := strings.Repeat("ab", 32)
pk, err := PublicKeyFromHex(hexStr)
if err != nil {
t.Fatalf("PublicKeyFromHex: unexpected error: %v", err)
}
for i := range pk {
if pk[i] != 0xAB {
t.Fatalf("PublicKeyFromHex: byte %d = %02x, want 0xAB", i, pk[i])
}
}
if pk.String() != hexStr {
t.Errorf("String: got %q, want %q", pk.String(), hexStr)
}
}
func TestPublicKeyFromHex_Bad(t *testing.T) {
_, err := PublicKeyFromHex("tooshort")
if err == nil {
t.Error("expected error for short hex")
}
}
func TestPublicKey_IsZero_Good(t *testing.T) {
var zero PublicKey
if !zero.IsZero() {
t.Error("zero key: IsZero() should be true")
}
nonZero := PublicKey{1}
if nonZero.IsZero() {
t.Error("non-zero key: IsZero() should be false")
}
}
func TestSecretKey_String_Good(t *testing.T) {
sk := SecretKey{0xFF}
s := sk.String()
if !strings.HasPrefix(s, "ff") {
t.Errorf("String: got %q, want prefix ff", s)
}
}
func TestKeyImage_String_Good(t *testing.T) {
ki := KeyImage{0xDE}
s := ki.String()
if !strings.HasPrefix(s, "de") {
t.Errorf("String: got %q, want prefix de", s)
}
}

View file

@ -164,6 +164,18 @@ func encodeInputs(enc *Encoder, vin []types.TxInput) {
encodeKeyOffsets(enc, v.KeyOffsets)
enc.WriteBlob32((*[32]byte)(&v.KeyImage))
enc.WriteBytes(v.EtcDetails)
case types.TxInputHTLC:
// Wire order: HTLCOrigin string BEFORE parent fields (C++ quirk).
encodeString(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)
}
}
}
@ -195,6 +207,21 @@ func decodeInputs(dec *Decoder) []types.TxInput {
dec.ReadBlob32((*[32]byte)(&in.KeyImage))
in.EtcDetails = decodeRawVariantVector(dec)
vin = append(vin, in)
case types.InputTypeHTLC:
var in types.TxInputHTLC
in.HTLCOrigin = decodeString(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)
default:
dec.err = coreerr.E("decodeInputs", fmt.Sprintf("wire: unsupported input tag 0x%02x", tag), nil)
return vin
@ -241,6 +268,87 @@ func decodeKeyOffsets(dec *Decoder) []types.TxOutRef {
return refs
}
// --- string encoding ---
// encodeString writes a varint-prefixed string.
func encodeString(enc *Encoder, s string) {
enc.WriteVarint(uint64(len(s)))
if len(s) > 0 {
enc.WriteBytes([]byte(s))
}
}
// decodeString reads a varint-prefixed string.
func decodeString(dec *Decoder) string {
n := dec.ReadVarint()
if n == 0 || dec.Err() != nil {
return ""
}
data := dec.ReadBytes(int(n))
if dec.Err() != nil {
return ""
}
return string(data)
}
// --- output targets ---
// encodeTarget serialises a txout_target_v variant (tag + fields).
func encodeTarget(enc *Encoder, target types.TxOutTarget) {
enc.WriteVariantTag(target.TargetType())
switch t := target.(type) {
case types.TxOutToKey:
enc.WriteBlob32((*[32]byte)(&t.Key))
enc.WriteUint8(t.MixAttr)
case types.TxOutMultisig:
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.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))
}
}
// decodeTarget deserialises a txout_target_v from the given tag.
// Returns nil for unsupported tags (caller should set dec.err).
func decodeTarget(dec *Decoder, tag uint8) 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()
n := dec.ReadVarint()
if dec.Err() != nil {
return nil
}
t.Keys = make([]types.PublicKey, n)
for i := uint64(0); i < n; 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:
return nil
}
}
// --- outputs ---
// encodeOutputsV1 serialises v0/v1 outputs. In v0/v1, outputs are tx_out_bare
@ -251,10 +359,7 @@ func encodeOutputsV1(enc *Encoder, vout []types.TxOutput) {
switch v := out.(type) {
case types.TxOutputBare:
enc.WriteVarint(v.Amount)
// Target is a variant (txout_target_v)
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&v.Target.Key))
enc.WriteUint8(v.Target.MixAttr)
encodeTarget(enc, v.Target)
}
}
}
@ -272,14 +377,15 @@ func decodeOutputsV1(dec *Decoder) []types.TxOutput {
if dec.Err() != nil {
return vout
}
switch tag {
case types.TargetTypeToKey:
dec.ReadBlob32((*[32]byte)(&out.Target.Key))
out.Target.MixAttr = dec.ReadUint8()
default:
target := decodeTarget(dec, tag)
if dec.Err() != nil {
return vout
}
if target == nil {
dec.err = coreerr.E("decodeOutputsV1", fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), nil)
return vout
}
out.Target = target
vout = append(vout, out)
}
return vout
@ -293,9 +399,7 @@ func encodeOutputsV2(enc *Encoder, vout []types.TxOutput) {
switch v := out.(type) {
case types.TxOutputBare:
enc.WriteVarint(v.Amount)
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&v.Target.Key))
enc.WriteUint8(v.Target.MixAttr)
encodeTarget(enc, v.Target)
case types.TxOutputZarcanum:
enc.WriteBlob32((*[32]byte)(&v.StealthAddress))
enc.WriteBlob32((*[32]byte)(&v.ConcealingPoint))
@ -323,13 +427,18 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
var out types.TxOutputBare
out.Amount = dec.ReadVarint()
targetTag := dec.ReadVariantTag()
if targetTag == types.TargetTypeToKey {
dec.ReadBlob32((*[32]byte)(&out.Target.Key))
out.Target.MixAttr = dec.ReadUint8()
} else {
if dec.Err() != nil {
return vout
}
target := decodeTarget(dec, targetTag)
if dec.Err() != nil {
return vout
}
if target == nil {
dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported target tag 0x%02x", targetTag), nil)
return vout
}
out.Target = target
vout = append(vout, out)
case types.OutputTypeZarcanum:
var out types.TxOutputZarcanum
@ -434,6 +543,14 @@ const (
tagZCAssetSurjectionProof = 46 // vector<BGE_proof_s>
tagZCOutsRangeProof = 47 // bpp_serialized + aggregation_proof
tagZCBalanceProof = 48 // generic_double_schnorr_sig_s (96 bytes)
// Asset operation tags (HF5 confidential assets).
tagAssetOperationProof = 49 // asset_operation_proof
tagAssetOperationOwnershipProof = 50 // asset_operation_ownership_proof
tagAssetOperationOwnershipProofETH = 51 // asset_operation_ownership_proof_eth
// Extra variant tags (asset operations).
tagAssetDescriptorOperation = 40 // asset_descriptor_operation
)
// readVariantElementData reads the data portion of a variant element (after the
@ -510,6 +627,16 @@ func readVariantElementData(dec *Decoder, tag uint8) []byte {
case tagZCBalanceProof: // generic_double_schnorr_sig_s (3 scalars = 96 bytes)
return dec.ReadBytes(96)
// Asset operation variants (HF5)
case tagAssetDescriptorOperation:
return readAssetDescriptorOperation(dec)
case tagAssetOperationProof:
return readAssetOperationProof(dec)
case tagAssetOperationOwnershipProof:
return readAssetOperationOwnershipProof(dec)
case tagAssetOperationOwnershipProofETH:
return readAssetOperationOwnershipProofETH(dec)
default:
dec.err = coreerr.E("readVariantElementData", fmt.Sprintf("wire: unsupported variant tag 0x%02x (%d)", tag, tag), nil)
return nil
@ -928,3 +1055,178 @@ func readZCOutsRangeProof(dec *Decoder) []byte {
raw = append(raw, v...)
return raw
}
// --- asset operation readers (HF5) ---
// readAssetDescriptorOperation reads asset_descriptor_operation (tag 40).
// Wire: ver(uint8) + operation_type(uint8) + opt_asset_id(optional 32-byte hash)
// + opt_descriptor(optional AssetDescriptorBase) + amount_to_emit(uint64 LE)
// + amount_to_burn(uint64 LE) + 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: optional<hash> — uint8 marker, then 32 bytes if present
marker := dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, marker)
if marker != 0 {
b := dec.ReadBytes(32)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
}
// opt_descriptor: optional<AssetDescriptorBase>
marker = dec.ReadUint8()
if dec.err != nil {
return nil
}
raw = append(raw, marker)
if marker != 0 {
// AssetDescriptorBase: ticker(string) + full_name(string)
// + total_max_supply(uint64 LE) + current_supply(uint64 LE)
// + decimal_point(uint8) + meta_info(string) + owner_key(32)
// + etc(vector<uint8>)
for range 2 {
s := readStringBlob(dec)
if dec.err != nil {
return nil
}
raw = append(raw, s...)
}
b := dec.ReadBytes(8 + 8 + 1) // total_max_supply + current_supply + decimal_point
if dec.err != nil {
return nil
}
raw = append(raw, b...)
s := readStringBlob(dec) // meta_info
if dec.err != nil {
return nil
}
raw = append(raw, s...)
b = dec.ReadBytes(32) // owner_key
if dec.err != nil {
return nil
}
raw = append(raw, b...)
v := readVariantVectorFixed(dec, 1) // etc
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: ver(uint8) + gss(generic_schnorr_sig_s = 64 bytes) + asset_id(32 bytes)
// + etc(vector<uint8>).
func readAssetOperationProof(dec *Decoder) []byte {
var raw []byte
// ver: uint8
b := dec.ReadBytes(1)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// gss: generic_schnorr_sig_s (s + c = 64 bytes)
b = dec.ReadBytes(64)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// asset_id: 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...)
return raw
}
// readAssetOperationOwnershipProof reads asset_operation_ownership_proof (tag 50).
// Wire: ver(uint8) + gss(generic_schnorr_sig_s = 64 bytes) + etc(vector<uint8>).
func readAssetOperationOwnershipProof(dec *Decoder) []byte {
var raw []byte
// ver: uint8
b := dec.ReadBytes(1)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// gss: generic_schnorr_sig_s (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: ver(uint8) + eth_sig(65 bytes: r=32 + s=32 + v=1) + etc(vector<uint8>).
func readAssetOperationOwnershipProofETH(dec *Decoder) []byte {
var raw []byte
// ver: uint8
b := dec.ReadBytes(1)
if dec.err != nil {
return nil
}
raw = append(raw, b...)
// eth_sig: 65 bytes
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
}