Locking Mechanisms

Understanding Ingot's authentication and access control mechanisms

Locking Mechanisms

Overview

Ingot offers four straightforward locking mechanisms to secure assets. These locks control who can spend an Ingot output and under what conditions.

Lock Types

1. None

No authentication - anyone can spend.

Use Cases:

  • Public inscriptions
  • Public data
  • Open-access assets

Example:

Lock::None

2. PubKey

Single signature or MuSig2 aggregated signature - requires cryptographic authentication.

Structure:

Lock::PubKey {
    pubkey: Vec<u8>,           // 32B x-only public key (may be MuSig2 aggregated)
    sig_type: SignatureType    // CopperootSchnorr or StandardSchnorr
}

Features:

  • Supports single-key signatures
  • Full MuSig2 aggregation support
  • Privacy advantage: MuSig2 aggregated keys completely indistinguishable from single keys on-chain
  • Performance advantage: MuSig2 requires only 1 signature, saves 57% space (vs old Multisig)

Signature Types:

  • CopperootSchnorr (0x01): BLAKE3 sighash + SHA256 challenge + BIP340 Schnorr (Tondi native optimization, 4-5x faster)
  • StandardSchnorr (0x04): SHA256 sighash + SHA256 challenge + BIP340 Schnorr (fully Bitcoin Taproot compatible)

Use Cases:

  • Personal asset ownership
  • Multi-signature wallets (via MuSig2)
  • Team-controlled assets

3. ScriptHash

Custom rules via Tondi VM or external VMs - enables programmable conditions.

Structure:

Lock::ScriptHash {
    script_hash: [u8; 20]      // HASH160(script)
}

HASH160 Definition:

script_hash = RIPEMD160(SHA256(script_reveal_bytes))

Where script_reveal_bytes is the raw byte sequence of the script (not Borsh wrapped).

Features:

  • Supports time locks (OP_CHECKLOCKTIMEVERIFY/OP_CHECKSEQUENCEVERIFY)
  • Supports arbitrary custom script logic
  • Executed via Tondi VM
  • Future: Supports WASM, EVM, and other VMs

Time Lock Examples:

Absolute Time Lock:

<unlock_time> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG

Relative Time Lock:

<relative_blocks> OP_CHECKSEQUENCEVERIFY OP_DROP <pubkey> OP_CHECKSIG

Use Cases:

  • Time-locked assets (vesting, escrow)
  • Custom validation logic
  • Multi-party conditions
  • Future: Cross-chain bridge validation

4. MastOnly

Pure MAST lock - maximum privacy mode.

Structure:

Lock::MastOnly

Requirements:

  • Requires mast_root to exist in IngotOutput
  • Must provide MAST proof when spending
  • All locking conditions hidden in Merkle tree

Features:

  • Hides all spending paths except the one used
  • Maximum privacy for complex multi-party scenarios
  • Only reveals the specific lock combination actually used

Use Cases:

  • Privacy-sensitive multi-signature wallets
  • Complex governance structures
  • Hidden spending conditions

Multi-Signature Support

MuSig2 Aggregation

Ingot uses MuSig2 for efficient multi-signature support:

N-of-N Multisig:

  1. Aggregate public keys off-chain
  2. Create Ingot with aggregated public key
  3. Aggregate signatures off-chain when spending
  4. On-chain verification (identical to single signature)

Advantages:

  • On-chain only needs 1 public key (32B) + 1 signature (64B) = 96B
  • Completely indistinguishable from single signature (best privacy)
  • Saves 57% space compared to traditional multisig

M-of-N Multisig:

  • Use MAST tree with C(N,M) combinations
  • Create MAST tree containing all possible signer combinations
  • Reveal only used combination when spending
  • Unused combinations remain hidden

MAST (Merkle Abstract Syntax Tree)

MAST provides privacy enhancement by hiding unused spending paths.

MAST Specification

Hash Calculation:

MAST_LEAF = blake3("MAST/leaf" || leaf_lock_bytes)
MAST_NODE = blake3("MAST/node" || min(Left,Right) || max(Left,Right))

Features:

  • Maximum depth: 8 levels
  • Sorted merge: Avoids left-right sensitivity
  • Leaf structure: Lock enum (Borsh serialized)
  • Path validation: Verifies Merkle path correctness

MAST Proof

When spending a MAST-locked output:

Structure:

mast_proof: {
    path: [hash...],      // Merkle path (depth โ‰ค 8)
    leaf_lock: Lock       // The actual lock used
}

Validation:

  1. Calculate leaf hash: blake3("MAST/leaf" || leaf_lock_bytes)
  2. Reconstruct root along path
  3. Verify reconstructed root matches MAST_ROOT in output
  4. Validate leaf_lock structure

Signature Message Domain

All signatures are bound to a specific message domain:

SigMsg = blake3(
  "Ingot/SigMsg/v1" ||
  network_id_byte ||
  wtxid ||
  u32le(input_index) ||
  spent_prevout{ txid || u32le(vout) } ||
  lock_bytes ||
  HASH_PAYLOAD
)

Security Features:

  • Network Isolation: Each network uses independent network_id signature domain
  • Anti-Replay: Signatures anchor to specific input, payload, and network
  • Transaction Binding: Includes wtxid to prevent malleability

Lock Selection Guide

Use Case Recommended Lock Notes
Public inscription None No access control needed
Personal asset PubKey Simple ownership
Multi-sig wallet PubKey + MuSig2 Efficient and private
Time-locked asset ScriptHash Use OP_CHECKLOCKTIMEVERIFY
Complex conditions ScriptHash Custom VM logic
Maximum privacy MastOnly Hide all spending paths

Security Considerations

Fail-Closed Principle

  • Unknown lock types โ†’ transaction rejected
  • Invalid signature format โ†’ transaction rejected
  • Script execution failure โ†’ transaction rejected
  • MAST path validation failure โ†’ transaction rejected

Signature Format

All signatures must be:

  • 64 bytes (BIP340 Schnorr format)
  • Format: (r || s) each 32 bytes
  • Curve: secp256k1 (same as Bitcoin)
  • Challenge: SHA256 (BIP340 standard)

Lock Validation

L1 consensus validates:

  • Lock structure and format
  • Signature correctness
  • Script execution (for ScriptHash)
  • MAST proof (for MastOnly)
  • All validation failures result in transaction rejection

Previous: Asset Management | Next: Tools & Reference