Tinylayer protocol 2
Tinylayer protocol 2 is a Mutinynet-only statechain using the BIP448 deployment:
BIP446 OP_TEMPLATEHASH and BIP348 OP_CHECKSIGFROMSTACK. A rollback-protected
Enclavia workload linearizes ownership; Bitcoin enforces latest-state
replacement and delayed settlement.
This document is normative for the proof-of-concept implementation.
Security model
A valid coin assumes:
- Mutinynet enforces
OP_TEMPLATEHASH(0xce) andOP_CHECKSIGFROMSTACK(0xcc). - The pinned Enclavia measurement runs the published state machine and its protected state cannot be rolled back or forked.
- At least one honest chain view reports confirmations and outspends.
- Every receiver independently validates the complete encrypted state history.
- The current owner or a watchtower reacts within the settlement delay.
Enclavia may deny new transfers. It cannot spend alone: every update also needs the transferable client key. After accepting a fully signed state, an owner can exit without Enclavia.
Fixed parameters
protocol version 2
network Mutinynet (signet address encoding)
transaction version 3
protocol input index 0
contest delay 12 blocks
state locktime range 500,000,000..=1,000,000,000
Taproot internal key BIP341 NUMS point
update output index 0
P2A anchor output index 1
annex forbidden
The NUMS internal key has no known discrete logarithm. Every protocol spend is a Taproot script-path spend.
BIP446 TemplateHash
For input index zero and no annex, Tinylayer computes the tagged SHA256
TemplateHash over:
nVersion 4-byte little endian
nLockTime 4-byte little endian
SHA256(all input sequences) 32 bytes
SHA256(all serialized outputs) 32 bytes
annex_present 0x00
input_index 4-byte little endian zero
Prevouts, input amounts, input scripts, and scriptSigs are deliberately absent. Changing only an update’s prevout therefore leaves its template hash and both signatures unchanged. Changing a sequence, locktime, output, or input index invalidates the signatures.
client/src/template.rs is pinned to the official BIP446 known-answer vector.
Keys
Each coin has:
client key one secret transferred between owners
Enclavia key one global key pinned by attestation
withdrawal key fresh secret generated by each receiver
capability fresh bearer secret generated by each receiver
The client and Enclavia keys sign the BIP446 template hash independently.
Tinylayer deliberately does not use OP_INTERNALKEY or MuSig in protocol 2.
Funding construction
The wallet first constructs initial update U1 with a null placeholder
prevout. Because TemplateHash excludes the prevout, its hash is final.
The funding output is a two-leaf P2TR tree:
bootstrap leaf:
<TemplateHash(U1)> OP_TEMPLATEHASH OP_EQUAL
live update leaf:
OP_TEMPLATEHASH
<client_key> OP_CHECKSIGFROMSTACK OP_VERIFY
OP_TEMPLATEHASH
<Enclavia_key> OP_CHECKSIGFROMSTACK
The bootstrap leaf makes U1 unconditionally publishable without server state.
The live leaf lets any later fully signed update spend the funding output
directly.
Funding order:
1. Query stateless Enclavia service info and pin its public key.
2. Generate client, initial capability, and initial withdrawal secrets.
3. Construct U1 and the funding address locally.
4. Send the exact amount to that address.
5. Wait for confirmation and bind the exact outpoint locally.
No enclave row is allocated during these steps. If Enclavia disappears, the
owner publishes U1, waits the contest delay, and settles.
State output
State n has an absolute ordering locktime L(n) and fresh withdrawal key
W(n). Its output is a two-leaf P2TR tree:
update leaf:
<L(n) + 1> OP_CHECKLOCKTIMEVERIFY OP_DROP
OP_TEMPLATEHASH
<client_key> OP_CHECKSIGFROMSTACK OP_VERIFY
OP_TEMPLATEHASH
<Enclavia_key> OP_CHECKSIGFROMSTACK
settlement leaf:
<12> OP_CHECKSEQUENCEVERIFY OP_DROP
<W(n)> OP_CHECKSIG
Only the selected leaf is revealed when spent. The hidden sibling is supplied as a Merkle hash in the control block.
Update transaction
Every state update is canonical:
version: 3
nLockTime: L(n)
inputs: exactly one protocol input
input 0: sequence zero, empty scriptSig, no annex
output 0: full coin amount to state output n
output 1: zero-value P2A anchor (OP_1 <0x4e73>)
fee: zero
L(n+1) must be strictly greater than L(n). An update for a newer state
passes an older output’s CLTV gate. An older update cannot pass a newer output’s
gate.
The zero-fee update is submitted with a version-3 child spending output 1 and a confirmed wallet fee input. Both are submitted as one package.
Lazy activation and admission
The first transfer activates protected Enclavia state. The untrusted wallet derives a coin ID from the funding outpoint and independently verifies the funding UTXO before sending this opaque request:
coin ID
initial capability hash
TemplateHash(U1)
The workload does not receive a transaction, outpoint, amount, script, proof, or network identifier. It never queries Mutinynet. Exact activation replay is idempotent; a conflicting request for the same coin ID is rejected.
Because the enclave cannot distinguish a real coin from an invented ID, public activation remains a capacity-exhaustion surface. A deployment must gate or rate-limit activation outside the enclave. That external admission policy is operational and is not part of protocol 2.
Protected Enclavia state
One global key signs every coin. Each active row stores only:
coin ID
current capability hash
state number
latest template hash
exact last request and response
Initial activation records state 1 and TemplateHash(U1).
A signing request supplies the current capability, next capability hash, next state number, and next template hash. The serialized critical section:
if request == last_request:
return last_response
require H(current_capability) == stored capability hash
require next_state_number == stored state number + 1
require next capability hash differs
require next template hash differs
sign next template hash with the global Enclavia key
persist capability, number, template, request, and response atomically
return response
Two siblings from the same state cannot both obtain Enclavia signatures.
Transfer
The receiver generates:
request ID
next capability and its hash
fresh withdrawal key
fresh transport key
The sender:
- Verifies current Enclavia status and complete state history.
- Constructs the next canonical update with greater
nLockTime. - Signs its TemplateHash with the client key.
- Durably records the exact request before calling Enclavia.
- Obtains the Enclavia signature and completes the state.
- Encrypts the client secret, coin metadata, and complete ordered history to the receiver transport key.
The receiver:
- Authenticates and decrypts the package.
- Verifies funding remains confirmed and unspent.
- Verifies every historical output tree, template hash, signature, state number, and increasing locktime.
- Requires history length and latest hash to equal attested Enclavia status.
- Requires the latest settlement leaf to contain its withdrawal key.
- Stores the transferred client secret, its capability, withdrawal secret, and history durably.
History is encrypted and never published during cooperative operation. It lets the latest owner reconstruct the control block for any stale state.
Latest-state replacement
If funding is unspent, the latest signed update spends it through the live funding leaf.
If old update Uk is confirmed:
1. Locate state k in encrypted history by TemplateHash(Uk).
2. Reconstruct state k's update leaf and settlement sibling.
3. Verify the reconstructed tree equals Uk.output[0].
4. Change only Ulatest.input[0].prevout to Uk:0.
5. Attach state k's update leaf and control block.
6. Submit Ulatest and its P2A fee child.
The client and Enclavia signature bytes do not change. No service call occurs.
Settlement
After the latest update has 12 confirmations, the owner spends output 0 through its settlement leaf with the withdrawal key. The settlement transaction pays a normal Mutinynet address and deducts its own fee.
A stale owner may publish an old update, but cannot settle it until the same 12-block delay expires. The latest owner or watchtower must replace it during that window.
Privacy
Before exit, funding is an opaque P2TR output and any number of ownership transfers leave no chain entries. A receiver learns encrypted cryptographic history, not participant identities. Exit reveals the chosen BIP448 and CSV leaves and links funding to settlement. Protocol 2 makes no key-path or cooperative-close privacy claim.
Known boundaries
- BIP448 is experimental and this protocol is Mutinynet-only.
- Every receiver must retain and validate
O(n)encrypted history. - A current owner or watchtower must monitor the funding lineage.
- Enclavia availability is required for transfers, not for accepted-state exit.
- Duplicate payments to an already used funding script are unsupported.
- Activation is intentionally Bitcoin-opaque; hard public admission and Sybil resistance remain external deployment problems.