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_rootto 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:
- Aggregate public keys off-chain
- Create Ingot with aggregated public key
- Aggregate signatures off-chain when spending
- 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:
- Calculate leaf hash:
blake3("MAST/leaf" || leaf_lock_bytes) - Reconstruct root along path
- Verify reconstructed root matches
MAST_ROOTin output - 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_idsignature 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