Locks & policy locks
Every output has a lock: a kind byte and a payload. Spending it requires a witness that satisfies the lock (§4).
| Kind | Name | Commits to | Address prefix |
|---|---|---|---|
| 0 | BURN | up to 80 bytes of data; never spendable | none |
| 1 | PUBKEY_HASH | scheme id + key hash | …1p |
| 2 | MULTISIG_HASH | scheme mask + m-of-n policy hash | …1z |
| 3 | POLICY | scheme mask + hash of a spending policy | none |
| 16–127 | reserved | 1–64 bytes; rules arrive by soft fork | none |
Addresses are bech32m with the network's prefix: arrk (mainnet), tarrk (testnet), rarrk
(regtest). Only PUBKEY_HASH and MULTISIG_HASH have addresses.
Multisig
A multisig policy is m of up to 15 distinct public keys. Keys may mix schemes (ML-DSA-65 and
ML-DSA-87); the lock publishes the scheme mask so wallets can show exposure. The witness has
exactly n slots with exactly m signatures, each in the position of its key.
Policy locks: a small language, not a VM
One lock kind covers the conditional spends that issuers and wallets need: vesting, escrow, cross-chain swaps, recovery paths, channel constructions.
KEY(pk) a signature from pk
MULTI(m, pk…) m of n signatures
AFTER(height) tx.lock_time ≥ height (absolute timelock)
OLDER(blocks) the coin is at least N blocks old (relative timelock, via input sequence)
SHA3(digest) a 32-byte preimage with sha3_256(preimage) == digest (hashlock)
AND(p, q) OR(p, q) THRESH(k, p…)
- Limits: depth ≤ 8, at most 20 distinct keys, 48 witness items, 64 KiB encoding.
- Witness: the policy encoding followed by items consumed in pre-order (a signature, preimage or empty item per leaf).
- No garbage: a non-empty item that fails verification invalidates the transaction, even in an unused branch.
- Deterministic: timelocks are properties of the transaction (lock time, sequence, version), so validity never depends on which block includes it.
Because the language is fixed and non-interactive, wallets, the PST format and hardware signers can display exactly what a policy allows.
Templates
The SDKs ship policy builders (key, multi, after, older, sha3, and, or, thresh)
and three templates:
| Template | Policy (reference arrekta_core.policy) | Guide |
|---|---|---|
| HTLC | OR(AND(KEY(receiver), SHA3(digest)), AND(KEY(refund), AFTER(timeout_height))) | HTLC swap |
| Vault | OR(KEY(primary), AND(KEY(recovery), OLDER(delay_blocks))) | Vault |
| Vesting | AND(KEY(holder), AFTER(unlock_height)) | Issue an asset |
Transaction expiry and relative locks
Transaction version 2 adds valid_until (a transaction is invalid after that height, error
tx-expired) and BIP 68-style relative locks through each input's
sequence (tx-premature-relative). Expiry stops stale PSTs,
swap offers and distributions from landing much later than intended.
Reserved kinds
Kinds 16–127 are valid to create and spendable by any non-empty witness until a soft fork gives them rules. Mempools treat them as non-standard, so nobody uses them by accident. See Soft-fork upgrades.