Tinylayer home

Tinylayer BIP448 client

tinylayer-client is the untrusted protocol library for Tinylayer 2. It constructs and verifies every Bitcoin object while the Enclavia workload only linearizes capabilities and signs 32-byte template hashes.

Public surface

The library provides:

See the protocol specification for normative scripts and transaction formats.

TemplateHash

let hash = tinylayer_client::template_hash(&transaction, 0)?;

Tinylayer supports only protocol input zero with no annex. The hash commits version, locktime, all sequences, all outputs, annex absence, and input index. It omits prevouts, amounts, and scripts as specified by BIP446.

Changing only:

transaction.input[0].previous_output = another_outpoint;

leaves hash unchanged. Any output, sequence, or locktime mutation changes it.

Funding without registration

let info = enclave.info().await?;
let keys = tinylayer_client::keys(&client_secret, info)?;
let initial = tinylayer_client::initial_state(
    keys,
    amount_sat,
    initial_locktime,
    withdrawal_pubkey,
)?;
let address = tinylayer_client::funding_address(keys, initial.template_hash);

No coin row exists yet. After the exact address is funded and confirmed, bind the placeholder prevout:

let initial = bind_initial_state(initial, funding_outpoint)?;
let metadata = metadata(funding_outpoint, amount_sat, keys, &initial)?;

The bootstrap leaf commits to the initial template hash, so state 1 is safe even if the service disappears.

Lazy activation and signing

The first sender creates:

let activation = activation_request(&metadata, capability_hash(&capability));
let status = enclave.activate(&activation).await?;
verify_activation(&metadata, &activation, &status)?;

Then prepare and durably store the exact next request before sending it:

let prepared = prepare_update(
    &metadata,
    &status,
    &history,
    &client_secret,
    capability,
    receiver_capability_hash,
    next_lock_time,
    receiver_withdrawal_pubkey,
)?;

let response = enclave.sign(prepared.request()).await?;
let state = complete_update(&metadata, prepared, &response)?;

The completed state holds independent client and Enclavia BIP340 signatures over the same TemplateHash.

Rebinding

Spend an unspent funding output with the latest signed state:

let transaction = update_for_source(&metadata, latest, UpdateSource::Funding)?;

Publish bootstrap state 1 without signatures:

let transaction = update_for_source(
    &metadata,
    &history[0],
    UpdateSource::FundingBootstrap,
)?;

Replace a confirmed stale state:

let transaction = update_for_source(
    &metadata,
    latest,
    UpdateSource::State {
        state: stale_descriptor,
        transaction: &observed_stale_transaction,
    },
)?;

The function verifies the observed transaction’s template, reconstructs its Taproot tree, changes the latest update’s prevout, and attaches the old update leaf/control block. It does not create a new signature.

Fee package

Updates preserve the full coin amount and include a zero-value P2A output at index 1. A confirmed wallet fee UTXO funds the child:

let child = build_update_fee_child(
    &update,
    fee_outpoint,
    &fee_output,
    &fee_secret,
    fee_rate_sat_vb,
)?;

Submit parent and child through Mutinynet package relay.

Settlement

After the update reaches the 12-block CSV age:

let settlement = build_settlement(
    &metadata,
    latest_state,
    &confirmed_update,
    &withdrawal_secret,
    destination_script,
    fee_rate_sat_vb,
)?;

The owner signs the settlement leaf locally. Enclavia is not contacted.

Remote Enclavia transport

Production:

let enclave = RemoteEnclave::connect(url, pinned_pcrs).await?;

Debug-attested test server:

let enclave = RemoteEnclave::connect_debug(url, pcrs).await?;

The SDK reconnects and re-attests but never silently resends an application request. Retrying exact activation/signing bytes is the caller’s responsibility.

Verification

cargo test --locked -p tinylayer-client --lib
cargo test --locked -p tinylayer-client --test bip448
cargo test --locked -p tinylayer-client --test remote

The tests cover the official BIP446 vector, committed-field mutation, activation, sibling rejection, three states, stale-state rebinding, P2A child, settlement, and attested remote calls.