Tinylayer wallet core

tinylayer-wallet-core contains the two pieces that should remain independent of networking and CLI parsing:

Protocol and Bitcoin validation live in tinylayer-client; I/O lives in tinylayer-wallet.

Wallet state

The protocol-2 state is intentionally small:

fee key
prepared unfunded coin, or current funded coin
pending exact Enclavia request
incoming receiver request
outgoing encrypted package

A funded coin stores:

transferable client secret
current capability
current withdrawal secret
funding metadata
ordered signed state history
activation flag
actual exit update, once published

The state file is JSON encrypted with Argon2id-derived XChaCha20-Poly1305. Its associated data binds the ciphertext to the plaintext wallet configuration. Files and directories must not be accessible to group or other users on Unix.

Transfer package

A receiver request contains only fresh public material:

request ID
next capability hash
withdrawal x-only public key
transport public key

The sender’s payload contains the transferred client secret, immutable coin metadata, and complete ordered state history. It is encrypted using ephemeral secp256k1 ECDH, HKDF-SHA256, and XChaCha20-Poly1305. The complete request JSON is associated data, so substituting any receiver key breaks authentication.

The current receiver already holds the next capability and withdrawal secrets; they are never sent by the sender.

Crash boundaries

Before calling Enclavia, the wallet stores PendingSend with the exact PreparedUpdate. A lost response is recovered by resending that exact request; the enclave returns its cached signature. After completion, the encrypted package is stored before being written to the requested destination, so output failure is retryable without another state transition.

Tests

cargo test --locked -p tinylayer-wallet-core

The codec test proves encryption round trips, wrong-password rejection, and configuration binding. End-to-end transfer behavior is exercised by the wallet CLI integration test.