go-blockchain/docs/superpowers/specs/2026-03-16-hf5-confidential-assets-design.md
Snider 34128d8e98
Some checks failed
Security Scan / security (pull_request) Successful in 11s
Test / Test (pull_request) Failing after 19s
refactor: migrate module path to dappco.re/go/core/blockchain
Update go.mod module line, all require/replace directives, and every
.go import path from forge.lthn.ai/core/go-blockchain to
dappco.re/go/core/blockchain. Add replace directives to bridge
dappco.re paths to existing forge.lthn.ai registry during migration.
Update CLAUDE.md, README, and docs to reflect the new module path.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 01:49:26 +00:00

6.2 KiB

HF5 Confidential Assets Support

Date: 2026-03-16 Author: Charon Package: dappco.re/go/core/blockchain Status: Draft Depends on: HF1 (types refactor), HF3 (block version), HF4 (Zarcanum — already implemented)

Context

HF5 introduces confidential assets — the ability to deploy, emit, update, and burn custom asset types on the Lethean chain. This is the Zano asset system: every output has a blinded_asset_id that proves (via BGE surjection proofs) it corresponds to a legitimate input asset without revealing which one.

On mainnet, HF5 is at height 999,999,999 (future). On testnet, HF5 activates at height 200.

What's already implemented:

  • BGE surjection proof verification (crypto.VerifyBGE) — crypto bridge done
  • BGE proof parsing (readBGEProof, readZCAssetSurjectionProof) — wire done
  • verifyBGEProofs in consensus/verify.go — verification logic done
  • Transaction version 3 wire format with hardfork_id field — wire done
  • VersionPostHF5 constant and decodePrefixV2 hardfork_id handling — done

What's NOT implemented:

  • Asset operation types in extra/attachment fields
  • Asset descriptor structures
  • Consensus validation for asset operations
  • Pre-hardfork transaction freeze (60 blocks before HF5 activation)
  • Minimum build version enforcement

Scope

Phase A: Asset descriptor types (types/)

New types for the asset_descriptor_operation extra variant:

// AssetDescriptorBase holds the core asset metadata.
type AssetDescriptorBase struct {
    Ticker       string    // max 6 chars
    FullName     string    // max 64 chars
    TotalMaxSupply uint64  // maximum supply cap
    CurrentSupply  uint64  // current circulating supply
    DecimalPoint   uint8   // display precision
    MetaInfo     string    // arbitrary metadata (JSON)
    OwnerKey     PublicKey // asset owner's public key
    // etc: reserved variant vector for future fields
    Etc          []byte   // opaque
}

// AssetDescriptorOperation represents a deploy/emit/update/burn operation.
type AssetDescriptorOperation struct {
    Version       uint8   // currently 0 or 1
    OperationType uint8   // ASSET_DESCRIPTOR_OPERATION_REGISTER, _EMIT, _UPDATE, _BURN, _PUBLIC_BURN
    Descriptor    *AssetDescriptorBase // present for register and update
    AssetID       Hash    // target asset ID (absent for register)
    AmountToEmit  uint64  // for emit operations
    AmountToBurn  uint64  // for burn operations
    Etc           []byte  // opaque
}

Operation type constants:

const (
    AssetOpRegister   uint8 = 0 // deploy new asset
    AssetOpEmit       uint8 = 1 // emit additional supply
    AssetOpUpdate     uint8 = 2 // update metadata
    AssetOpBurn       uint8 = 3 // burn supply (with proof)
    AssetOpPublicBurn uint8 = 4 // burn supply (public amount)
)

Phase B: Wire encoding for asset operations (wire/)

The asset_descriptor_operation appears as a variant element in the tx extra field (tag 40 in the C++ SET_VARIANT_TAGS).

Add to readVariantElementData:

case tagAssetDescriptorOperation (40):
    read version transition header
    read operation_type (uint8)
    read opt_asset_id (optional hash)
    read opt_descriptor (optional AssetDescriptorBase)
    read amount_to_emit/burn (varint)
    read etc (opaque vector)

This is stored as raw bytes in the extra field (same opaque pattern as everything else), but we need the wire reader to not choke on tag 40 during deserialization.

Phase C: Asset operation proof types (wire/)

New proof variant tags for HF5:

tagAssetOperationProof           = 49  // asset_operation_proof
tagAssetOperationOwnershipProof  = 50  // asset_operation_ownership_proof
tagAssetOperationOwnershipETH    = 51  // asset_operation_ownership_proof_eth

Each needs a reader in readVariantElementData. The proof structures contain crypto elements (Schnorr signatures, public keys) that are fixed-size.

Phase D: Consensus validation (consensus/)

Transaction version enforcement:

  • After HF5: transaction version must be 3 (not 2)
  • hardfork_id field must be present and match current hardfork

Pre-hardfork freeze:

  • 60 blocks before HF5 activation, reject non-coinbase transactions
  • config.PreHardforkTxFreezePeriod = 60 already defined

Asset operation validation:

  • Register: descriptor must be valid (ticker length, supply caps, owner key non-zero)
  • Emit: asset_id must exist, caller must prove ownership
  • Update: asset_id must exist, caller must prove ownership
  • Burn: amount must not exceed current supply

Minimum build version:

  • C++ enforces MINIMUM_REQUIRED_BUILD_VERSION = 601 for mainnet, 2 for testnet
  • Go equivalent: reject connections from peers with build version below threshold

Phase E: Asset state tracking (chain/)

Need to track:

  • Asset registry: asset_id → AssetDescriptorBase
  • Current supply per asset
  • Asset ownership proofs

This requires new storage groups in chain/store.go.

What can be deferred

  • Full asset operation validation — complex, needs ownership proof verification. Can accept blocks containing asset operations structurally (wire parsing) without deep validation initially, then add validation incrementally.
  • Asset state tracking — needed for wallet/explorer, not strictly for block sync if we trust the C++ daemon's validation.
  • Wallet asset support — separate design.

Minimum viable HF5: Wire parsing only. Add tag 40 and the asset proof tags to readVariantElementData so the Go node can deserialise HF5 blocks without crashing. Store asset operations as opaque bytes in the extra field (existing pattern). Gate transaction version 3 on HF5.

This follows the same pattern used for extra, attachment, etc_details — opaque bytes for bit-identical round-tripping. Deep validation can layer on top.

Testing

  • Wire round-trip tests with constructed v3 transactions containing asset operations
  • Testnet block parsing past height 200 (HF5 activation)
  • Version enforcement tests (reject v2 after HF5, accept v3)
  • Pre-hardfork freeze tests (reject non-coinbase 60 blocks before activation)

Out of scope

  • Wallet asset management (deploy/emit/burn CLI)
  • Asset explorer UI
  • Asset whitelist management
  • Cross-asset atomic swaps
  • HF6 block time halving (separate spec)