This requires building a one-time use and dispose wallet
for the following bitcoin script:
BOLTZ NORMAL SWAP: p2shwsh
HASH160 <hash of the preimage>
EQUAL
IF <receiver public key>
ELSE <timeout block height>
CHECKLOCKTIMEVERIFY
DROP <sender public key>
ENDIF
CHECKSIG
BOLTZ REVERSE SWAP: p2sh
SIZE
[32]
EQUAL
IF
HASH160 <hash of the preimage>
EQUALVERIFY <receiver public key>
ELSE
DROP <timeout block height>
CLTV
DROP <sender public key>
ENDIF
CHECKSIG
This script captures the following spending conditions:
Either; a preimage and the receiver's signature is required // happy case (claimTx)
Or; after a timeout the senders signature is required. // dispute (refundTx)
The receiver
will be able to claim the funds on-chain,
We are the receiver in case of a reverse swap and this would be boltz in case of a normal swap.
The sender
will be able to claim funds on LN, once the receiver claims the on-chain funds and reveals the preimage.
We are the sender in the case of a normal swap, and boltz in the case of a reverse swap.
There is no requirement for a database as we will not persist and data.
We simply create keys, build a script, generate a single address corresponding to this key, watch the address for payment and spend the utxo by building a transaction, solving the spending conditions and broadcasting. We do not need to store transaction history or address indexes etc. This has to be handled by the client.
The client must ensure that they are rotating the keys and preimages being used. There are helper structs and methods for this.
In the case of normal swaps
; In the happy case, everything goes well, boltz pays our invoice and claims the on-chain funds.
The client (us) will ONLY be required to create the swap script and spend it in case boltz cheats and we need to claim back funds onchain from the script after a timeout.
We would be the sender
; and can only spend after a timeout in case of a dispute.
In the case of reverse
swaps; In the happy case, the client (us) will ALWAYS be required to build and spend from the script to claim on-chain funds. We would be the
receiver, and the solution we have to create for the reverse swap is the
preimageof a hash
and a `signature from our key.
For the most part, normal swaps only require interaction with the boltz.exchange
API, making it quite straightforward. In case of a dispute, we need to claim back funds from the on-chain script we funded, for which, we will need to build the script and spend it (refund tx).
For the sake of unifying the implementation challenge, we will look at the standard procedure when doing a reverse swap
happy case.
keypair.{seckey,pubkey}
hash
=sha256(preimage)keypair.pubkey
and hash
with boltz.exchange
redeem_script
along with an LN invoice
for us to pay and an onchain address
that they will fund for us to claimpubkey
and the timeout
usedour_pubkey, hash, boltz_pubkey and timeout
address
provided by boltzredeemScript
provided by boltzinvoice
(use local clightning)invoice
paid and send funds to the address
creating a utxo that we can spendinvoice
The procedure for liquid is the same as Bitcoin, with the addition of blinding logic associated with Asset
and Value
teleport A Proof of Concept on-chain utxo swap protocol.
bitcoin-wallet A simple rust bitcoin wallet
rust-bitcoin-wallet Another old simple rust bitcoin wallet - only upto Psbt building
boltz Boltz-core - solving the claim script
tdryja-ct Tadge Dryja's MIT Opencourseware presentation on Confidential Transactions
elements Docs from main elements repo on confidential transactions
The best place to start diving into this repo is tests
directory. This contains integration tests for bitcoin and liquid.
They contain the entire example of usage of the library.
Run all tests, except ignored tests
./test # test helper script
# OR MANUALLY
cargo test
cargo test -- --nocapture # for println! logs
To run the complete reverse swap integration test:
cargo test test_rsi -- --nocapture --include-ignored
test_rsi
is interactive.
It will block the terminal and prompt you to pay an ln invoice to proceed.
cargo test test_normal_swap -- --nocapture --include-ignored
test_normal_swap
is ignored since it requires always using a new invoice or else it errors with 409
So when manually testing, make sure you update the invoice variable.
For all ignored unit tests read the tests before running.
It's better to connect directly to boltz for testing to ensure payment routes are guaranteed.
Use botlz's 02* channel for testing with small amounts.
export FUND=2100000
lightning-cli --lightning-dir=/.lightning connect 029040945df331e634fba152ce6a21e3dfca87b68d275e078caeee4753f43e9acb 212.46.38.66:9736
lightning-cli --lightning-dir=/.lightning fundchannel 029040945df331e634fba152ce6a21e3dfca87b68d275e078caeee4753f43e9acb $FUND
This library makes the following assumptions:
In bitcoin, we use listunspent
and take the first utxo only (array 0 index). The only case where something could go wrong here is if the script at any point has more than one utxo, which is unlikely. Boltz will always fund the script address with the entire amount.
When we fetch utxos now, we expect a single utxo funded with the exact amount of the swap. If the amount does not match or if there is more than one utxo, we do not claim the transaction.
An improvement on this is to make the utxos field in SwapTx use a Vec and make sweeps always use all available/spendable utxos. We should also ensure that the amount is equal to or more than the value of the swap and accordingly notify the user of the unexpected amount.
Bitcoin reverse swap sweep/drain is 1 output
Liquid reverse swap sweep/drain is 1 confidential output and 1 explicit fee output
Liquid reverse swap utxo is always confidential If boltz funds the swap script with Explicit values, the library will error. It currently only handles Confidential transactions.
This library is developed and maintained by Bull Bitcoin (www.bullbitcoin.com).
Special thanks to: