Skip to content

Commit

Permalink
docs: Taproot scripting
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Feb 12, 2024
1 parent d180636 commit 0390f90
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 29 deletions.
3 changes: 3 additions & 0 deletions docs/README.md
Expand Up @@ -6,6 +6,9 @@ cover: .gitbook/assets/boltz-backend_header.png
coverY: 0
---

// TODO: websocket
// TODO: e2e example

# 👋 Introduction

## Instances
Expand Down
5 changes: 3 additions & 2 deletions docs/SUMMARY.md
@@ -1,10 +1,11 @@
# Table of contents

* [👋 Introduction](README.md)
* [🤖 REST API](api.md)
* [🤖 REST API v2](https://api.boltz.exchange/swagger)
* [🤖 REST API v1 (deprecated)](api.md)
* [🔁 Swap Types & States](lifecycle.md)
* [🙋♂ Claim & Refund Transactions](claiming-swaps.md)
* [🚫 Don't trust. Verify!](dont-trust.-verify.md)
* [🚫 Don't trust. Verify!](dont-trust-verify.md)
* [⏩ 0-conf](0-conf.md)
* [🚢 Backend Deployment](deployment.md)
* [🐳 Regtest Environment](regtest.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/api.md
Expand Up @@ -207,7 +207,7 @@ For UTXO chains, `/createswap` requests have to contain one additional parameter

Responses also contain one additional value:

* `redeemScript`: redeem script from which the `address` is derived. The redeem script should be used, [to verify](dont-trust.-verify.md#utxo-chain-address-verification) that Boltz did provide the correct address.
* `redeemScript`: redeem script from which the `address` is derived. The redeem script should be used, [to verify](dont-trust-verify#utxo-chain-address-verification) that Boltz did provide the correct address.

In case the address is for the Liquid Network, it will be blinded by a key that is also in the response:

Expand Down Expand Up @@ -478,7 +478,7 @@ For UTXO chains, `/createswap` requests have to contain one additional parameter

Responses also contain one additional value:

* `redeemScript`: Redeem script from which the lockup address is derived. The redeem script should be used, [to verify](dont-trust.-verify.md#utxo-chain-address-verification) that Boltz didn't try to cheat by creating an address without a HTLC.
* `redeemScript`: Redeem script from which the lockup address is derived. The redeem script should be used, [to verify](dont-trust-verify#utxo-chain-address-verification) that Boltz didn't try to cheat by creating an address without a HTLC.

In case the lockup address is on the Liquid Network, it will be blinded by a key that is returned in the response too:

Expand Down
114 changes: 93 additions & 21 deletions docs/claiming-swaps.md
Expand Up @@ -5,15 +5,101 @@ description: >-
swaps.
---

# 🙋 Claim & Refund Transactions
# 🙋 Claim & Refund Transactions

## Reverse Swaps: Claim Transactions
Boltz API clients need to craft and broadcast in order to:

Boltz API clients need to craft and broadcast **claim transactions** in order to claim chain bitcoin and successfully complete **Reverse Submarine Swaps**.
- **claim transactions** to claim chain bitcoin and successfully complete **Reverse Submarine Swaps**
- **refund transactions** to refund chain bitcoin to get back coins locked for failed **Submarine Swaps**

### UTXO Chains
## Taproot

Boltz currently supports two types of outputs:
> In case you are not familiar with Taproot yet, or want to refresh your memory, we recommend watching the [Taproot workshop of Bitcoin Optech](https://bitcoinops.org/en/schorr-taproot-workshop/)
All Taproot swaps are using tweaked public keys aggregated with [Musig2](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).
When a swap is created, the client provides its public key in the request and Boltz returns one in the response.
Those two public keys are aggregated and the ***Boltz public key always comes first***.
That aggregated public key is tweaked by the tagged hash of the Taptree of the scripts of the swap.
The result is the public key used in the [P2TR address](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) of the swap.

```
OP_1
<tweaked aggregated public key>
```

Key path spends should always be preferred over using a script path spends.
Not only, to save on miner fees but also, because key path spends are better for privacy.
Partial signatures from Boltz are always using `SIGHASH_DEFAULT`.

Examples for constructing Taproot swap transactions can be found in [boltz-core](https://github.com/BoltzExchange/boltz-core/blob/v2.1.0/lib/swap/Claim.ts#L124).

### Submarine Swaps

#### Claim

To allow Boltz to claim Submarine Swaps cooperative, `GET /swap/submarine/{id}/claim` to obtain the necessary information to create a partial signature.
Give Boltz your partial signature with `POST /swap/submarine/{id}/claim` and it will broadcast the key path spend claim transaction.

In case the client does not cooperate, Boltz will eventually broadcast a script path claim transaction to sweep the UTXO.

#### Refund

In case the swap failed (e.g. status `invoice.failedToPay` or `transaction.lockupFailed`) a key path refund can be done.
Get a partial signature from Boltz with `POST /swap/submarine/{id}/refund`, aggregate the partials and broadcast the transaction.
This can be done *before* time lock of the swap has expired.

Script path refunds are also possible after the time lock did expire.
Set the locktime of the transaction to >= the time lock of the swap and make sure to not use the max sequence in the inputs.
Structure the input witness like this:

```
<signature>
<refund script>
<control block>
```

### Reverse Swaps

Using the endpoint `POST /swap/reverse/{id}/claim`, Boltz gives you the values required to create an aggregated signature and broadcast a key path spend.

In case Boltz is not cooperating, a script path spend can be done via witness structured like this:

```
<signature>
<preimage>
<claim script>
<control block>
```

## EVM

On EVM chains, a deployed contract is used for enforcing swaps onchain.
The code of our contracts can be found in [boltz-core](https://github.com/BoltzExchange/boltz-core/tree/v2.1.0/contracts).
To fetch the addresses of the swap contracts Boltz is using `GET /chain/contracts`.

### Submarine Swaps

The `lock` function of the swap contract is used to lockup coins for a Submarine Swap.
All the parameter values are returned in the response when creating the swap.

With the `refund` function of the contract, locked coins can be refunded in case the swap failed.
This function takes almost the same parameters as `lock`, so the data from the response of the swap creation should be stored after locking coins in case they are needed again for a refund.
In case that information is not available, all parameters required for a refund can be queried from the `Lockup` event logs of the contract.
Those event logs are indexed by `refundAddress` which is the address with which the client locked the coins.

To refund before the time lock of the swap expired, an EIP-712 signature can be requested from Boltz.
`GET /swap/submarine/{id}/refund` to obtain that signature and use it in the `refundCooperative` function of the contract.
Similarly to cooperative Taproot refunds, Boltz will only return such a signature in case the swap has failed already.

### Reverse Swaps

To claim coins locked in the contract, use the function `claim`.
All parameters apart from the `preimage` are returned in the response when creating the swap.
The client needs to make sure to securely store the preimage after creating the swap to make sure that claiming the coins locked for it is possible.

## Legacy

Boltz supports two non-Taproot output types:

* [P2SH nested P2WSH](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#user-content-P2WSH\_nested\_in\_BIP16\_P2SH) for Normal Submarine Swaps
* [P2WSH](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#user-content-P2WSH) for Reverse Submarine Swaps
Expand All @@ -28,7 +114,7 @@ Claiming works a little different for every output type, but you always need the

#### P2SH nested P2WSH

When spending a P2SH nested P2WSH output, the signature, preimage and original reedem script are added to the witness of the input and one adds the Opcode `OP_0` and the SHA256 hash of the redeem script to the signature script of the input.
When spending a P2SH nested P2WSH output, the signature, preimage and original redeem script are added to the witness of the input and one adds the Opcode `OP_0` and the SHA256 hash of the redeem script to the signature script of the input.

#### P2WSH

Expand All @@ -38,18 +124,12 @@ To spend a P2WSH output, signature, preimage and original redeem script have to

Examples for all output types can be found in the [`boltz-core`](https://github.com/BoltzExchange/boltz-core/blob/v2.0.1/lib/swap/Claim.ts#L83) reference library.

### EVM Chains

The HTLCs that Boltz uses on EVM chains are not single use scripts, but contracts. The source of the contracts can be found [`here`](https://github.com/BoltzExchange/boltz-core/tree/v2.0.1/contracts).

## Normal Swaps: Refund transactions
### Normal Swaps: Refund transactions

Similar to claim transactions, Boltz API clients need to be able to craft and broadcast **refund transactions** for failed **Normal Submarine Swaps**. This section provides an overview of what refunds are, how they work and touches on the low-level scripting for your Boltz API client to successfully submit a refund.

> The concept of refunds currently only exists for failed Normal Submarine Swaps. In case of a failed Reverse Submarine Swaps, Lightning funds automatically bounce back to the user, no active refunding is needed.
### UTXO Chains

Refunding an output works just like claiming. Since the refund process doesn't need the preimage (or knows it but can't use it since that would require the claim keys) any value apart from the actual preimage can be used but there has to be a value to prevent the signature from being hashed and compared to the preimage hash. To save on transaction fees, we recommend using a 0 value.

There is one more difference when compared to claim transactions: the `nLockTime` of the transaction has to be set to a value equal to or greater than the timeout block height in the redeem script. Otherwise, the Bitcoin network will not accept your transaction.
Expand All @@ -58,10 +138,6 @@ There is one more difference when compared to claim transactions: the `nLockTime

An example can be found in the [`boltz-core`](https://github.com/BoltzExchange/boltz-core/blob/v2.0.1/lib/swap/Refund.ts) reference library. The function uses the claim function but requires the timeout block height as argument and sets an empty preimage.

### EVM Chains

The HTLCs that Boltz uses on EVM chains are not single use scripts, but contracts. In order to submit a refund, one calls the `refund` function of the contract. Similar to UTXO chains, Boltz API clients need to ensure a safe way to store the required values until the swap reaches a [final state](lifecycle.md). The source of the contracts can be found [`here`](https://github.com/BoltzExchange/boltz-core/tree/v2.0.1/contracts).

### Emergency Procedures

#### UTXO Chains
Expand Down Expand Up @@ -95,7 +171,3 @@ Example:
```

If a user lost all refund information, but still has access to the lightning invoice and can extract the preimage, this can be used to claim the locked bitcoin back to a user-controlled address. Feel free to [contact us](https://discord.gg/QBvZGcW) should you be in such a situation. We are happy to help!

#### EVM Chains

If refund information is lost involving an EVM Chain, one can retrieve the required values to call `refund` via contract call logs.
8 changes: 7 additions & 1 deletion docs/dont-trust.-verify.md → docs/dont-trust-verify.md
Expand Up @@ -13,7 +13,13 @@ As a general concept, Boltz API clients should not trust any critical informatio

## UTXO Chain Address Verification

Boltz currently supports two types of addresses:
### Taproot

The [scripting section](claiming-swaps.md) explains how P2TR addresses are created for Taproot swaps.

### Legacy

Boltz currently supports two types of addresses for legacy swaps:

* [P2SH nested P2WSH](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#user-content-P2WSH\_nested\_in\_BIP16\_P2SH) for Normal Submarine Swaps
* [P2WSH](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#user-content-P2WSH) for Reverse Submarine Swaps
Expand Down
5 changes: 3 additions & 2 deletions docs/lifecycle.md
Expand Up @@ -29,7 +29,8 @@ When a Normal Submarine Swap is created, it passes through the following states:
* `invoice.pending`: if paying the invoice is in progress
* `invoice.paid`: if paying the invoice was successful or
* `invoice.failedToPay`: if paying the invoice failed. In this case the user needs to broadcast a refund transaction to reclaim the locked up chain bitcoin.
6. `transaction.claimed`: indicates that after the invoice was successfully paid, the chain Bitcoin were successfully claimed _by Boltz_. This is the final status of a successful Normal Submarine swap.
6. `transaction.claim.pending`: Taproot swaps are not claimed immediately after the invoice has been paid. Boltz either waits for the client to create a cooperative signature for a key path spend or batches script path spends. This status indicates that Boltz is ready for the creation of a cooperative signature
7. `transaction.claimed`: indicates that after the invoice was successfully paid, the chain Bitcoin were successfully claimed _by Boltz_. This is the final status of a successful Normal Submarine swap.

If the user doesn't send chain bitcoin and the swap expires (approximately 24h), Boltz will set the state of the swap to `swap.expired` , which means that it was cancelled and chain bitcoin shouldn't be sent anymore. In case of `invoice.failedToPay` or `swap.expired` but bitcoin were sent, the user needs to submit a refund transaction to reclaim their locked chain bitcoin. For more information about how Boltz API clients can construct & submit refund transactions for users, check the [Claiming Swaps & Refunds](claiming-swaps.md) section. The states `invoice.failedToPay` and `swap.expired` are final since Boltz is _not_ monitoring refund transactions on the chain.

Expand All @@ -45,7 +46,7 @@ The following states are traversed in the course of a Reverse Submarine Swap:
4. `transaction.confirmed`: the lockup transaction was included in a block. This state is skipped, if the client optionally accepts the transaction without confirmation. Boltz broadcasts chain transactions non-RBF only.
5. `invoice.settled`: the transaction claiming chain Bitcoin was broadcasted by the user's client and Boltz used the preimage of this transaction to settle the Lightning invoice. This is the final status of a successful Reverse Submarine Swap.

The status `invoice.expired` is set when the invoice of Boltz expired and pending HTLCs are cancelled. Boltz invoices currently expire after [50% of the swap timeout window](https://github.com/BoltzExchange/boltz-backend/blob/master/lib/consts/Types.ts). If the swap expires without the lightning invoice being paid, the final status of the swap will be `swap.expired`.
The status `invoice.expired` is set when the invoice of Boltz expired and pending HTLCs are cancelled. Boltz invoices currently expire after 50% of the swap timeout window. If the swap expires without the lightning invoice being paid, the final status of the swap will be `swap.expired`.

In the unlikely event that Boltz is unable to send the agreed amount of chain bitcoin after the user set up the payment to the provided Lightning invoice, the status of the swap will be `transaction.failed` and the pending Lightning HTLC will be cancelled. The Lightning bitcoin automatically bounce back to the user, no further action or refund is required and the user didn't pay any fees.

Expand Down
5 changes: 4 additions & 1 deletion lib/api/Utils.ts
Expand Up @@ -14,7 +14,10 @@ type ApiArgument = {
};

// Some endpoints are getting spammed on mainnet, so we don't log the warnings for them
const errorsNotToLog: any[] = [ServiceErrors.SWAP_NO_LOCKUP().message];
const errorsNotToLog: any[] = [
ServiceErrors.SWAP_NO_LOCKUP().message,
ServiceErrors.ETHEREUM_NOT_ENABLED().message,
];

/**
* Validates that all required arguments were provided in the body correctly
Expand Down

0 comments on commit 0390f90

Please sign in to comment.