NUT-14: Hashed Timelock Contracts (HTLCs)
optional
depends on: NUT-10, NUT-11
extended by: NUT-28
This NUT describes the use of Hashed Timelock Contracts (HTLCs)
which defines a spending condition based on NUT-10’s well-known Secret
format. Using HTLCs, ecash proofs can be locked to the hash of a
preimage and a timelock. This enables use cases such as atomic
swaps of ecash between users, and atomic coupling of an ecash
spending condition to a Lightning HTLC.
HTLC spending conditions can be thought of as an
extension of P2PK locks (see NUT-11) but with a hash lock in
Secret.data and a new
Proof.witness.preimage witness in the locked inputs
to be spent. The preimage used to spend a locked
Proof can be retrieved using NUT-07.
Caution: applications that rely on being able to retrieve the witness independently of the spender must check, via the mint’s info endpoint, that NUT-07 is supported.
Caution: if the mint does not support this type of spending condition, proofs may be treated as regular anyone-can-spend proofs. Applications must ensure that the mint supports a specific kind of spending condition by checking the mint’s info endpoint.
HTLC Locked Proof
NUT-10 Secret
kind: HTLC
If for a Proof, Proof.secret is a
Secret of kind HTLC, the hash of the
lock is in Proof.secret.data. The preimage for
unlocking the HTLC is in the witness
Proof.witness.preimage. All additional tags from P2PK
locks can also be used here, allowing a locktime, signature flag,
and multisig (see NUT-11).
Here is a concrete example of a Secret of kind
HTLC:
[
"HTLC",
{
"nonce": "da62796403af76c80cd6ce9153ed3746",
"data": "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c",
"tags": [
[
"pubkeys",
"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904"
],
["locktime", "1689418329"],
[
"refund",
"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e"
]
]
}
]
The hash lock in Secret.data and the preimage in
Proof.witness.preimage are treated as 32-byte data,
encoded as 64-character hexadecimal strings.
See NUT-11 for a description of the signature scheme, the additional use of signature flags, and how to require signatures from multiple public keys (Multisig).
Spending HTLC Proofs
A Proof with a Secret of kind
HTLC can be spent in two ways.
Receiver Pathway (hash lock)
The receiver(s) listed in the pubkeys tag can
spend the proof by providing BOTH of the
following:
- The preimage to
Secret.datain theProof.witness - Signature(s) as per the NUT-11 rules for Locktime MultiSig.
This pathway is ALWAYS available to the receivers, as possession of the preimage confirms performance of the Sender’s wishes.
NOTE: If the pubkeys tag is
absent, the preimage alone spends the proof; no signature is
required.
Sender Pathway (timelocked refund)
The sender(s) listed in the refund tag can spend
the proof once the locktime lock has “expired” by
providing signature(s) as per the NUT-11
rules for Refund MultiSig.
NOTE: As per the NUT-11 rules, if the refund
tag is not present, the HTLC proof would become “anyone can spend”
after the locktime lock has “expired”. Likewise, if
the locktime is not present, or is not a valid unix
timestamp, the HTLC proof will be permanently locked and can only
be spent using the Receiver (hash lock) pathway.
Hash lock
Aligned with Bitcoin’s HTLC construction, the hash lock in
Secret.data represents the SHA-256
hash of a 32-byte preimage.
When an HTLC-locked proof is created, the
Secret.data field must contain:
hash_hex = bytes_to_hex(SHA256(preimage_bytes))
where:
preimage_bytesis exactly 32 bytes of arbitrary data, commonly random and uniformly distributedhash_hexis the 32-byte SHA-256 digest of preimage_bytes, encoded as a 64-character lowercase hexadecimal string
To successfully spend a Proof via the Receiver
Pathway, the spender must present the matching
preimage_bytes, encoded as a 64-character lowercase
hexadecimal string in the Proof.witness.preimage.
Mints and wallets must verify this equality before accepting the spend as valid:
SHA256(hex_to_bytes(Proof.witness.preimage)) == hex_to_bytes(Proof.secret.data)
Here is an example of a matching hash / preimage pair:
Proof.secret.data = 'ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5'
Proof.witness.preimage = '0000000000000000000000000000000000000000000000000000000000000001'
This hash-lock mechanism ensures that the Proof can only be spent once the secret preimage is revealed, allowing interoperability with external HTLC systems (such as Bitcoin or Lightning Network contracts).
Witness format
HTLCWitness is a serialized JSON string of the
form
{
"preimage": <hex_str>,
"signatures": <Array[<hex_str>]>
}
The witness for a spent proof can be obtained with a
Proof state check (see NUT-07).
Complex Example
This is an example Secret that locks a
Proof with an HTLC condition that can be spent in
either of the following ways:
Receiver Pathway - 2-of-3 signatures from the
public keys in the pubkeys tag PLUS
the preimage in Proof.witness.preimage of the hash in
Secret.data.
Sender Pathway - One signature from the public
keys in the refund tag, only available once the
locktime has expired.
The signature flag sigflag indicates that
signatures are necessary on the inputs and the
outputs of the transaction this Proof is
spent by.
[
"HTLC",
{
"nonce": "da62796403af76c80cd6ce9153ed3746",
"data": "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c",
"tags": [
["sigflag", "SIG_ALL"],
["n_sigs", "2"],
["locktime", "1689418329"],
[
"refund",
"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
"02e2aeb97f47690e3c418592a5bcda77282d1339a3017f5558928c2441b7731d50"
],
[
"pubkeys",
"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7"
]
]
}
]
Mint info setting
The NUT-06 nuts object
indicates support for this feature:
{
"14": {
"supported": true
}
}