tinylayer |
[==] [========] |
smallest BIP448 statechain we can explain
Tinylayer is a Mutinynet-only Layer 2 for transferring ownership of one Bitcoin UTXO off chain. A rollback-protected Enclavia workload chooses one successor; BIP448 lets the accepted latest state replace any stale state on chain without another signature.
Bitcoin / Mutinynet
funding UTXO
│
│ N private ownership transfers
▼
client ↔ Enclavia
│
│ unilateral close
▼
latest BIP448 update → 12-block contest → owner settlement
The complete wire and transaction rules are in the protocol specification.
Why this is a Layer 2
Alice can transfer to Bob, Bob to Carol, and so on without broadcasting a transaction. Mutinynet normally sees only funding and the final exit sequence. Every receiver gets an encrypted history and a fully signed latest update.
After Carol accepts, Enclavia can disappear. If Alice publishes an old state, Carol changes only her update’s prevout, keeps both signature bytes unchanged, and spends Alice’s state before its delayed settlement matures.
That is the eltoo/LN-Symmetry latest-state mechanism applied to a statechain. Enclavia remains necessary for a different problem: refusing two sibling successors from the same owner.
The deliberately small design
Protocol 2 uses:
- BIP446
OP_TEMPLATEHASH; - BIP348
OP_CHECKSIGFROMSTACK; - one transferable client key;
- one global Enclavia key;
- fresh receiver capabilities and settlement keys;
- an unspendable BIP341 NUMS internal key;
- version-3 zero-fee updates with a P2A package anchor;
- a 12-block CSV settlement delay.
This makes every exit a recognizable Taproot script-path close. Funding and all cooperative ownership transfers remain hidden until then.
Read the complete signer
The rollback-protected signing state machine is about 254 lines. Its Bitcoin-opaque HTTP workload brings the complete production enclave Rust to 300 lines.
The website renders the exact compiled source rather than maintaining a second handwritten copy:
Components
| Crate | Responsibility | Guide |
|---|---|---|
enclave |
Global BIP340 template signer, capability CAS, exact retry | Enclave guide |
client |
BIP446 hash, Taproot trees, updates, rebinding, settlement, attested transport | Client guide |
wallet-core |
Encrypted state and encrypted transfer package | Wallet core guide |
wallet |
Mutinynet-only CLI, explorer checks, P2A packages, state monitoring | Wallet guide |
Mutinynet prerequisites
Mutinynet’s custom Bitcoin Inquisition build includes the BIP448 opcodes. The
TEMPLATEHASH activation signal was mined in Mutinynet block 3,108,700.
Before using funds, the deployment node must report both templatehash and
checksigfromstack active through getdeploymentinfo.
The reference wallet defaults to https://mutinynet.com/api for chain reads
and package submission. The Enclavia workload never parses a transaction or
contacts a Bitcoin service. Public activation admission and rate limiting must
be enforced outside the enclave.
Build and test
cargo build --locked --workspace --all-features
cargo test --locked --workspace --all-features
The tests pin the official BIP446 vector, prove prevout rebinding, reject transaction mutations and sibling transitions, exercise idempotent opaque activation, and run a complete CLI Alice-to-Bob transfer against local servers.
CLI walkthrough
Set a password non-interactively for the examples:
export ENCLAVIA_WALLET_PASSWORD='replace this'
Initialize Alice:
tinylayer-wallet --data-dir alice init \
--enclave-url wss://<deployment>.enclavia.io \
--pcr0 <96-hex> --pcr1 <96-hex> --pcr2 <96-hex>
Prepare a 100,000-sat coin without allocating enclave state:
tinylayer-wallet --data-dir alice coin new --amount-sat 100000
Send exactly that amount from the Mutinynet faucet to the returned address. After confirmation, bind the output:
tinylayer-wallet --data-dir alice coin bind --outpoint <txid>:<vout>
The coin is still absent from Enclavia. Its committed bootstrap update is enough for Alice to exit.
Bob creates a request:
tinylayer-wallet --data-dir bob init \
--enclave-url wss://<deployment>.enclavia.io \
--pcr0 <96-hex> --pcr1 <96-hex> --pcr2 <96-hex>
tinylayer-wallet --data-dir bob transfer request --output bob-request.json
Alice performs the first lazy activation and transfer:
tinylayer-wallet --data-dir alice transfer send \
--request bob-request.json \
--output bob-package.json
Bob verifies and accepts:
tinylayer-wallet --data-dir bob transfer receive \
--request bob-request.json \
--package bob-package.json
Before exit, fund Bob’s fee address shown by:
tinylayer-wallet --data-dir bob fee-address
Then publish the latest update:
tinylayer-wallet --data-dir bob exit --destination <mutinynet-address>
Run the same command after the update has 12 confirmations to broadcast the settlement.
See the wallet guide for stale-state defense, local debug mode, and the complete command lifecycle.
What this proves
The public demonstration is complete when it links Mutinynet transactions for:
fund coin
Alice → Bob → Carol off chain
stop Enclavia
publish Alice's stale bootstrap state
rebind Carol's already-signed update to Alice's output
confirm Carol's update without new signatures
wait 12 blocks
settle to Carol
Live Mutinynet proof
Bootstrap-state replacement
| Step | Transaction |
|---|---|
| Funding | d2a5b0f8…d5f2b |
Alice stale U1 |
cbb573fe…c2bbf |
U1 P2A child |
94984ba1…2c25a |
Carol rebound U3 |
dd9565bd…8ebe1 |
U3 P2A child |
3c116cdc…7a76c |
| Carol settlement | de85499e…98c9f |
Alice transferred state 2 to Bob and Bob transferred state 3 to Carol entirely
off chain. The signer was stopped before Alice’s state confirmed. Carol’s
already-signed U3 then spent U1:0; no new client or Enclavia signature was
created. After 12 confirmations, Carol’s CSV settlement confirmed at Mutinynet
height 3,366,659.
Fully signed state replacement
A second coin demonstrated the stronger U2 → U3 case:
| Step | Transaction |
|---|---|
| Funding | 0ad7d83b…cc634 |
Bob stale signed U2 |
c9e12daa…1cfff |
U2 P2A child |
f09b5ddd…7e985 |
Carol rebound U3 |
be7a747c…9b9ac |
U3 P2A child |
68dbdcce…95f42 |
| Carol settlement | b75bc489…b1e63 |
The signer was stopped before Bob published his fully client-and-Enclavia-signed
state 2. U2 revealed the funding live-update leaf. Carol then spent U2:0
with her already-signed state 3, revealing state 2’s L2+1 CLTV update leaf.
No signature was created after the signer stopped. Carol’s settlement confirmed
at Mutinynet height 3,366,720.
This is an experiment, not a production wallet or a mainnet protocol.
Security reports follow SECURITY.md.
Contributions follow CONTRIBUTING.md.
The project is MIT licensed.