Spending conditions, swaps and channels
Requant has limited smart contracts in the Bitcoin sense: fixed templates in consensus, not a script language. They cover what atomic swaps and payment channels need — one-way and two-way, with a penalty for cheating — so the protocols can be built in software without a later hard fork.
| Template | What it does | Active on the test network from |
|---|---|---|
| 2-of-2 | two keys must both sign | height 1400 |
| HTLC | claim with a secret, or refund after a timeout | height 1400 |
| Time locks | absolute (a block height) and relative (blocks after the coin) on any input | height 1400 |
| Revocable output | the owner after a relative delay, or the revocation key at once | height 4700 |
| Revocable HTLC | an HTLC with delays on its claim and refund paths and a revocation path | height 4700 |
| Anyone-can-pay | a signature over its own input and all outputs, so others may add inputs | height 4700 |
| k-of-n | off chain: FROST or MuSig2 aggregate ed25519 keys into one ordinary key | always |
On a main network all of them are active from genesis. Nodes older than 0.16.0 leave the test network at the first block that uses the templates of height 4700.
How a condition looks on the chain
An output always pays a 32-byte hash. Besides a key's pkh, it may be the hash of a condition, revealed only by the input that spends it. Until then a locked coin looks like any other, and locking coins is an ordinary transfer to the condition's address.
multi2 = H("requant/multi2", pubkey_a || pubkey_b)
htlc = H("requant/htlc", hash || claim_pkh || refund_pkh || LE64 timeout)
delayed = H("requant/delayed", owner_pkh || revoke_pkh || LE32 delay)
htlc-revocable = H("requant/htlc-revocable", hash || claim_pkh || refund_pkh || revoke_pkh
|| LE64 timeout || LE32 claim_delay || LE32 refund_delay)
Spending paths
A transfer that uses a condition or a lock is kind 2. Each input carries its locks (after_height, after_blocks) and an unlock:
| Unlock | Spends | Needs |
|---|---|---|
| 0 key | a key's output | the key |
| 1 multi2 | a 2-of-2 | both keys, signing the same message |
| 2 htlc claim | an HTLC | the claim key and the 32-byte secret whose SHA-256 is hash |
| 3 htlc refund | an HTLC | the refund key and after_height ≥ timeout |
| 4 delayed owner | a revocable output | the owner key and after_blocks ≥ delay |
| 5 delayed revoke | a revocable output | the revocation key |
| 6 revocable claim | a revocable HTLC | claim key, the secret and after_blocks ≥ claim_delay |
| 7 revocable refund | a revocable HTLC | refund key, after_height ≥ timeout, after_blocks ≥ refund_delay |
| 8 revocable revoke | a revocable HTLC | the revocation key |
The top bit of the unlock byte is the anyone-can-pay flag. Such an input signs H("requant/sighash-acp", chain_id || version || its own input || all outputs) instead of the whole transaction, so others may add inputs later; changing an output breaks the signature.
Time locks are checked for the block at height h that contains the transfer and the spent coin's height c: h ≥ after_height and h − c ≥ after_blocks. A pre-signed transfer with a lock cannot enter a block or a node's mempool early.
Design details that matter:
- The txid omits signatures, so pre-signed refund and commitment transactions keep their references.
- The secret is exactly 32 bytes and
hashits plain SHA-256, as Bitcoin-familyOP_SHA256and EVMsha256contracts check it. - A kind-2 encoding without any condition, lock or flag is invalid, so every transaction has exactly one encoding.
From the wallet
requant-wallet 0.5.0 describes conditions, locks coins under them and spends them along any path, with the time locks the path needs:
requant-wallet secret # a 32-byte secret and its sha256
requant-wallet pubkey my.wallet # public key of the current address
requant-wallet condition htlc <sha256> <claim> <refund> <timeout height> --out htlc.json
requant-wallet condition multi2 <pubkey A> <pubkey B> --out m2.json
requant-wallet condition delayed <owner> <revoke> <delay blocks> --out d.json
requant-wallet condition htlc-revocable <sha256> <claim> <refund> <revoke> <timeout> <claim delay> <refund delay> --out r.json
requant-wallet send my.wallet <condition address> 1 # lock 1 RQT under it
requant-wallet spend-condition my.wallet htlc.json claim <to> --preimage <secret>
requant-wallet spend-condition my.wallet htlc.json refund <to>
requant-wallet spend-condition a.wallet m2.json both <to> --out part.json # first signature
requant-wallet cosign b.wallet part.json # second signature, then send
--anyone-can-pay signs only the input and the outputs. The condition file holds the parameters and the address and is checked when it is read. The node's conditionaddress RPC computes the same addresses.
Fees on pre-signed transfers
Refunds and channel states are signed long before they are sent. Node 0.16.0 gives three ways to get them in when fees rise:
- Child pays for parent: blocks take transactions by the rate of the package they complete with their ancestors, so a child spending one of their outputs can pay for both.
- Anyone-can-pay: a transfer signed with the flag accepts an extra input that raises the fee.
- Replace by fee: a single-signer transfer can be replaced by one that pays more in total and at a higher rate.
Every transaction must still pay the minimum relay rate (1 atom per byte) on its own: nodes do not yet relay packages with a cheaper parent.
What can be built
HTLC swaps with Bitcoin-family and EVM chains
Party A picks a secret and publishes its SHA-256 hash. Both lock coins under that hash; the RQT timeout must be well after the other chain's. Claiming one side reveals the secret, which unlocks the other. The shared hash, amounts and timing link the two halves.
Monero swaps with adaptor signatures
RQT is locked in a 2-of-2 output, with cancel, refund and punish transfers on relative time locks. Requant and Monero both use ed25519, so no cross-curve proof is needed. Still to build: ed25519 adaptor signatures, MuSig2 aggregation, the protocol state machine and an external review.
One-way payment channels
A client deposits into a 2-of-2 with a refund to itself after an expiry height, signed by the server, and then signs ever larger payments to the server. A newer state always pays the server more, so no revocation is needed. This is the basis for paying network services such as MagnetGate per use.
Two-way channels with a penalty
Each party holds its own version of the latest commitment transfer from the 2-of-2 deposit. In it, its own balance pays a revocable output — itself after a delay, or the other party's revocation key at once — and the other party's balance pays the other party directly. Moving to a new state, each side gives the other the secret of its revocation key for the old one. Publishing a revoked state then lets the other side take the publisher's balance through the revocation path before the delay ends. Payments routed through channels sit in revocable HTLCs.
Status
- Built: the consensus templates and their tests, the pool policy, the wallet commands and
conditionaddress. - Not built: swap tools, a maker bot, channel software and package relay.
- Needed before real value: confirmation depth scaled to amount and hashrate, and an external review of the templates and the protocols.
Design notes: SWAPS.md. Normative rules: CHAIN.md §4.1–4.3.