[agent/claude:opus] DX audit and fix. 1) Review CLAUDE.md — update any outdate... #3
11 changed files with 458 additions and 42 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
106
types/types_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue