diff --git a/README.md b/README.md index b4ff15f..25561f4 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![Test](https://github.com/hyahatiph-labs/mass/actions/workflows/test.yml/badge.svg)](https://github.com/hyahatiph-labs/mass/actions/workflows/test.yml) [![CodeQL](https://github.com/hyahatiph-labs/mass/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/hyahatiph-labs/mass/actions/workflows/codeql-analysis.yml) -[POC] [WIP] Java implementation of monero anon swaps server +[ POC ] / [ WIP ] Java implementation of monero anon swaps server This is an experimental Spring Boot Application that utilizes [LND hold invoices](https://wiki.ion.radar.tech/tech/research/hodl-invoice) -If successfully settled the equivalent amount in Monero is sent +If successfully settled the equivalent amount in Monero or Bitcoin is sent ## Development @@ -16,332 +16,19 @@ If successfully settled the equivalent amount in Monero is sent 2. The `src/main/resources/application.yml` can be configured as needed. 3. Run bitcoind on [regtest](https://developer.bitcoin.org/examples/testing.html) 4. Setup LND nodes for invoice generation and settling. *[Polar](https://lightningpolar.com/) is a cool tool! -5. Run Monero on [stagenet](https://monerodocs.org/infrastructure/networks/). Get those lovely piconeros at the [Stagenet Faucet](https://melo.tools/faucet/stagenet/) +5. Run Monero on two [stagenet](https://monerodocs.org/infrastructure/networks/) servers. Get those lovely piconeros at the [Stagenet Faucet](https://melo.tools/faucet/stagenet/) or by mining. 6. H2 db runs at host/h2-console. 7. Currently working on Bitcoin core 0.21, LND 0.13.x, Fedora 34, Java 11, Maven 3.6 and Monero 0.17.2 NOTE: currently have an issue with Monero digest authentication rpc calls, so use `--disable-rpc-login` ```bash -~/monero-x86_64-linux-gnu-v0.17.2.3/monero-wallet-rpc --rpc-bind-port=18083 --wallet-dir=/home/USER/Monero/wallets/mass/ --disable-rpc-login --daemon-address monero-stagenet.exan.tech:38081 --stagenet +~/monero-x86_64-linux-gnu-v0.17.2.3/monero-wallet-rpc --rpc-bind-port=18083 --wallet-dir=/home/USER/Monero/wallets/mass/ --disable-rpc-login --stagenet ``` -## BTC -> XMR API +## API -1. Generate a quote at GET `http://localhost:6789/quote/xmr` -2. Pay the (hold) `invoice` from response. Fund the consensus wallet by sending quote id and output from make_multisig_info -3. Send export_multisig_info and quote id to initialize the swap -4. Use the `quoteId` (aka preimage hash) and preimage to complete swap execution - -samples at `./api.http` - -health check at GET `http://localhost:6789/health` - -source code integrity hashes at GET `http://localhost:6789/integrity` - -obtain rate and bypass quote at GET `http://localhost:6789/rate` - -integrity response: - -```json -{ - "integrity": "93c53eedd4fd32d832a4d4086f42ba8397d2d1690242183c128785a6e8969c7c", - "btcQuoteController": "1d5b779a64b0ff237624cef9c2a897b21c6496e4eb61a48674d674020f7dbe43", - "btcSwapController": "c16f9a67df5f4060cddb9c1179f29a228052f11ad0cd839ccbe9fd64bbb6d26a", - "btcQuoteService": "05adb103051b7025fe95fb0b152680e02c2aa305374bd5711d7984d5d08a803c", - "btcSwapService": "a3b021dba3f97b8dcb4c81ddede140574c61a1407c9b77aacd0a2d3f6a4c9cbf", - "xmrQuoteController": "3d1b335f974985871cf77aa1ab90863b051f960e3ead20c08f7e247c43d68b39", - "xmrSwapController": "95296e96e25331c88bbaee1bf5c08e30bd9adbaf7bbd6ee2bb107d3e87d3a18f", - "xmrQuoteService": "a2465a8fe8e61ba97e627074d6742c52fa5656d3fa5a31dbe2d9d052758d3a33", - "xmrSwapService": "30b07e5568355079595f44fde25bc9696d9dd8977bf2b2d105ad54773365e057" -} -``` - -### Quote Request - -`address` - recipient's address to receive xmr - -`amount` - amount of monero requested - -`multisigInfo` - client's prepare_multisig info (from a newly generated wallet) - -`preimageHash` - client must generate a 32-byte array preimage hash and reveal the preimage to complete the swap - -#### Sample preimage generation in node.js - -```javascript -// import crypto module -const crypto = require("crypto") -// use cryptographically secure random bytes generation -const preimage = crypto.randomBytes(32) -// get the raw bytes from the buffer -const preimageRaw = JSON.parse(JSON.stringify(preimage)).data -// log the preimage raw bytes -console.log(`preimage bytes: ${preimageRaw}`) -// create SHA-256 hash -const hash = crypto.createHash('sha256') -hash.update(preimage); -// log preimage hash raw bytes -console.log(`hash bytes: ${new Uint32Array(hash.copy().digest())}`) -// log hex-encoded preimage -console.log(`hex: ${preimage.toString('hex')}`) -// log hex-encoded preimage hash -console.log(`hash: ${hash.copy().digest('hex')}`); -``` - -```json -{ - "address": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH", - "amount": 0.123, - "multisigInfo": "MultisigV1QUMnWLpohJt1va3RiRZJ5sMGzRuMCRxLu2L2oLdXEU5jSudoWf2L1UTJ89NUTqFqiRLriB2toCzquARxagDXaBZsKEGVSu9suSobN2jR68fgp4c5doHbKsWExZHQ4UkqcBJhd24VxTA4mR2Yw2eDGpeQ1kR8EW6aMdtozYeNkhKmBt94", - "preimageHash": [9,89,171,169,187,120,6,135,239,136,79,42,77,80,218,62,155,44,141,244,220,100,203,213,201,181,85,16,29,242,129,80] -} -``` - -### Quote Response -```json -{ - "amount": 0.123, - "destAddress": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH", - "invoice": "lnbcrt715930n1ps3scfzpp5p9v6h2dm0qrg0mugfu4y65x686djer05m3jvh4wfk423q80js9gqdq8d4shxuccqzpgxqrpcgsp506z2znrgcdxyrjd5egdae5n8fa5py8f4cf0x427nlqjgpv3067qq9qyyssqyssc3wn3xrgtw04wm8wamxglpge8cmu0qex6rd4c4jhz4cyggvh3mtaw9axf8hjvvtdl98jxjflscqp50tsu96j08xvzfmmc4akuwyqqw3xlja", - "maxSwapAmt": 10000000, - "minSwapAmt": 10000, - "swapMakeMultisigInfo": "MultisigV1apVSuNSr13h2sjc41NM4twGjavqUGuZXtEWWExcTsfrEahkE1AxKWDSggyNAAhMfvmcjjBSmJbRqfWGWjC9QGFAnEY64yFpkb4LCLmFmCiCNRwbJr5pWoCqnp4q7j8xy8m1R5gcw1vrrFVXhNzzKGZ3pq1RTupACz57qzhBbzDu9B6Ks", - "mediatorMakeMultisigInfo": "MultisigV1HQhvW8ANd9RMG8WqA68iVbAwWd9F7E1zbVSwv2sWcsD5HddsvBDQSamUUND5GDtyknDV55eXJYwxv22Fd4mZz4AKdMYwzFdaiYpYJHZcayxJDGeZ2naTCmb48DeWqQRiCj1Q3KdT24wJKHRJz8EY72fVezJHQU3S4aKMZ3JfKbnts8wq", - "swapFinalizeMultisigInfo": "MultisigxV1X69iiu6DQF3PcfhhqAEJgCKVA1vTforUs3GPNqN5uDjRU92SauVA1CGGu3HS7FC57WSkCeZn3dxhyMPAgBdnGgLB13fmdEUgYWx8pJt6AzRnYnBVAhkaFtp1BQTCQf1HEV6BDUfenBzFrnmioL9JUCB8FCisEyf6QXYGVW1uUgYxCevGNDzHKhmMJAvb2pYQTHazTdfeS2VXwfQGaVzVWaJZNGQJ", - "mediatorFinalizeMultisigInfo": "MultisigxV1X69iiu6DQF3PcfhhqAEJgCKVA1vTforUs3GPNqN5uDjRU92SauVA1CGGu3HS7FC57WSkCeZn3dxhyMPAgBdnGgLB13fmdEUgYWx8pJt6AzRnYnBVAhkaFtp1BQTCQf1HEV6BDUfenBzFrnmioL9JUCB8FCisEyf6QXYGVW1uUgYxCevGNDzHKhmMJAvb2pYQTHazTdfeS2VXwfQGaVzVWaJZNGQJ", - "quoteId": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", - "rate": 0.00582063, - "reserveProof": { - "signature": "ReserveProofV21AiqQ3aniJq1RHxjRQnD8AT4qqR4iQ2m75csXaJQszdNjEVT5DQSGM2FU9NFKN44ii44xpPx9maxSiEkcowi7aHhCeZe9B8EANcU4mD3FxBAGXHf4vsTFyAz9bZEUgURsQ7g8NR3dNnAP1pRSSWRTgdqMUV4fAWF666bbHbvt21WeH9e54V5p4E1HVMKKB3f41Ea1G5WDFMYecU34Jeic7gsJe4sKDnyp6FSeNnG6VNGuRnpi9jevjTVxPuUcHaW2c3FXrhe9dymviL44VY6Pn8uGVbtG6QfgKtAnR1xEhSqvkxnhLDUPMfyYjphayXXNWvXnPfKYp2YCZjiQ9nbvZcKj3oTEbMvYfGmKEkrN2qKFTq5TVRT74frGjUTDuJSzFAXWWmSPG6ZNMppnZB17rkNuLcTfFr2QtC8G5nD9Fvmei9FoyQAKi8K9SQarua", - "proofAddress": "54gqcJZAtgzBFnQWEQHec3RoWfmoHqL4H8sASqdQMGshfqdpG1fzT5ddCpz9y4C2MwQkB5GE2o6vUVCGKbokJJa6S6NSatn" - } -} -``` - -### Funding Request - -POST http://localhost:6789/swap/fund/xmr - -```json -{ - "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", - "makeMultisigInfo": "MultisigxV1SR6MxEnobnw8xYjJ2WCBNh4gQDHvLR1pX7df9xABRHHL1hbcb5A5NLgCxgEcNz61tpeofhcut9o6xWnFyhBpiGzLKfGTBomYYuN8P1ZPgjNpHBaXM52LStyhaAuFp43WAx2HdHKpVj9pXdmdVrhoWdGRNCKURwPYsPnP1idNvmVAxQajYYave3A4r6DYPzTAETac4pLijvR8ixT3kNgW1oj1RGLY" -} -``` - -### Funding Response - -/swap/fund/xmr response: - -```json -{ - "txid": "88546046d697e8c193fa6a85ab9d8374e9c65c10d2fafbc06223a60cf7c5ad2c", - "swapAddress": "53QeEYRzE8t4PuTu2VDb9DaWbNvgqYhgf3gEYdyWSoQNggyJJwwZd1nQpqzu3dhfZBXocUrtABLZX6xQY5i3fDT9JvCmYEb" -} -``` - -#### Mediator - -* Attempts to sweep funds if no action is taken t+3600s -* Max 3 retries -* src/main/services/monero/Mediator.java - - -### Initialize the Swap - -* NOTE: there appears to be a bug here with n_ouputs = 0 on the first try, so you need some retry logic here. It 'should' work on the retry but if not open an issue. - -* CANCELLING? don't send output from export_multisig_info, just send the hash - -POST http://localhost:6789/swap/initialize/xmr - -```json - -{ - "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", - "importInfo": "MultiSigInfo..." -} -``` - -### Initialize Response - -/swap/initialize/xmr response: -```json - -{ - "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", - "swapExportInfo": "MultiSigInfo...", - "mediatorExportInfo": "MultiSigInfo..." -} -``` - -### Swap - -POST http://localhost:6789/swap/xmr - -```json -{ - "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", - "preimage": [223,170,234,101,192,203,20,100,57,7,43,236,232,7,121,194,116,73,13,147,124,252,119,126,25,147,65,130,81,233,9,84] -} -``` - -/swap/xmr response: - -lncli - -```bash -user@server:~$ lncli -n regtest payinvoice $PAY_REQ -Payment hash: 02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94 -Description: mass -Amount (in satoshis): 77382 -Fee limit (in satoshis): 77382 -Destination: 03e420f400087f0640ee6dfd5b0b589908133c8cf36a737e2d0c3c908661477597 -Confirm payment (yes/no): yes -+------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ -| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE | -+------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ -| SUCCEEDED | 0.041 | 33.868 | 77382 | 0 | 792 | 713583046557696 | 03e420f400087f0640ee | -+------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ -Amount + fee: 77382 + 0 sat -Payment hash: 02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94 -Payment status: SUCCEEDED, preimage: xxx -``` - -```json - -{ - "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", - "multisigTxSet": "Multisig..." -} - -``` - -sign and submit the transaction - -* [sign_multisig](https://web.getmonero.org/resources/developer-guides/wallet-rpc.html#sign_multisig) - -* [submit_multisig](https://web.getmonero.org/resources/developer-guides/wallet-rpc.html#submit_multisig) - -### Cancel - -* At this point it is still possible to cancel the swap by sending the hash -* If export_multisig_info was sent during initialization then client must wait for 7200s timelock - - -POST http://localhost:6789/swap/cancel/xmr - -```json -{ - "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94" -} -``` - -## XMR -> BTC API - -1. Send reserve proof along with refund address, amount and prepare multisig info (two newly generated wallets) -2. Send quoteId and output from make multisig info x2, fund the multisig wallet prior to initializing -3. Send quoteId, export multisig x2 info and payment request generated from the preimage hash (< 7200 expiry). Derive the amount of sats to send from the quote. (rate x amount * COIN) -4. Send quoteId and multisigTxSet in exchange for preimage to complete the swap - -### Quote Request - -GET http://localhost:6789/quote/btc - -```json -{ - "amount": 0.123, - "proofAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", - "proofSignature": "ReserveProofV2...", - "refundAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", - "swapMultisigInfos": ["MultisigV1..."] -} -``` - -### Quote Response - -```json - -{ - "amount": 0.123, - "quoteId": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", - "refundAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", - "rate": 0.0055731800000000005, - "minSwapAmt": 10000, - "maxSwapAmt": 10000000, - "sendTo": "5BG7NvDurzu1Mnrb5akNfdZwrQK95m9g8JW61mnxbeFTDHHCoRmt8fQPuHjENNBSRKGtssLyH4xkrhbHKoSzquKpUFcgA3C", - "swapMakeMultisigInfo": "MultisigV1X...", - "swapFinalizeMultisigInfo": "Multisigx..." -} -``` - -### Funding Request - -POST http://localhost:6789/swap/fund/btc - -```json -{ - "hash": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", - "makeMultisigInfos": ["Multisigx...","Multisigx..."] -} -``` - -### Funding Response - -GET http://localhost:6789/quote/btc - -```json -{ - "txid": null, - "swapAddress": "57ZWGGmAqHBSsXgQrDpkSkPv4QQaqvCobG2ET29HbD81ZgoAPCsvpJDMp5tbSRt76XYGaREsjKCEG4y4kEzzr1avK4t15fw" -} -``` - -### Initialize Request - -POST http://localhost:6789/swap/initialize/btc - -```json -{ - "hash": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", - "importInfos": ["46895...","48ksfafi..."], - "paymentRequest": "lnbcrt..." -} -``` - -### Initialize Response - -```json -{ - "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", - "swapExportInfo": "MultiSigInfo...", - "mediatorExportInfo": null -} - -``` - -### Swap Request - -POST http://localhost:6789/swap/btc - -```json -{ - "hash": "3ac451b7910267ed82d656cd738fdb05994820ecc90a86b1a3a5c4699910ec07", - "txset": "...long hex..." -} -``` -### Swap Response - -```json -{ - "preimage": "88d093e1ee8ba149141bd1ee1e0c9a87b2579a201bab3f4435b24b6503ab59de" -} -``` -### Rate Lock Mode - -* `./src/main/application.yml` property `mode.rate-lock` -* There is default price confidence interval of 95% to handle the bug that causes /swap/initialize/btc to fail when large price fluctuations occur over swap duration. The value will be configurable as well. Markup must be > 0.05 in order to run a profitable swap server. Swaps with larger price fluctuations will be destroyed and new swaps must be created. Rate lock mode will not be enabled by default. +see [api.md](./api.md) ## Tests diff --git a/api.md b/api.md new file mode 100644 index 0000000..be34a47 --- /dev/null +++ b/api.md @@ -0,0 +1,318 @@ +# API + +samples at `./api.http` + +health check at GET `http://localhost:6789/health` + +source code integrity hashes at GET `http://localhost:6789/integrity` + +obtain rate and bypass quote at GET `http://localhost:6789/rate` + +integrity response: + +```json +{ + "integrity": "93c53eedd4fd32d832a4d4086f42ba8397d2d1690242183c128785a6e8969c7c", + "btcQuoteController": "1d5b779a64b0ff237624cef9c2a897b21c6496e4eb61a48674d674020f7dbe43", + "btcSwapController": "c16f9a67df5f4060cddb9c1179f29a228052f11ad0cd839ccbe9fd64bbb6d26a", + "btcQuoteService": "05adb103051b7025fe95fb0b152680e02c2aa305374bd5711d7984d5d08a803c", + "btcSwapService": "a3b021dba3f97b8dcb4c81ddede140574c61a1407c9b77aacd0a2d3f6a4c9cbf", + "xmrQuoteController": "3d1b335f974985871cf77aa1ab90863b051f960e3ead20c08f7e247c43d68b39", + "xmrSwapController": "95296e96e25331c88bbaee1bf5c08e30bd9adbaf7bbd6ee2bb107d3e87d3a18f", + "xmrQuoteService": "a2465a8fe8e61ba97e627074d6742c52fa5656d3fa5a31dbe2d9d052758d3a33", + "xmrSwapService": "30b07e5568355079595f44fde25bc9696d9dd8977bf2b2d105ad54773365e057" +} +``` + +## BTC -> XMR API + +1. Generate a quote at GET `http://localhost:6789/quote/xmr` +2. Pay the (hold) `invoice` from response. Fund the consensus wallet by sending quote id and output from make_multisig_info +3. Send export_multisig_info and quote id to initialize the swap +4. Use the `quoteId` (aka preimage hash) and preimage to complete swap execution + +### Quote Request + +`address` - recipient's address to receive xmr + +`amount` - amount of monero requested + +`multisigInfo` - client's prepare_multisig info (from a newly generated wallet) + +`preimageHash` - client must generate a 32-byte array preimage hash and reveal the preimage to complete the swap + +#### Sample preimage generation in node.js + +```javascript +// import crypto module +const crypto = require("crypto") +// use cryptographically secure random bytes generation +const preimage = crypto.randomBytes(32) +// get the raw bytes from the buffer +const preimageRaw = JSON.parse(JSON.stringify(preimage)).data +// log the preimage raw bytes +console.log(`preimage bytes: ${preimageRaw}`) +// create SHA-256 hash +const hash = crypto.createHash('sha256') +hash.update(preimage); +// log preimage hash raw bytes +console.log(`hash bytes: ${new Uint32Array(hash.copy().digest())}`) +// log hex-encoded preimage +console.log(`hex: ${preimage.toString('hex')}`) +// log hex-encoded preimage hash +console.log(`hash: ${hash.copy().digest('hex')}`); +``` + +```json +{ + "address": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH", + "amount": 0.123, + "multisigInfo": "MultisigV1QUMnWLpohJt1va3RiRZJ5sMGzRuMCRxLu2L2oLdXEU5jSudoWf2L1UTJ89NUTqFqiRLriB2toCzquARxagDXaBZsKEGVSu9suSobN2jR68fgp4c5doHbKsWExZHQ4UkqcBJhd24VxTA4mR2Yw2eDGpeQ1kR8EW6aMdtozYeNkhKmBt94", + "preimageHash": [9,89,171,169,187,120,6,135,239,136,79,42,77,80,218,62,155,44,141,244,220,100,203,213,201,181,85,16,29,242,129,80] +} +``` + +### Quote Response +```json +{ + "amount": 0.123, + "destAddress": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH", + "invoice": "lnbcrt715930n1ps3scfzpp5p9v6h2dm0qrg0mugfu4y65x686djer05m3jvh4wfk423q80js9gqdq8d4shxuccqzpgxqrpcgsp506z2znrgcdxyrjd5egdae5n8fa5py8f4cf0x427nlqjgpv3067qq9qyyssqyssc3wn3xrgtw04wm8wamxglpge8cmu0qex6rd4c4jhz4cyggvh3mtaw9axf8hjvvtdl98jxjflscqp50tsu96j08xvzfmmc4akuwyqqw3xlja", + "maxSwapAmt": 10000000, + "minSwapAmt": 10000, + "swapMakeMultisigInfo": "MultisigV1apVSuNSr13h2sjc41NM4twGjavqUGuZXtEWWExcTsfrEahkE1AxKWDSggyNAAhMfvmcjjBSmJbRqfWGWjC9QGFAnEY64yFpkb4LCLmFmCiCNRwbJr5pWoCqnp4q7j8xy8m1R5gcw1vrrFVXhNzzKGZ3pq1RTupACz57qzhBbzDu9B6Ks", + "mediatorMakeMultisigInfo": "MultisigV1HQhvW8ANd9RMG8WqA68iVbAwWd9F7E1zbVSwv2sWcsD5HddsvBDQSamUUND5GDtyknDV55eXJYwxv22Fd4mZz4AKdMYwzFdaiYpYJHZcayxJDGeZ2naTCmb48DeWqQRiCj1Q3KdT24wJKHRJz8EY72fVezJHQU3S4aKMZ3JfKbnts8wq", + "swapFinalizeMultisigInfo": "MultisigxV1X69iiu6DQF3PcfhhqAEJgCKVA1vTforUs3GPNqN5uDjRU92SauVA1CGGu3HS7FC57WSkCeZn3dxhyMPAgBdnGgLB13fmdEUgYWx8pJt6AzRnYnBVAhkaFtp1BQTCQf1HEV6BDUfenBzFrnmioL9JUCB8FCisEyf6QXYGVW1uUgYxCevGNDzHKhmMJAvb2pYQTHazTdfeS2VXwfQGaVzVWaJZNGQJ", + "mediatorFinalizeMultisigInfo": "MultisigxV1X69iiu6DQF3PcfhhqAEJgCKVA1vTforUs3GPNqN5uDjRU92SauVA1CGGu3HS7FC57WSkCeZn3dxhyMPAgBdnGgLB13fmdEUgYWx8pJt6AzRnYnBVAhkaFtp1BQTCQf1HEV6BDUfenBzFrnmioL9JUCB8FCisEyf6QXYGVW1uUgYxCevGNDzHKhmMJAvb2pYQTHazTdfeS2VXwfQGaVzVWaJZNGQJ", + "quoteId": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", + "rate": 0.00582063, + "reserveProof": { + "signature": "ReserveProofV21AiqQ3aniJq1RHxjRQnD8AT4qqR4iQ2m75csXaJQszdNjEVT5DQSGM2FU9NFKN44ii44xpPx9maxSiEkcowi7aHhCeZe9B8EANcU4mD3FxBAGXHf4vsTFyAz9bZEUgURsQ7g8NR3dNnAP1pRSSWRTgdqMUV4fAWF666bbHbvt21WeH9e54V5p4E1HVMKKB3f41Ea1G5WDFMYecU34Jeic7gsJe4sKDnyp6FSeNnG6VNGuRnpi9jevjTVxPuUcHaW2c3FXrhe9dymviL44VY6Pn8uGVbtG6QfgKtAnR1xEhSqvkxnhLDUPMfyYjphayXXNWvXnPfKYp2YCZjiQ9nbvZcKj3oTEbMvYfGmKEkrN2qKFTq5TVRT74frGjUTDuJSzFAXWWmSPG6ZNMppnZB17rkNuLcTfFr2QtC8G5nD9Fvmei9FoyQAKi8K9SQarua", + "proofAddress": "54gqcJZAtgzBFnQWEQHec3RoWfmoHqL4H8sASqdQMGshfqdpG1fzT5ddCpz9y4C2MwQkB5GE2o6vUVCGKbokJJa6S6NSatn" + } +} +``` + +### Funding Request + +POST http://localhost:6789/swap/fund/xmr + +```json +{ + "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", + "makeMultisigInfo": "MultisigxV1SR6MxEnobnw8xYjJ2WCBNh4gQDHvLR1pX7df9xABRHHL1hbcb5A5NLgCxgEcNz61tpeofhcut9o6xWnFyhBpiGzLKfGTBomYYuN8P1ZPgjNpHBaXM52LStyhaAuFp43WAx2HdHKpVj9pXdmdVrhoWdGRNCKURwPYsPnP1idNvmVAxQajYYave3A4r6DYPzTAETac4pLijvR8ixT3kNgW1oj1RGLY" +} +``` + +### Funding Response + +/swap/fund/xmr response: + +```json +{ + "txid": "88546046d697e8c193fa6a85ab9d8374e9c65c10d2fafbc06223a60cf7c5ad2c", + "swapAddress": "53QeEYRzE8t4PuTu2VDb9DaWbNvgqYhgf3gEYdyWSoQNggyJJwwZd1nQpqzu3dhfZBXocUrtABLZX6xQY5i3fDT9JvCmYEb" +} +``` + +#### Mediator + +* Attempts to sweep funds if no action is taken t+3600s +* Max 3 retries +* src/main/services/monero/Mediator.java + + +### Initialize the Swap + +* NOTE: there appears to be a bug here with n_ouputs = 0 on the first try, so you need some retry logic here. It 'should' work on the retry but if not open an issue. + +* CANCELLING? don't send output from export_multisig_info, just send the hash + +POST http://localhost:6789/swap/initialize/xmr + +```json + +{ + "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", + "importInfo": "MultiSigInfo..." +} +``` + +### Initialize Response + +/swap/initialize/xmr response: +```json + +{ + "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", + "swapExportInfo": "MultiSigInfo...", + "mediatorExportInfo": "MultiSigInfo..." +} +``` + +### Swap + +POST http://localhost:6789/swap/xmr + +```json +{ + "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", + "preimage": [223,170,234,101,192,203,20,100,57,7,43,236,232,7,121,194,116,73,13,147,124,252,119,126,25,147,65,130,81,233,9,84] +} +``` + +/swap/xmr response: + +lncli + +```bash +user@server:~$ lncli -n regtest payinvoice $PAY_REQ +Payment hash: 02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94 +Description: mass +Amount (in satoshis): 77382 +Fee limit (in satoshis): 77382 +Destination: 03e420f400087f0640ee6dfd5b0b589908133c8cf36a737e2d0c3c908661477597 +Confirm payment (yes/no): yes ++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ +| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE | ++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ +| SUCCEEDED | 0.041 | 33.868 | 77382 | 0 | 792 | 713583046557696 | 03e420f400087f0640ee | ++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+ +Amount + fee: 77382 + 0 sat +Payment hash: 02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94 +Payment status: SUCCEEDED, preimage: xxx +``` + +```json + +{ + "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94", + "multisigTxSet": "Multisig..." +} + +``` + +sign and submit the transaction + +* [sign_multisig](https://web.getmonero.org/resources/developer-guides/wallet-rpc.html#sign_multisig) + +* [submit_multisig](https://web.getmonero.org/resources/developer-guides/wallet-rpc.html#submit_multisig) + +### Cancel + +* At this point it is still possible to cancel the swap by sending the hash +* If export_multisig_info was sent during initialization then client must wait for 7200s timelock + + +POST http://localhost:6789/swap/cancel/xmr + +```json +{ + "hash": "02a69bb6043d2a4101502efef3da901095c7ea97c0e6bd277b9b7430de8a7b94" +} +``` + +## XMR -> BTC API + +1. Send reserve proof along with refund address, amount and prepare multisig info (two newly generated wallets) +2. Send quoteId and output from make multisig info x2, fund the multisig wallet prior to initializing +3. Send quoteId, export multisig x2 info and payment request generated from the preimage hash (< 7200 expiry). Derive the amount of sats to send from the quote. (rate x amount * COIN) +4. Send quoteId and multisigTxSet in exchange for preimage to complete the swap + +### Quote Request + +GET http://localhost:6789/quote/btc + +```json +{ + "amount": 0.123, + "proofAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", + "proofSignature": "ReserveProofV2...", + "refundAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", + "swapMultisigInfos": ["MultisigV1..."] +} +``` + +### Quote Response + +```json + +{ + "amount": 0.123, + "quoteId": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", + "refundAddress": "54Czx3ETpFZJPeYJC9RPvD9MzTrRjtdM4AYkryvQVD9259CUZ3shG99S53LzWFr5E1jJgd9fqZkzZbVz28N4oCzJBMLjDpj", + "rate": 0.0055731800000000005, + "minSwapAmt": 10000, + "maxSwapAmt": 10000000, + "sendTo": "5BG7NvDurzu1Mnrb5akNfdZwrQK95m9g8JW61mnxbeFTDHHCoRmt8fQPuHjENNBSRKGtssLyH4xkrhbHKoSzquKpUFcgA3C", + "swapMakeMultisigInfo": "MultisigV1X...", + "swapFinalizeMultisigInfo": "Multisigx..." +} +``` + +### Funding Request + +POST http://localhost:6789/swap/fund/btc + +```json +{ + "hash": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", + "makeMultisigInfos": ["Multisigx...","Multisigx..."] +} +``` + +### Funding Response + +GET http://localhost:6789/quote/btc + +```json +{ + "txid": null, + "swapAddress": "57ZWGGmAqHBSsXgQrDpkSkPv4QQaqvCobG2ET29HbD81ZgoAPCsvpJDMp5tbSRt76XYGaREsjKCEG4y4kEzzr1avK4t15fw" +} +``` + +### Initialize Request + +POST http://localhost:6789/swap/initialize/btc + +```json +{ + "hash": "58e778fa4223f30b14c2228edc31f6c1d0a461c12ef7587f34f0aa213ab8bba8", + "importInfos": ["46895...","48ksfafi..."], + "paymentRequest": "lnbcrt..." +} +``` + +### Initialize Response + +```json +{ + "hash": "0959aba9bb780687ef884f2a4d50da3e9b2c8df4dc64cbd5c9b555101df28150", + "swapExportInfo": "MultiSigInfo...", + "mediatorExportInfo": null +} + +``` + +### Swap Request + +POST http://localhost:6789/swap/btc + +```json +{ + "hash": "3ac451b7910267ed82d656cd738fdb05994820ecc90a86b1a3a5c4699910ec07", + "txset": "...long hex..." +} +``` +### Swap Response + +```json +{ + "preimage": "88d093e1ee8ba149141bd1ee1e0c9a87b2579a201bab3f4435b24b6503ab59de" +} +``` +### Rate Lock Mode + +* `./src/main/resources/application.yml` property `mode.rate-lock` +* There is default price confidence interval of 95% to handle the bug that causes /swap/initialize/btc to fail when large price fluctuations occur over swap duration. The value will be configurable as well. Markup must be > 0.05 in order to run a profitable swap server. Swaps with larger price fluctuations will be destroyed and new swaps must be created. Rate lock mode will not be enabled by default. \ No newline at end of file