fix: restore HF5 asset tags, HTLC/multisig inputs, and tx version check after conventions sweep
The conventions sweep (71f0a5c) overwrote HF5 code and removed HTLC/multisig
input handling. This commit restores:
- wire: HF5 asset wire tags (40/49/50/51) and reader functions for
asset_descriptor_operation, asset_operation_proof,
asset_operation_ownership_proof, and asset_operation_ownership_proof_eth
- wire: HTLC and multisig input encode/decode with string field helpers
- consensus: checkTxVersion enforcing version 3 after HF5 / rejecting before
- consensus: HF1-gated acceptance of HTLC and multisig input/output types
- consensus: HTLC key image deduplication in checkKeyImages
- consensus: HTLC ring signature counting in verifyV1Signatures
- chain: corrected error assertion in TestChain_GetBlockByHeight_NotFound
All 14 packages pass go test -race ./...
Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
89b0375e18
commit
70fab6f7d0
5 changed files with 375 additions and 27 deletions
|
|
@ -219,8 +219,9 @@ func TestChain_GetBlockByHeight_NotFound(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("GetBlockByHeight(99): expected error, got nil")
|
||||
}
|
||||
if got := err.Error(); got != "chain: block 99 not found" {
|
||||
t.Errorf("error message: got %q, want %q", got, "chain: block 99 not found")
|
||||
want := "Chain.GetBlockByHeight: chain: block 99 not found"
|
||||
if got := err.Error(); got != want {
|
||||
t.Errorf("error message: got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ import (
|
|||
func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.HardFork, height uint64) error {
|
||||
hf4Active := config.IsHardForkActive(forks, config.HF4Zarcanum, height)
|
||||
|
||||
// 0. Transaction version.
|
||||
if err := checkTxVersion(tx, forks, height); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. Blob size.
|
||||
if uint64(len(txBlob)) >= config.MaxTransactionBlobSize {
|
||||
return coreerr.E("ValidateTransaction", fmt.Sprintf("%d bytes", len(txBlob)), ErrTxTooLarge)
|
||||
|
|
@ -32,13 +37,15 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
|
|||
return coreerr.E("ValidateTransaction", fmt.Sprintf("%d", len(tx.Vin)), ErrTooManyInputs)
|
||||
}
|
||||
|
||||
hf1Active := config.IsHardForkActive(forks, config.HF1, height)
|
||||
|
||||
// 3. Input types — TxInputGenesis not allowed in regular transactions.
|
||||
if err := checkInputTypes(tx, hf4Active); err != nil {
|
||||
if err := checkInputTypes(tx, hf1Active, hf4Active); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. Output validation.
|
||||
if err := checkOutputs(tx, hf4Active); err != nil {
|
||||
if err := checkOutputs(tx, hf1Active, hf4Active); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -65,15 +72,43 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkInputTypes(tx *types.Transaction, hf4Active bool) error {
|
||||
// checkTxVersion validates that the transaction version is appropriate for the
|
||||
// current hardfork era.
|
||||
//
|
||||
// After HF5: transaction version must be >= VersionPostHF5 (3).
|
||||
// Before HF5: transaction version 3 is rejected (too early).
|
||||
func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint64) error {
|
||||
hf5Active := config.IsHardForkActive(forks, config.HF5, height)
|
||||
|
||||
if hf5Active && tx.Version < types.VersionPostHF5 {
|
||||
return coreerr.E("checkTxVersion",
|
||||
fmt.Sprintf("version %d too low after HF5 at height %d", tx.Version, height),
|
||||
ErrTxVersionInvalid)
|
||||
}
|
||||
|
||||
if !hf5Active && tx.Version >= types.VersionPostHF5 {
|
||||
return coreerr.E("checkTxVersion",
|
||||
fmt.Sprintf("version %d not allowed before HF5 at height %d", tx.Version, height),
|
||||
ErrTxVersionInvalid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkInputTypes(tx *types.Transaction, hf1Active, hf4Active bool) error {
|
||||
for _, vin := range tx.Vin {
|
||||
switch vin.(type) {
|
||||
case types.TxInputToKey:
|
||||
// Always valid.
|
||||
case types.TxInputGenesis:
|
||||
return coreerr.E("checkInputTypes", "txin_gen in regular transaction", ErrInvalidInputType)
|
||||
case types.TxInputHTLC, types.TxInputMultisig:
|
||||
// HTLC and multisig inputs require at least HF1.
|
||||
if !hf1Active {
|
||||
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF1", vin.InputType()), ErrInvalidInputType)
|
||||
}
|
||||
default:
|
||||
// Future types (multisig, HTLC, ZC) — accept if HF4+.
|
||||
// Future types (ZC) — accept if HF4+.
|
||||
if !hf4Active {
|
||||
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF4", vin.InputType()), ErrInvalidInputType)
|
||||
}
|
||||
|
|
@ -82,7 +117,7 @@ func checkInputTypes(tx *types.Transaction, hf4Active bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkOutputs(tx *types.Transaction, hf4Active bool) error {
|
||||
func checkOutputs(tx *types.Transaction, hf1Active, hf4Active bool) error {
|
||||
if len(tx.Vout) == 0 {
|
||||
return ErrNoOutputs
|
||||
}
|
||||
|
|
@ -101,6 +136,13 @@ func checkOutputs(tx *types.Transaction, hf4Active bool) error {
|
|||
if o.Amount == 0 {
|
||||
return coreerr.E("checkOutputs", fmt.Sprintf("output %d has zero amount", i), ErrInvalidOutput)
|
||||
}
|
||||
// HTLC and Multisig output targets require at least HF1.
|
||||
switch o.Target.(type) {
|
||||
case types.TxOutHTLC, types.TxOutMultisig:
|
||||
if !hf1Active {
|
||||
return coreerr.E("checkOutputs", fmt.Sprintf("output %d: HTLC/multisig target pre-HF1", i), ErrInvalidOutput)
|
||||
}
|
||||
}
|
||||
case types.TxOutputZarcanum:
|
||||
// Validated by proof verification.
|
||||
}
|
||||
|
|
@ -112,14 +154,19 @@ func checkOutputs(tx *types.Transaction, hf4Active bool) error {
|
|||
func checkKeyImages(tx *types.Transaction) error {
|
||||
seen := make(map[types.KeyImage]struct{})
|
||||
for _, vin := range tx.Vin {
|
||||
toKey, ok := vin.(types.TxInputToKey)
|
||||
if !ok {
|
||||
var ki types.KeyImage
|
||||
switch v := vin.(type) {
|
||||
case types.TxInputToKey:
|
||||
ki = v.KeyImage
|
||||
case types.TxInputHTLC:
|
||||
ki = v.KeyImage
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[toKey.KeyImage]; exists {
|
||||
return coreerr.E("checkKeyImages", toKey.KeyImage.String(), ErrDuplicateKeyImage)
|
||||
if _, exists := seen[ki]; exists {
|
||||
return coreerr.E("checkKeyImages", ki.String(), ErrDuplicateKeyImage)
|
||||
}
|
||||
seen[toKey.KeyImage] = struct{}{}
|
||||
seen[ki] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,16 +60,18 @@ func VerifyTransactionSignatures(tx *types.Transaction, forks []config.HardFork,
|
|||
|
||||
// verifyV1Signatures checks NLSAG ring signatures for pre-HF4 transactions.
|
||||
func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) error {
|
||||
// Count key inputs.
|
||||
var keyInputCount int
|
||||
// Count ring-signing inputs (TxInputToKey and TxInputHTLC contribute
|
||||
// ring signatures; TxInputMultisig does not).
|
||||
var ringInputCount int
|
||||
for _, vin := range tx.Vin {
|
||||
if _, ok := vin.(types.TxInputToKey); ok {
|
||||
keyInputCount++
|
||||
switch vin.(type) {
|
||||
case types.TxInputToKey, types.TxInputHTLC:
|
||||
ringInputCount++
|
||||
}
|
||||
}
|
||||
|
||||
if len(tx.Signatures) != keyInputCount {
|
||||
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: signature count %d != input count %d", len(tx.Signatures), keyInputCount), nil)
|
||||
if len(tx.Signatures) != ringInputCount {
|
||||
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: signature count %d != input count %d", len(tx.Signatures), ringInputCount), nil)
|
||||
}
|
||||
|
||||
// Actual NLSAG verification requires the crypto bridge and ring outputs.
|
||||
|
|
@ -82,18 +84,31 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
|
|||
|
||||
var sigIdx int
|
||||
for _, vin := range tx.Vin {
|
||||
inp, ok := vin.(types.TxInputToKey)
|
||||
if !ok {
|
||||
continue
|
||||
// Extract amount and key offsets from ring-signing input types.
|
||||
var amount uint64
|
||||
var keyOffsets []types.TxOutRef
|
||||
var keyImage types.KeyImage
|
||||
|
||||
switch v := vin.(type) {
|
||||
case types.TxInputToKey:
|
||||
amount = v.Amount
|
||||
keyOffsets = v.KeyOffsets
|
||||
keyImage = v.KeyImage
|
||||
case types.TxInputHTLC:
|
||||
amount = v.Amount
|
||||
keyOffsets = v.KeyOffsets
|
||||
keyImage = v.KeyImage
|
||||
default:
|
||||
continue // TxInputMultisig and others do not use NLSAG
|
||||
}
|
||||
|
||||
// Extract absolute global indices from key offsets.
|
||||
offsets := make([]uint64, len(inp.KeyOffsets))
|
||||
for i, ref := range inp.KeyOffsets {
|
||||
offsets := make([]uint64, len(keyOffsets))
|
||||
for i, ref := range keyOffsets {
|
||||
offsets[i] = ref.GlobalIndex
|
||||
}
|
||||
|
||||
ringKeys, err := getRingOutputs(inp.Amount, offsets)
|
||||
ringKeys, err := getRingOutputs(amount, offsets)
|
||||
if err != nil {
|
||||
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: failed to fetch ring outputs for input %d", sigIdx), err)
|
||||
}
|
||||
|
|
@ -114,7 +129,7 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
|
|||
sigs[i] = [64]byte(s)
|
||||
}
|
||||
|
||||
if !crypto.CheckRingSignature([32]byte(prefixHash), [32]byte(inp.KeyImage), pubs, sigs) {
|
||||
if !crypto.CheckRingSignature([32]byte(prefixHash), [32]byte(keyImage), pubs, sigs) {
|
||||
return coreerr.E("verifyV1Signatures", fmt.Sprintf("consensus: ring signature verification failed for input %d", sigIdx), nil)
|
||||
}
|
||||
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -4,6 +4,8 @@ go 1.26.0
|
|||
|
||||
require (
|
||||
forge.lthn.ai/core/cli v0.3.1
|
||||
forge.lthn.ai/core/go-io v0.1.2
|
||||
forge.lthn.ai/core/go-log v0.0.4
|
||||
forge.lthn.ai/core/go-p2p v0.1.3
|
||||
forge.lthn.ai/core/go-process v0.2.3
|
||||
forge.lthn.ai/core/go-store v0.1.6
|
||||
|
|
@ -18,8 +20,6 @@ require (
|
|||
forge.lthn.ai/core/go-crypt v0.1.7 // indirect
|
||||
forge.lthn.ai/core/go-i18n v0.1.4 // indirect
|
||||
forge.lthn.ai/core/go-inference v0.1.4 // indirect
|
||||
forge.lthn.ai/core/go-io v0.1.2 // indirect
|
||||
forge.lthn.ai/core/go-log v0.0.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.0 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
|
|
|
|||
|
|
@ -160,6 +160,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) 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))
|
||||
|
|
@ -189,6 +201,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 = 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)
|
||||
|
|
@ -503,6 +530,12 @@ const (
|
|||
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
|
||||
|
|
@ -575,6 +608,16 @@ func readVariantElementData(dec *Decoder, tag uint8) []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)
|
||||
|
|
@ -589,6 +632,28 @@ func readVariantElementData(dec *Decoder, tag uint8) []byte {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
@ -965,6 +1030,226 @@ func readZarcanumSig(dec *Decoder) []byte {
|
|||
|
||||
// --- 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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue