oUTXO: Object Layer (Off-Chain Strong Constraints)

Understanding the ordered UTXO model that bridges Pay2Ingot outputs to object state machines

oUTXO Model

Overview

The oUTXO (object-oriented UTXO) model is an "object state machine" layer that runs off-chain. It organizes the "bytes and policies" provided by Pay2Ingot outputs according to rules, forming an ordered, replayable object graph.

This layer is not written into L1 blocks, but through indexers, wallets, and other client software, as well as publicly available Registry test vectors, the entire community is forced to reach consistent execution.

Why oUTXO?

If we only rely on "byte commitments" on-chain, everyone will get a pile of raw data, but there's no way to guarantee they can be assembled into a stable, consistent object world. For example:

  • Multiple transactions concurrently create different versions of the same objectโ€”which one is "canonical"?
  • Should an object be considered the latest state or already outdated?
  • Without schema FSM constraints, fields in the payload may be written incorrectly.

These problems cannot be delegated to L1, because the consensus layer must remain extremely minimal and only handle hard constraints.

Interaction Principles Diagram

โ‘  oUTXO โ†’ UTXO โ†’ oUTXO

Background and Context

  1. Pay2Ingot (P2I) is a new output format on-chain.

    • It acts as a "container" that commits to a payload (DAG-CBOR encoded bytes), policies (signatures, timelocks, multisig rules), and basic metadata (schema routing, account number, salt, etc.).
    • L1 nodes have limited responsibilities: they only check structural validity, hash matching, signature and timelock satisfaction, and fee adequacy, then decide whether the transaction can be included in a block.
    • However, L1 does not understand the true semantics of the payload. For example, whether this payload represents an FT, an NFT, or a complex contract, L1 does not care at all.
  2. The Problem: If we only rely on these "byte commitments" on-chain, everyone will get a pile of raw data, but there's no way to guarantee they can be assembled into a stable, consistent object world. For example:

    • Multiple transactions concurrently create different versions of the same objectโ€”which one is "canonical"?
    • Should an object be considered the latest state or already outdated?
    • Without schema FSM constraints, fields in the payload may be written incorrectly.

    These problems cannot be delegated to L1, because the consensus layer must remain extremely minimal and only handle hard constraints.

  3. The Solution: oUTXO (ordered UTXO)

    • It is an "object state machine" layer that runs off-chain.
    • It organizes the "bytes and policies" provided by P2I according to rules, forming an ordered, replayable object graph.
    • This layer is not written into L1 blocks, but through indexers, wallets, and other client software, as well as publicly available Registry test vectors, the entire community is forced to reach consistent execution.

Off-Chain oUTXO Layer

L1 On-Chain

references parent_instance_id

Old P2I UTXO

(with HASH_PAYLOAD commitment)

Transaction (spends old UTXO)

New P2I UTXO

(commits to new HASH_PAYLOAD)

oUTXO Instance #1

(old tip)

oUTXO Instance #2

(new tip)

Interpretation

  1. The off-chain oUTXO1 is the previous active version of the object.

  2. It is spent, corresponding to the on-chain UTXO1 โ†’ consumed in transaction TX.

  3. The transaction produces a new UTXO2, committing to a new payload.

  4. The indexer derives a new oUTXO2, becoming the latest tip of this object.


โ‘ก oUTXO โ†’ oUTXO โ†’ oUTXO (Pure Off-Chain Perspective)

oUTXO Instance #1 (initial Mint)

oUTXO Instance #2 (Transfer)

oUTXO Instance #3 (Mutate / Attach)

Interpretation

  • From the off-chain perspective, oUTXO is an "instance chain": Mint โ†’ Transfer โ†’ Mutateโ€ฆ

  • Each instance must "spend old, create new"โ€”the previous one becomes invalid (stale), and the next becomes the tip.

  • Conflicts are resolved through (blue_score, block_id, wtxid) lexicographic ordering: first compare blue_score (ascending); if equal, compare block_id (block hash byte string lexicographic order, big-endian); then compare wtxid (transaction hash including witness, byte string lexicographic order, big-endian).

โ‘ข Three-Phase Interaction: oUTXO โ†’ oUTXO โ†’ UTXO โ†’ oUTXO

Off-Chain oUTXO Layer

L1 On-Chain

Off-Chain oUTXO Layer

oUTXO #1 (Mint)

oUTXO #2 (Transfer)

Transaction (spends #2) creates new P2I UTXO

New P2I UTXO (commits HASH_PAYLOAD)

oUTXO #3 (new tip)


๐Ÿ“Œ Summary:

  • oUTXO โ†’ UTXO โ†’ oUTXO: Explains how on-chain commitments are consumed and re-derived off-chain.
  • oUTXO โ†’ oUTXO โ†’ oUTXO: Emphasizes the generational evolution of object instances, like a family tree.

UTXO โ†” oUTXO Mapping Relationship

Core Principle: Each Pay2Ingot UTXO typically derives one oUTXO instance; but due to unknown schemas (raw_data), concurrency/reorgs causing stale instances, invisible windows for unlocked timelocks, and other situations, off-chain there may exist a one-to-many relationship of "one active instance + several historical/invalid instances". The oUTXO layer always guarantees that each artifact_id has at most one active tip.

1. Basic Case (Ideal Path)

  • Each Pay2Ingot UTXO (on-chain output) is parsed by the indexer and typically derives one oUTXO instance.
  • This derived instance carries artifact_id / instance_id and other off-chain identifiers, entering the oUTXO state graph.
  • ๐Ÿ‘‰ Under normal circumstances, we can say UTXO โ†” oUTXO instance โ‰ˆ one-to-one correspondence.

2. Special Cases (Leading to "Non-One-to-One")

  1. Unknown/Unregistered Schema

    • On-chain P2I UTXO still exists, but off-chain cannot run FSM.
    • The indexer marks it as raw_data, not entering executable object semantics.
    • You can understand it as "UTXO exists on-chain, but off-chain there's only a pending data record, not a true oUTXO instance".
  2. Conflict Resolution (Concurrency or Duplicate Instances)

    • Two transactions may concurrently produce seemingly identical instances (e.g., different successors of the same artifact).
    • Off-chain rules will determine one as the tip, the other marked as stale.
    • Here the UTXO still exists on-chain, but off-chain only recognizes one as "active oUTXO".
    • So there will be 1 on-chain UTXO โ†’ 1 stale oUTXO (historical record, inactive) situation.
  3. Timelock Not Unlocked

    • On-chain P2I UTXO has been produced, but timelock hasn't expired.
    • Off-chain instance will be counted as "exists but invisible" (INDEXER_TIMELOCKED), not entering the active tip set.

3. Reverse Angle: Must oUTXO Have a UTXO?

  • Yes, oUTXO instances must have an on-chain UTXO foundation.
  • oUTXO is derived from on-chain commitments (HASH_PAYLOAD, SALT, and other fields), cannot be generated out of thin air.
  • However: a UTXO may be parsed as "raw_data" or "stale", in which case its oUTXO is not considered an active instance.

โœ… Summary

  • Default Case: One P2I UTXO โ†’ one oUTXO instance (active or historical).
  • Exception Cases:
    • Unknown schema โ†’ only raw_data, placeholder but not a complete instance;
    • Concurrency/conflict โ†’ one active instance + several stale instances;
    • Timelock not met โ†’ instance exists, but temporarily not entering the active set.

Therefore, UTXO and oUTXO are not strictly one-to-one, but a "one-to-one or one-to-many (active+historical/invalid)" mapping relationship.


โ‘  Data Structure Overview (Fields and Normalization Standards)

References

Spends this output

HASH_PAYLOAD commitment

Derived from header fields

Defines signature message

Contains previous output data

AUTH_SIGS signature message domain

ยซL1 Output Header / Consensus Validation Objectยป

Pay2IngotOutput

+ VER: u8 = 0x01 %% Version gate; unknown versions fail-closed

+ HASH_PAYLOAD: [32]

+ LOCK: Lock %% None / PubKey / ScriptHash / MastOnly; L1 strict validation

+ SALT: [32] %% Instance anchoring: one input to instance_id

+ LEN: u32(LE) %% Payload normalization byte limit(โ‰ค MAX_PAYLOAD)

+ FLAGS: u16(LE)

+ SCHEMA_ID: [32] %% Semantic routing(off-chain FSM) : , L1 only checks length

+ MAST_ROOT?: [32] %% Optional from v1.1; v1.0 rejects if present(hard fork)

ยซL1 Input Witness / Consensus Validation Objectยป

Witness

+ REVEAL_FLAG: u8

+ PAYLOAD_HASH:[32]

+ PAYLOAD?: bytes %% Revealed payload bytes(if REVEAL_REQUIRED=true)

+ AUTH_SIGS: Sig[] %% CopperootSchnorr(v1.0), +StandardSchnorr(v1.1)

+ MAST_PATH?: node[](โ‰ค8)

ยซLock Types (Finite Enum)ยป

Lock

+ NONE: "No lock"

+ PubKey: "Public key lock"

+ ScriptHash: "Script hash lock"

+ MastOnly: "MAST-only lock"

ยซOff-Chain Normalized Bytes / But Hash is Committed on L1ยป

CanonicalPayload

+ Encoding: DAG-CBOR deterministic encoding

+ canonical_bytes: bytes

+ HASH_PAYLOAD = blake3("ingot/payload" || canonical_bytes)

ยซOff-Chain Derived Identifiers (Object/Instance)ยป

Ids

+ artifact_id = blake3("ingot/artifact" || SCHEMA_ID || HASH_PAYLOAD)

+ instance_id = blake3("ingot/instance" || artifact_id || outpoint_bytes)

ยซSignature Message Domain (L1 Strong Constraint)ยป

Sighash

+ SigMsg: SignatureMessage

ยซSignature Message Structureยป

SignatureMessage

+ domain: "Ingot/SigMsg/v1"

+ network_id: bytes

+ wtxid: bytes32

+ input_index: u32

+ spent_prevout: PrevoutData

+ lock_bytes: bytes

+ hash_payload: bytes32

ยซPrevious Output Dataยป

PrevoutData

+ txid: bytes32

+ vout: u32LE

+ amount: u64LE

This diagram is like a "dictionary," placing all key structures together: Pay2Ingot output header (on-chain fields), witness, lock, normalized payload, derived identifiers, signature message domain, etc.

  • Output Header (Pay2IngotOutput): The core on-chain commitment. It specifies payload size limit, hash, flags, lock, multisig/timelock rules, etc. Here HASH_PAYLOAD calculation is unified to blake3("ingot/payload" || canonical_bytes), avoiding ambiguity.
  • Witness: Must be submitted when spending P2I output, containing payload data (if revealed), payload hash, signatures, and optional MAST path. It proves that the "on-chain committed payload" is indeed fully revealed and signatures are correct.
  • Lock: Describes unlock rules, L1 nodes strictly check (e.g., timelock not met directly rejects).
  • CanonicalPayload: Refers to the DAG-CBOR normalized byte sequence, all hash calculations are based on it.
  • Ids (artifact_id / instance_id): Off-chain derived identifiers, describing "what object this is" and "its specific version".
  • SigMsg: Signature-bound message domain, ensuring signatures are strongly bound to payload, input, lock, wtxid, preventing replay attacks.
  • Unique Active Anchor: For any artifact_id, at any height there is at most one active tip (active UTXO).
  • Spend Old, Create New: Any object change must spend the previous generation tip and produce a new tip.
  • Lexicographic Resolution: Concurrent conflicts resolved by (blue_score โ†“, block_id โ†‘, wtxid โ†‘) uniqueness.
  • Replayable: Scan from genesis Pay2Ingot โ†’ reassemble payload โ†’ execute oUTXO rules and schema FSM โ†’ get the same state.
  • Clear Boundary with L1: Timelock/signature/fees and other hard conditions in L1; oUTXO only handles object semantics and resolution.

โ‘ก Identifier and Object Layer Relationships (ER Diagram)

One object, multiple instances (version evolution)

One instance corresponds to one on-chain P2I output

Except Mint, must spend parent tip

ARTIFACT

bytes32

artifact_id

= blake3('ingot/artifact'||SCHEMA_ID||HASH_PAYLOAD)

bytes32

schema_id

Routes to FSM (off-chain)

INSTANCE

bytes32

instance_id

= blake3('ingot/instance'||artifact_id||outpoint_bytes)

bytes32

outpoint

Instance anchoring; length and formula validated separately in L1/off-chain

bool

tip_flag

Unique active anchor after oUTXO resolution

P2I_OUTPUT

uint8

ver

uint32

len_LE

bytes32

hash_payload

Unified: blake3('ingot/payload'||canonical_bytes)

uint16

flags_LE

lock

lock

bytes32

schema_id

bytes32

salt

PARENT_INSTANCE

This diagram shows the object layer (oUTXO) identity and evolution logic.

  • Artifact: Equivalent to the "class" of an object, identified by artifact_id. It binds to a schema (defining rules and semantics).
  • Instance: A specific version of the object, identified by instance_id. Each change produces a new instance.
  • P2I Output: On-chain physical commitment, each instance corresponds to one P2I output.
  • Parent Instance: Except Mint (first generation of object), other operations must "spend parent instance", ensuring the version chain is a single chain, not arbitrarily forked.

In concurrent situations, two different instances may simultaneously try to inherit the same parent. At this point, oUTXO uses a unified resolution rule (blue_score โ†“, block_id โ†‘, wtxid โ†‘) to select the unique legal tip, the other marked as stale.

One-sentence summary: This diagram tells you that objects in the oUTXO world are like a family tree: artifact is the surname, instance is a specific generation, must spend old and create new, conflicts retain only one canonical version.

โ‘ข On-Chain / Off-Chain Boundary and Interaction (System Component View)

Off-Chain Object Layer oUTXO ยท Indexer/Wallet/SDK

L1 Consensus Layer Full Node/Miner

|HASH_PAYLOAD commitment consistency (domain separation constant)|

|Signature anchors input/lock/commitment (non-replayable)|

|Provides wtxid / blue_score|

Type Identification

โ€” OP_PAY2INGOT

โ€” Unknown type: fail-closed

Structure/Boundary Validation

โ€” REVEAL_REQUIRED flag

โ€” Payload size limits

โ€” Field length checks

Hash Consistency

โ€” Payload hash matching

โ€” canonical_bytes normalization

โ€” blake3('ingot/payload' || canonical_bytes) == witness.PAYLOAD_HASH == output.HASH_PAYLOAD

Lock and Signature

โ€” LOCK: timelock/multisig etc.

โ€” SigMsg binding: network_id + wtxid + input_index + prevout + lock_bytes + HASH_PAYLOAD

MAST v1.1

โ€” Only validates path and leaf boundedness

โ€” Depth โ‰ค 8, no script execution

Fees / Limits

โ€” Pay per byte or tiered pricing

โ€” Exceeding limits directly rejected

Persistence

โ€” Output header/witness

โ€” Normalized payload bytes

โ€” Auditable and replayable materials

Identity Derivation

โ€” artifact_id

โ€” instance_id (unique primary key)

Three Disciplines Resolution

โ€” Unique active tip

โ€” Spend old create new (Mint exception)

โ€” Concurrent resolution key: blue_score โ†“ ยท block_id โ†‘ ยท wtxid โ†‘

Visibility Rules

โ€” TIMELOCK not unlocked โ†’ not counted in active set

FSM Execution (by SCHEMA_ID)

โ€” Registered schema runs state machine

โ€” Unknown schema โ†’ raw_data

Interface / Proofs

โ€” get_tip / event stream / state query

โ€” Sampling fragments + content hash verification


This diagram describes separation of responsibilities: what L1 full nodes handle, what off-chain indexers handle.

  • L1 (Left): Only cares "whether this transaction can enter a block". It checks structure compliance (flags, size, field lengths), payload hash consistency, signature and lock validity, timelock satisfaction, and fee adequacy. If not satisfied, transaction directly rejected.
  • Off-Chain oUTXO (Right): After getting on-chain approved transactions, organizes the object world. It persists payloads, calculates artifact/instance IDs, determines which instance is the tip, executes schema FSM to generate derived state. It must also guarantee replayability: anyone scanning from genesis to current height can calculate the exact same object state.
  • Boundary: L1 doesn't understand semantics, doesn't know what's actually in the payload, only does structure and constraint validation; oUTXO doesn't change consensus, only transforms bytes into "meaningful objects" off-chain.

One-sentence summary: This diagram emphasizes "who handles what": L1 ensures security and determinism, oUTXO ensures semantics and consistency, they complement each other with clear boundaries.

2) Fields and Data Surface

2.1 On-Chain Native Fields (from Pay2Ingot Output and Witness)

  • SCHEMA_ID : [32] โ€” Semantic routing (indexer selects state machine based on it); L1 doesn't understand semantics.
  • HASH_PAYLOAD : [32] โ€” Commitment to payload (DAG-CBOR normalized bytes). Calculation formula: HASH_PAYLOAD = blake3("ingot/payload" || canonical_bytes), where canonical_bytes is DAG-CBOR normalized bytes.
  • LEN : u32 โ€” Declared payload upper limit (โ‰ค protocol ParamSet).
  • FLAGS : u16 โ€” REVEAL_REQUIRED | ALLOW_MUTABLE | INLINE_OK | โ€ฆ.
  • LOCK โ€” None / PubKey / ScriptHash / MastOnly etc. (L1 validation).
  • SALT : [32] โ€” Instance anchoring to prevent reuse (see below).

Instance Unique Primary Key: instance_id = blake3("ingot/instance" || artifact_id || outpoint_bytes). (artifact_id, outpoint) is only an equivalent index, not a canonical layer uniqueness source. If multiple on-chain instances with the same instance_id appear, resolve by (blue_score โ†“, block_id โ†‘, wtxid โ†‘) to retain a single active instance, others marked collision_stale (off-chain error code INDEXER_E_INSTANCE_COLLISION).

  • PAYLOAD? : bytes โ€” Revealed payload in witness (indexer persists).

Indexer must persist original payload bytes along with the above header, as replay and audit materials.

2.2 Off-Chain Derived Fields (oUTXO defined)

  • artifact_id : [32] Formula: blake3("ingot/artifact" || SCHEMA_ID || HASH_PAYLOAD) Role: Cross-version identifier for the same object (deduplication, reference).
  • instance_id : [32] Formula: blake3("ingot/instance" || artifact_id || outpoint_bytes) Role: Identifies this UTXO instance (a specific version of the same artifact).
  • parent_instance_id? : [32] Source: Explicit reference in payload; used for "spend old, create new" topological constraints.
  • tip_flag : bool Whether it is the current active tip (determined by oUTXO three disciplines).
  • ordinal_seq? : u64 (non-consensus) First appearance sequence number (deterministic numbering of first_seen (txid:vout)); display only.

3) Terminology (Glossary)

  • Artifact: Object entity (e.g., an inscription, an FT contract, a composable Bundle). Uniquely identified by artifact_id.
  • Instance: Object's specific generation instance (product of one state change), corresponding to one Pay2Ingot output; identified by instance_id.
  • Tip (Active Anchor): Current instance recognized by oUTXO as uniquely valid latest instance.
  • Stale: Instance defeated in concurrency or replaced by successor (historical retention, no longer active).
  • Mint / Transfer / Attach / Mutate / Bundle / Rebase: Object operation semantics (defined by schema FSM; oUTXO only handles "sequence and uniqueness").
  • Schema FSM: Finite state machine corresponding to each SCHEMA_ID (executed off-chain), constraining field types, legal operations, and derived state.
  • Soft-Consensus: Constraints not written into L1, but enforced through public specification + test vectors + multi-implementation consistency to force community compliance.

4) Object-Oriented (OO) Mapping

  • Class = Schema: SCHEMA_ID corresponds to "class definition" (fields, available operations, constraints and test vectors).
  • Object = Artifact: An object of a certain "class", identified by artifact_id.
  • Constructor = Mint: Creates first instance (first tip).
  • Methods = Mutators: Transfer/Attach/Mutate/Bundle, etc., input is parent tip, output is new instance.
  • State = Canonical Payload + Derived State: Normalized bytes of payload + FSM-derived balance/ownership, etc.
  • Access Control = LOCK: Methods can be called by whom (multisig, timelock, etc. validated at L1).
  • Composition = Bundle: Inter-object composition/aggregation (references multiple artifact_id).

Inheritance/Polymorphism not in v1 goals; recommend using composition and schema upgrades for extension.


5) Invariants and Resolution

  1. Unique Active Tip For any artifact_id, at any height there is at most one instance with tip_flag = true.
  2. Spend Old, Create New
    • Mint: No parent_instance_id, does not spend parent tip, creates first tip.
    • Transfer/Attach/Mutate/Bundle: If ALLOW_MUTABLE=1, must spend current tip (same transaction/cross-transaction both allowed); if ALLOW_MUTABLE=0, indexer marks any "attempt to replace tip" update as INDEXER_MUTATION_FORBIDDEN, does not become active tip.
  3. Lexicographic Resolution When concurrent successors compete, select one as tip by (blue_score, block_id, wtxid): first compare blue_score (ascending); if equal, compare block_id (block hash byte string lexicographic order, big-endian); then compare wtxid (byte string lexicographic order, big-endian), others marked stale.
  4. Lock Visibility Whether an instance is spendable is completely determined by L1 consensus; off-chain only determines tip visibility based on L1 spendability results.
  5. Unknown Schema Mark raw_data, must not execute FSM, but can be re-indexed and upgraded to executable state (through Registry test vectors).

6) Operation Semantics (Indexer Side)

  • Mint: No parent_instance_id, does not spend parent tip; generates first tip.
  • Transfer: If ALLOW_MUTABLE=1, spend tip โ†’ output new instance (ownership and other fields change); allows HASH_PAYLOAD unchanged (only ownership/lock changes).
  • Attach: Append auxiliary data in payload (reference parent tip); requires ALLOW_MUTABLE=1, must spend current tip.
  • Mutate: Change object mutable fields; requires ALLOW_MUTABLE=1, must spend current tip.
  • Bundle: Aggregate multiple artifact_id, form composite object; requires ALLOW_MUTABLE=1, must spend current tip.
  • Rebase (Suggested): When increments are excessive, produce "consolidated version" (schema semantics; doesn't change invariants).

The above operations are defined by schema FSM for fields and legality; oUTXO only handles sequence and uniqueness.


7) Replay Algorithm

Input: All P2I transactions from genesis to target height (including revealed payload and header).

Process:

  1. Witness Validation (rigid validation sequence):

    • Structure/limit checks โ†’ payload hash validation โ†’ reassemble normalized canonical_bytes โ†’ calculate H = blake3("ingot/payload" || canonical_bytes) โ†’ assert H == witness.PAYLOAD_HASH == output.HASH_PAYLOAD โ†’ LOCK validation/signature/timelock โ†’ fee check
    • Payload hash mismatch โ†’ CONSENSUS_E_HASH_MISMATCH
    • witness.PAYLOAD_HASH != output.HASH_PAYLOAD โ†’ CONSENSUS_E_HASH_MISMATCH
  2. Verify/recover payload (DAG-CBOR normalization).

  3. Calculate artifact_id / instance_id.

  4. Apply oUTXO three disciplines:

    • Initialize or update tip for this artifact_id;
    • Concurrent resolution by (blue_score, block_id, wtxid) uniqueness: first compare blue_score (ascending); if equal, compare block_id (block hash byte string lexicographic order, big-endian); then compare wtxid (byte string lexicographic order, big-endian);
    • Exclude unlocked instances from active set.
  5. If SCHEMA_ID is enabled by Registry โ†’ execute FSM โ†’ update derived state; otherwise mark raw_data.

Output:

  • Active tip mapping: artifact_id โ†’ instance_id
  • Event stream (Mint/Transfer/Attach/Mutate/Bundle)
  • Derived state (balance/ownership/index)

8) Indexer Interface (Minimal Set)

  • get_tip(artifact_id) -> instance_id | None
  • get_artifact(artifact_id) -> {schema_id, payload, lock, tip_flag, ...}
  • list_events(artifact_id, from_height)
  • state_query(schema_id, key) -> value (defined by FSM)
  • prove(artifact_id, height) -> {merkle_proof, bls_agg_sig?} (optional: state root and aggregate signature)

Light client recommendation: Pull minimal proof and sampled fragments by artifact_id, verify against HASH_PAYLOAD.


9) Error Code Layered Naming Convention

L1 / Consensus Layer Error Codes (CONSENSUS_*)

  • CONSENSUS_E_INVALID_STRUCTURE: Structure/boundary validation failed
  • CONSENSUS_E_HASH_MISMATCH: Reassembled hash doesn't match witness hash, or witness hash doesn't match output hash
  • CONSENSUS_E_SIGNATURE_INVALID: Signature verification failed
  • CONSENSUS_E_TIMELOCK_NOT_SATISFIED: Timelock not satisfied
  • CONSENSUS_E_UNKNOWN_VERSION: Unknown version
  • CONSENSUS_E_SCRIPT_FAILED: Script execution failed (for ScriptHash locks)

Off-Chain / oUTXO Error Codes (INDEXER_*)

  • INDEXER_CONFLICT_RESOLVED: After concurrent conflict resolution, this instance was judged as stale
  • INDEXER_MUTATION_FORBIDDEN: Parent instance didn't set ALLOW_MUTABLE, mutation forbidden
  • INDEXER_TIMELOCKED: Instance not unlocked, cannot be counted as tip
  • INDEXER_UNKNOWN_SCHEMA: Not enabled in Registry
  • INDEXER_FORMAT_INVALID: Payload encoding not DAG-CBOR normalized or missing fields
  • INDEXER_INSTANCE_COLLISION: Multiple on-chain conflicts with same instance_id
  • INDEXER_PARENT_MISSING: Declared parent_instance_id doesn't exist/not visible

Policy / Network Layer Error Codes (POLICY_*)

  • POLICY_RATELIMITED: Account rate limit triggered
  • POLICY_FEE_FLOOR_UNMET: Fee floor not met

Processing Flow:

  • L1 rejection errors โ†’ transaction/block directly invalid
  • oUTXO errors โ†’ doesn't affect L1, effectively records/rollbacks/resolutions
  • DAG reorg: Indexer allows tip flip within FINALITY_K; beyond K locks and only rolls back on longer reorg

10) Payload Structure Suggestions (DAG-CBOR, Example Keys)

  • artifact_type : "inscription" | "ft" | "nft" | "bundle" | "custom"
  • schema_version : u16
  • parent_instance_id? : bytes[32] (required for non-Mint)
  • mutator? : { op: "transfer|attach|mutate|bundle", args: {...} }
  • body? : bytes | map (original text or structured data)
  • uris? : [ { uri, content_hash } ] (if ALLOW_EXTERNAL_URIS)
  • author_sig? : bytes (display/source proof)

HASH_PAYLOAD Unified Definition: HASH_PAYLOAD = blake3("ingot/payload" || canonical_bytes), where canonical_bytes is DAG-CBOR normalized encoding payload bytes. Witness domain PAYLOAD_HASH calculation standard is the same, and must satisfy: blake3("ingot/payload" || canonical_bytes(reassembled_payload)) == witness.PAYLOAD_HASH == output.HASH_PAYLOAD. Any step not equal โ†’ CONSENSUS_E_HASH_MISMATCH.


11) Version Compatibility (v1.0 / v1.1)

Item v1.0 v1.1 (Hard Fork Activation)
Signature Algorithm CopperootSchnorr CopperootSchnorr + BIP-340 StandardSchnorr
MAST Not Supported Optional MAST_ROOT[32]; witness requires MAST_PATH + leaf_lock; only validates path and leaf boundedness
Field Compatibility No MAST_ROOT New MAST_ROOT (v1.0 nodes fail-closed โ†’ requires hard fork)
Witness Validation No MAST Additional step: validate MAST_PATH, depth โ‰ค 8

MAST_ROOT Purpose: Hides specific lock combinations (privacy enhancement), does not introduce script execution, only validates Merkle path and leaf encoding boundedness.


12) Registry Enforcement Mechanism and Admission Pipeline

Registry Enforcement Mechanism Overview

Registry serves as oUTXO protocol's "off-chain hard consensus" mechanism, ensuring consistency of all implementations through test vector enforced verification. Implementations that fail Registry verification can only output raw_data, must not claim "executable state".

Admission Pipeline

  1. Testvectors Repository (with commit hash):

    • S1 Serialization/Endianness: Verifies little-endian encoding of all numeric fields
    • S2 MAST Path: Verifies v1.1 MAST path calculation and depth limits
    • S3 Concurrent Resolution: Verifies (blue_score, block_id, wtxid) resolution rules
    • S4 Reorg Rollback: Verifies state rollback and re-resolution during DAG reorgs
    • S5 Signature Strictness: Verifies CopperootSchnorr strict validation and BIP-340 Schnorr
    • S6 Error Code Mapping: Verifies correct mapping of layered error codes
  2. CI Enforced Validation:

    • Must pin commit to pull testvectors
    • Must run all 6 test suites (S1-S6)
    • 100% pass rate requirement
  3. Registry Badge Issuance:

    • After passing, generate Registry Badge: registry.ingot.badge::<schema_id>::<commit>
    • Badge contains version stamp, commit hash, pass timestamp
  4. Ecosystem Whitelist Mechanism:

    • Wallet default index source only whitelists implementations with Badge
    • Ecosystem listings only display verified implementations
    • Unverified implementations can only claim raw_data mode
  5. Runtime Self-Check:

    • Implementation must print registry_version/commit and self-check hash at startup
    • Facilitates network-side inspection and consistency verification

Registry Deprecation Policy

  • When Registry publishes deprecates: ["vX.Y.Z"]
  • Deprecated versions cannot claim executable after grace period (e.g., 90 days)
  • Can still run during grace period, but cannot apply for new Badge

Implementation Requirements

  • Indexers: Must pass Registry validation to provide "executable state" API
  • Wallets/SDKs: Recommend integrating verified indexer sources
  • Ecosystem Applications: Recommend using implementations with Badge to ensure consistency

13) Interface and Boundary with L1

Separation of Responsibilities

  • L1 Responsible: Structural integrity, hash consistency, payload size limits, signatures and timelocks, fees adequate.
  • oUTXO Responsible: Object uniqueness, version chain, concurrent resolution, FSM invocation and derived state.
  • Registry Responsible: Schema documentation and test vector hashes; indexers must pass before enabling FSM.

Timelock Responsibility Boundary

L1 fully executes TIMELOCK_* (unsatisfied rejects tx); oUTXO excludes unlocked instances from visibility, ensuring active tip set only contains spendable instances. They complement rather than conflict.

Unique Primary Key Convention

instance_id = blake3("ingot/instance" || artifact_id || outpoint_bytes) is the unique primary key; (artifact_id, outpoint) is only an equivalent index key, used for implementation-layer lookup and deduplication. External identifiers and interfaces uniformly use instance_id.


Previous: Data Insertion Mechanism | Next: Asset Management | Technical Specifications