Skip to content
LogoLogo

Arrekta payment channels (L2): specification

Status: draft v1, 27 October 2026. Phase 1 (this document and the arrekta-channels library) is built. Phases 2–4 (daemon, multi-hop routing, wallet integration) follow; see §12. This is off-chain protocol, not consensus: it needs no fork, and it uses only lock features consensus already has (PROTOCOL.md §4, policy locks).

1. Why channels, and why this design

Goal: instant, near-free payments in ARRK and in issued assets between parties who do not trust each other, settling on Arrekta only when they open or close.

What the research shows:

DesignHow it works elsewhereUsable with ML-DSA today?
Lightning payment channels2-of-2 funding, revocable commitments, HTLCs (BOLT 2/3/5)Yes, once revocation stops depending on elliptic-curve key tweaking (§4)
Statechains (Spark)Key handed over through threshold Schnorr (FROST)No. Threshold ML-DSA is still research: the 2026 NIST threshold-call previews (TALUS, Mithril, Quorus) have one open key-recovery claim
Ark (virtual UTXOs)Covenants, or many pre-signed transactions with MuSig2No. There is no ML-DSA signature aggregation, and pre-signing without it is impractical at about 3.3 KB per signature
Eltoo / LN-SymmetryNeeds SIGHASH_ANYPREVOUTNo. Arrekta signatures commit to the whole transaction
Sidechains / federationsCustodial multisig (Liquid: 11-of-15)Possible, but it is not trust-minimised

The gap this fills: Lightning's revocation derives keys by elliptic-curve point addition, which no post-quantum scheme offers. The 2026 PQLN paper adds ML-DSA and ML-KEM to Lightning's messages but keeps secp256k1 for the channel funds themselves. A post-quantum migration roadmap proposes "per-state hash commitments with post-quantum signatures" for revocation, but nobody has specified or built it. Arrekta can express it natively with SHA3 hashlocks in policy locks, which makes these the first channels that are post-quantum end to end: funds, revocation, messages and routing.

Deliberate differences from Lightning:

  1. Hash-based revocation (§4). Revealing the preimage of a per-state SHA3 hash revokes that state; there are no derived keys.
  2. No second-stage HTLC transactions (§5.4). Arrekta has only whole-transaction signatures (no SINGLE|ANYONECANPAY), so the delay goes directly into the HTLC output's policy. Each HTLC output can then be swept with one transaction. The cost is that HTLC expiry margins must exceed the self-delay (§8).
  3. Zero-fee commitments with anchors (§5.3). Commitments are TRUC (v3) transactions paying no fee. Whoever broadcasts one attaches the fee with a CPFP child spending their anchor, relayed as a package (PROTOCOL.md §13.2). There is no fee negotiation (update_fee) and no fee-related force-closes; this is where Lightning is heading too (BOLT #1228).
  4. Simplified updates (§6). One side proposes at a time, and both commitments always hold the same HTLC set. This is the "simplified update" variant proposed for Lightning, chosen for provable correctness over the last few percent of throughput.
  5. Assets are first class (§7). A channel is denominated in one asset (ARRK or any issued asset). Arrekta's strict asset conservation means nothing can be "trimmed" into fees.

2. Notation and constants

  • H(x) = SHA3-256. ‖ = concatenation. All integers little-endian (as in PROTOCOL.md).
  • Policies are written in the policy-lock language: key(K), multi(m, K…), sha3(D), after(h) (absolute height), older(n) (relative blocks), and, or.
  • A is the channel opener (funder) and B the acceptor. "Local" and "remote" are relative to the owner of a commitment.
ConstantValueWhy
COMMITMENT_VERSION3 (TRUC)Package relay with CPFP, pinning limits
ANCHOR_AMOUNT10,000 base units of ARRKA positive amount (consensus forbids zero-value spendable outputs) and cheap
ANCHOR_SWEEP_DELAY16 blocksAfter this, anyone may sweep an unused anchor (it keeps the UTXO set clean)
TO_REMOTE_DELAY1 blockThe remote output is spendable only after confirmation (pinning protection, as in anchor channels)
MAX_HTLCS30 per directionA commitment with 60 HTLCs stays under the 40 KB TRUC limit
default to_self_delay720 blocks (~24 h)The time a party has to punish a revoked state; negotiable from 144 to 4,032
MIN_FUNDING_DEPTH6 blocks, or final (a signer checkpoint), whichever comes first

3. Keys

Each party derives a fresh set per channel from its wallet's master seed (hardened path m/9000'/1'/channel'/k', same scheme as account keys):

KeyUsed in
fundingthe 2-of-2 funding output
paymentto_remote outputs paying this party, and HTLC success/timeout branches
delayedthis party's to_local output (after to_self_delay)
revocationthe revocation branches on the other party's commitments
anchorthis party's anchor
shachain seed32 bytes from which every per-commitment secret is derived (§4.2)

The keys are ML-DSA-65 by default; the policy-lock scheme mask allows ML-DSA-87.

4. Revocation without key tweaking

4.1 Rule

Every commitment n held by party P carries a revocation hash R(P,n) = H(s(P,n)), where s(P,n) is a secret only P knows. Every output that could benefit P on that commitment has a branch and(key(revocation of the other party), sha3(R(P,n))).

When P moves to state n+1, it sends s(P,n). From then on the other party can take every one of P's outputs on commitment n the moment it appears on chain, because P's own branches are delayed by to_self_delay. Publishing an old state therefore costs P everything.

4.2 Deriving and storing secrets (shachain over SHA3)

This follows BOLT 3's per-commitment secret generation with SHA3-256 in place of SHA-256:

  • s(n) = derive(seed, I) with I = 2^48 − 1 − n. The derivation walks bits 47 down to 0 of I, and for each set bit b it flips bit b of the running value and hashes.
  • The receiver stores at most 49 secrets and can rebuild any earlier one, because a secret with more trailing zero bits derives all secrets below it. It checks every new secret against what it has stored, so a peer cannot hand it a secret that fails to derive the earlier ones.
  • Before signing, each side sends the revocation hash for its next commitment, so updates never wait on a round trip for hashes.

5. Transactions

5.1 Funding

OutputAssetAmountLock
fundingthe channel assetchannel capacitymulti(2, A.funding, B.funding)
fee reserve (asset channels only)ARRK2 × ANCHOR_AMOUNTmulti(2, A.funding, B.funding)

The opener builds, signs and broadcasts the funding transaction only after holding B's signature on A's first commitment. A ARRK channel has no reserve output: its anchors come out of the opener's balance, as in Lightning.

Dual funding. Open may ask the acceptor to contribute (requested_funding, at the opener's fee_per_kb). The acceptor may put in up to that much: its Accept lists the contribution, its inputs and its change.

  1. The opener checks the contribution's accounting:
    • the channel asset it adds equals the contribution exactly;
    • its ARRK pays for its own inputs and outputs at the agreed rate.
  2. The opener builds the funding transaction from its own coins plus the acceptor's, and sends it unsigned in FundingCreated, with the outputs each input spends and its signature on the acceptor's commitment 0.
  3. The acceptor checks that the transaction carries its inputs once each, its change intact, and the right funding output. Then it answers FundingSigned and, only after that, FundingTxSigs with witnesses for its inputs.
  4. The opener adds its own witnesses and broadcasts.

Ordering: commitment signatures always go before transaction signatures, as in BOLT 2 interactive funding. So neither side signs coins into a 2-of-2 it cannot get back.

The acceptor's opening balance is its contribution. A peer that withholds FundingTxSigs only stops the opening; the opener's coins were never signed away.

5.2 Commitment (owned by P; Q is the other party)

  • version 3, lock_time 0.
  • Inputs: the funding output, plus the fee reserve for asset channels. Each input has sequence 0xFFFFFFFF.
  • Outputs, sorted by (asset, amount, lock bytes) so both sides build identical bytes:
OutputAssetAmountPolicy
to_local (P)channel assetP's balanceor(and(key(Q.revocation), sha3(R(P,n))), and(key(P.delayed), older(to_self_delay_P)))
to_remote (Q)channel assetQ's balanceand(key(Q.payment), older(1))
anchor_P, anchor_QARRKANCHOR_AMOUNT eachor(key(X.anchor), older(16))
offered HTLC (P → Q)channel assetHTLC amount§5.4
received HTLC (Q → P)channel assetHTLC amount§5.4
  • Empty outputs: an output with amount 0 is left out. In ARRK channels, the anchors come out of the opener's balance.
  • Delay: to_self_delay_P is the delay Q chose for P, so that Q has time to react.

5.3 Fees: zero-fee commitments

Commitments pay no fee, so their total output equals their total input. To get one confirmed, the broadcaster also sends a child that spends its own anchor plus wallet coins and pays the fee for both, as a package to /v1/txs/package (CPFP; PROTOCOL.md §13.2).

Because commitments never pay fees, the parties never negotiate or update fees. A commitment is never stuck below the fee floor, and a fee spike never forces a close.

5.4 HTLC outputs, with no second stage

For an HTLC with payment hash X and absolute expiry E, on P's commitment n:

HTLCPolicy
offered by P (P pays Q)or(and(key(Q.revocation), sha3(R(P,n))), or(and(key(Q.payment), sha3(X)), and(key(P.payment), and(after(E), older(to_self_delay_P)))))
received by P (Q pays P)or(and(key(Q.revocation), sha3(R(P,n))), or(and(key(P.payment), and(sha3(X), older(to_self_delay_P))), and(key(Q.payment), after(E))))

How each party gets its money:

  • Q (remote): claims with the preimage at once (offered), or after E (received). Neither needs a delay, because Q's claims on P's commitment can never be revoked.
  • P (local): both of P's own branches wait to_self_delay_P, just like to_local. That gives Q time to punish if this commitment turns out to be revoked.
  • Revoked commitment: Q takes every output with its revocation key and the revealed secret.

Lightning would put P's delay into a pre-signed second-stage transaction. Arrekta cannot sign part of a transaction, so the delay is in the output itself. The rule this imposes is in §8.

5.5 Cooperative close

  • Shape: a zero-fee TRUC (version 3) transaction spending the funding input (and the reserve, for asset channels). It has one output per party with a non-zero balance, at the destination each party named in Shutdown, sorted like a commitment. Both sides build identical bytes, so there is nothing to negotiate.
  • Fee: paid by CPFP. Either party attaches a child spending its own close output, as it would for a commitment anchor, and submits the pair as a package. Agreeing a fee in advance would bring back the stale-fee problem that zero-fee commitments solve (§5.3).
  • Anchors and reserve: the close has no anchors. In a ARRK channel the two anchor amounts go back to the opener's balance; in an asset channel the ARRK reserve goes back to the opener.
  • Preconditions: both Shutdowns were exchanged, no HTLC is pending, and no round is in progress. After a Shutdown no new HTLCs may be added; existing ones settle or fail.

5.6 Claim transactions (built by arrekta-channels, broadcast by the daemon)

SituationTransaction
Our commitment confirmedto_local after to_self_delay; our offered HTLCs after E + delay; our received HTLCs with the preimage after the delay
Their current commitment confirmedto_remote after 1 block; their offered HTLCs (received by us) with the preimage at once; our offered HTLCs after E
Their revoked commitment confirmedOne justice transaction taking every output (to_local and all HTLCs) through the revocation branches, before their delay runs out
AnchorCPFP child, or a sweep after 16 blocks

5.7 Splicing

Splicing changes a ARRK channel's capacity without closing it. A splice-in adds the initiator's coins; a splice-out pays out of its balance to any outputs.

The splice transaction:

  • Inputs: the funding output (shared, 2-of-2) plus the initiator's coins.
  • Outputs: the new funding output (same 2-of-2 lock, capacity ± the contribution) plus the initiator's outputs.
  • Fee: paid by the initiator, at the rate it proposes.
  • Result: commitment n+1 has the initiator's balance adjusted by the contribution.
StepMessageEffect
1SpliceInitcontribution, inputs, outputs, fee rate; both sides build the same transaction
2SpliceAckthe peer agrees
3SpliceCommit (both ways)signatures on each side's commitment n+1 on the new funding output
4SpliceTxSigs (initiator first)the 2-of-2 signature on the shared input, and the initiator's witnesses; then either side broadcasts
5SpliceLocked (both ways, at min_depth)revoke commitment n on the old funding output; the channel lives on the new one

Safety:

  • Commitment n+1 is held before anyone signs the splice. Commitment n is revoked only once the splice is buried, so whichever funding output survives, a valid commitment exists for it.
  • If the splice confirms but the peer never locks, the node adopts n+1 and can close on it; the old output is spent, so n can never confirm.
  • A splice nobody has signed is abandoned on reconnect (SpliceAbort).
  • The channel id stays the one from opening; the public graph re-announces the channel on its new funding output.

Limits of this version:

  • The channel must be quiescent (no HTLCs, no round in flight, no shutdown).
  • It stays quiescent until the splice locks, so payments pause for min_depth blocks. Lightning's design keeps both commitment sets updating in parallel meanwhile; that is the planned next step.
  • Asset channels do not splice yet. Their ARRK reserve output would need its own shared input.

6. Update protocol

Messages travel over the authenticated post-quantum P2P transport (PROTOCOL.md §12.1). Each one names the channel id, which is H(funding txid ‖ vout).

TypeMessageFields
1Opentemporary id, network magic, asset, capacity, to_self_delay the acceptor must use, keys, first two revocation hashes
2Accepttemporary id, to_self_delay the opener must use, keys, first two revocation hashes
3FundingCreatedfunding outpoint, optional ARRK reserve outpoint, the opener's signatures on the acceptor's commitment 0
4FundingSignedchannel id, the acceptor's signatures on the opener's commitment 0
5Readysent once the funding is at MIN_FUNDING_DEPTH
6UpdateBatcha list of updates (AddHtlc id/amount/hash/expiry/onion, FulfillHtlc id/preimage, FailHtlc id/reason), and signatures on the receiver's next commitment
7RevokeAndSignthe secret of the sender's current commitment, its next-but-one revocation hash, and signatures on the proposer's next commitment
8RevokeAndAckthe proposer's secret and next-but-one revocation hash
9Shutdownclose destination (any lock)
10ClosingSignedsignatures on the zero-fee close (§5.5)
11Reestablishcommitment number held, number of the peer's secrets received
12Errortext; the channel is failed and closed with the latest commitment
13FundingTxSigsdual funding: the acceptor's witnesses for its funding inputs (§5.1)
64–69SpliceInit, SpliceAck, SpliceCommit, SpliceTxSigs, SpliceLocked, SpliceAbortsplicing (§5.7)

Every message is u8 type ‖ body with little-endian integers and length-capped vectors; decoding rejects unknown types, truncation and trailing bytes. Signatures are one per funding input (two in an asset channel).

One round (simplified updates):

  1. The proposer sends UpdateBatch: its updates, and its signatures on the other party's next commitment.
  2. The other side applies every update to the current state, checks the signatures against the commitment it would own, and replies RevokeAndSign: the secret for its old commitment and its signatures on the proposer's next commitment.
  3. The proposer checks the signatures and the secret, commits, and replies RevokeAndAck with its own secret. The other side checks it and commits.

After each round both commitments hold the same balances and HTLC set. When both sides send UpdateBatch at once, the side with the lexically lower funding key goes first: the other abandons its proposal, answers, and re-proposes its updates afterwards. It also remembers the abandoned state, because the peer holds its signature on a commitment for it; that commitment carries the same revocation hash as the round that did happen, so it is revoked with it and justice applies to it too. Nobody revokes a state before holding a valid signature on the next one, so at every point each party can close unilaterally with its latest commitment.

Reconnecting: after a disconnect both sides send Reestablish:

  • their commitment number;
  • how many of the peer's secrets they hold, proved by including the last one;
  • whether a round is in flight.

Each side then retransmits exactly the message the other is missing (UpdateBatch, RevokeAndSign or RevokeAndAck).

Data-loss protection: a peer that proves it holds more of our secrets than we revealed means we lost state. Then we never broadcast our commitment, which is revoked; we wait for the peer to close. A peer that is behind is failed with our latest commitment. A claim without the right last secret is refused. Details: CHANNELD.md §4.

7. Assets and issuer controls

  • One asset per channel, set at open. HTLC amounts are in that asset; fees are always ARRK.
  • Conservation: every unit of the asset sits in an output of every commitment and close. There is no dust trimming. The minimum HTLC is 1 base unit, and the HTLC count limit keeps commitments small.
  • Issuer controls apply. If the issuer freezes a channel's funding lock or a party's address, or claws back, consensus enforces it on channel transactions as on any other. The opener's wallet warns when the asset is freezable or clawback-enabled. That is correct behaviour for regulated assets, not a bug.

8. Timelocks and safety rules

RuleWhy
to_self_delay is from 144 to 4,032 blocks; default 720Enough time to see and punish a revoked state, including through a watchtower
Close on-chain when E − tip ≤ to_self_delay + 144 for a received HTLC whose preimage we knowOur success branch waits to_self_delay after confirmation, and must land before the remote timeout at E
Forwarding: incoming expiry ≥ outgoing expiry + cltv_delta, with cltv_delta ≥ to_self_delay + 144No second-stage transactions, so a hop needs the delay plus a grace window to settle upstream (phase 3)
Final hop: E ≥ tip + min_final_expiry (default 288)
Reserve: each side keeps ≥ 1% of capacity after the first paymentRevocation must cost the cheater something
Funding usable at MIN_FUNDING_DEPTHReorg safety. A final (checkpointed) funding transaction is safe at once

9. Invoices

  • Contents:

    • network, payment hash and a payment secret;
    • asset and amount (the amount is optional for a "pay what you want" invoice);
    • description (≤ 256 bytes), creation time and expiry, minimum final expiry;
    • up to 4 route hints (private last hops, each with its forwarding policy);
    • the payee's identity key and onion key (ML-KEM-768).

    It is signed with ML-DSA by the payee's node identity.

  • Payment secret: it goes into the final onion payload. Only someone who saw the invoice can complete the payment, so intermediate hops cannot probe the payee (as in BOLT 11).

  • Encoding: binary with a version byte, carried as arrektapay: + base64url.

  • Size: a signed invoice is about 6.5 KB, too big for one QR code. Wallets show it as an animated UR (ur:arrekta-invoice, see docs/AIRGAP_SIGNING.md) or fetch it over HTTPS.

10. Security analysis (summary)

  • Revoked-state theft: every output that benefits the cheater waits to_self_delay, and the victim's justice transaction is valid at once. The secret check (§4.2) stops a peer from handing over a bad secret.
  • HTLC races: the rules in §8 make sure each party's claims confirm before the other's conflicting branch becomes valid.
  • Fees: zero-fee commitments plus anchors mean nobody depends on a fee agreed in the past; TRUC limits stop pinning by giant children.
  • Quantum: every signature (funding, commitments, claims) is ML-DSA, revocation is SHA3, the transport is X25519 + ML-KEM-768, and onions use ML-KEM (phase 3). No classical public-key primitive protects funds.
  • Not covered: a party that stays offline longer than to_self_delay while its peer publishes an old state. That is why watchtowers exist (phase 3).

11. Verification

node-rs/crates/arrekta-channels/tests/chain.rs builds every transaction in §5 and submits it to the real consensus code (an in-process regtest chainstate and mempool):

  • funding; payments in both directions; a cooperative close confirmed by CPFP;
  • a unilateral close with anchor CPFP (and that the zero-fee commitment alone is refused); to_remote after one block; to_local and the HTLC success and timeout branches refused before their delay or expiry and accepted after it;
  • a revoked commitment answered with a justice sweep, after which the cheater's delayed claim is invalid;
  • the same flows for an issued-asset channel with its ARRK reserve.

tests/protocol.rs runs randomised two-party sequences (property tests, including simultaneous proposals), with every message passed through the wire encoding. After every round it checks that both sides agree on the state, the channel total is conserved, each side holds the peer's secret for every superseded commitment, and each side recognises every revoked commitment the peer could broadcast. It also checks that forged signatures, bad secrets, wrong preimages, replays and malformed messages are rejected, and covers invoices.

12. Phases

PhaseContentsStatus
1This specification; arrekta-channels library (keys, shachain, policies, commitments, update state machine, closes, claims, justice, invoices) with consensus-validated testsbuilt
2arrekta-channeld daemon: P2P sessions with identity, persistence, reconnection with data-loss protection, chain watcher with automatic justice and sweeps, REST API, payments, regtest testsbuilt (CHANNELD.md)
3Multi-hop: ML-KEM onion routing (Outfox-style, fixed length), signed gossip checked against the chain, probabilistic pathfinding, forwarding with fees, watchtower server and clientbuilt
4SDKs (TypeScript, Python: invoice decoding, channel-node client), wallet-core (invoice preview, ur:arrekta-invoice, ChannelsWallet), desktop and mobile Instant payments screens, docs site (guide, API, design), Docker/Compose/release images, alertsbuilt
5Multi-path payments, dual funding, splicing (quiescent), browser extension, explorer channel viewbuilt
nextSplicing with HTLCs in flight (parallel commitments), splicing asset channels, liquidity ads, asset swaps across channels

Sources