-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request]: A way to automatically recover an ongoing swap's data if the user's local data-dir gets wiped #1160
Comments
We can start implementing such functionality by adding a cli mode to mm2. |
@sergeyboyko0791 If you started (or plan to start very soon) working on the issue, please assign yourself and add it to the MM2 project "In progress" column. You can do it on the issue page at the right: |
@artemii235 sorry, I forgot to do this |
* Add and implement a swap recoverer * Add the `recover_swap` RPC call * Fix PR issues * Rename `recover_swap` to `recreate_swap_data` * Handle the Taker `TakerPaymentSpent` event * Handle the Maker `TakerPaymentReceived`, 'TakerPaymentSpent' events * Fix PR issues * Rename `recover_swap.rs` to `recreate_swap_data.rs` * Handle error events * Fix PR issues * Push `MakerSwapEvent::TakerFeeValidated` if only Taker received `MakerPayment`
@sergeyboyko0791 Could you please prepare examples for documentation and transfer this issue to @smk762? |
@sergeyboyko0791 can you pls let me know the params and an example for this method? |
@smk762 thanks for helping! If you have questions, please feel free to ask here or DM :) Commandcurl --url "http://127.0.0.1:7783" --data "
{
\"userpass\": \"${userpass}\",
\"mmrpc\": \"2.0\",
\"method\": \"recreate_swap_data\",
\"params\": {
\"swap\": \"${opposite_swap_data}\"
},
\"id\": 0
}
" where export opposite_swap_data = '{
"type": "Taker",
"uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"my_order_uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"events": [
{
"timestamp": 1638984440546,
"event": {
"type": "Started",
"data": {
"taker_coin": "MORTY",
"maker_coin": "RICK",
"maker": "15d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732",
"my_persistent_pub": "03b1e544ce2d860219bc91314b5483421a553a7b33044659eff0be9214ed58addd",
"lock_duration": 7800,
"maker_amount": "0.9090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909091",
"taker_amount": "1",
"maker_payment_confirmations": 1,
"maker_payment_requires_nota": false,
"taker_payment_confirmations": 1,
"taker_payment_requires_nota": false,
"taker_payment_lock": 1638992240,
"uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"started_at": 1638984440,
"maker_payment_wait": 1638987560,
"maker_coin_start_block": 1207822,
"taker_coin_start_block": 1222573,
"fee_to_send_taker_fee": {
"coin": "MORTY",
"amount": "0.00001",
"paid_from_trading_vol": false
},
"taker_payment_trade_fee": {
"coin": "MORTY",
"amount": "0.00001",
"paid_from_trading_vol": false
},
"maker_payment_spend_trade_fee": {
"coin": "RICK",
"amount": "0.00001",
"paid_from_trading_vol": true
}
}
}
},
{
"timestamp": 1638984456603,
"event": {
"type": "Negotiated",
"data": {
"maker_payment_locktime": 1639000040,
"maker_pubkey": "0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732",
"secret_hash": "4da9e7080175e8e10842e0e161b33cd298cab30b",
"maker_coin_swap_contract_addr": null,
"taker_coin_swap_contract_addr": null
}
}
},
{
"timestamp": 1638984456814,
"event": {
"type": "TakerFeeSent",
"data": {
"tx_hex": "0400008085202f89016383e8aced2256378bb126a1ca1a41e2f344d9295f65b3ea4b99055c5eb4a6cb000000006a47304402201c7e661e0dbeb9b3eb6e4e9e3194010e5772227017772b2e48c1b8d48ed3b21f02201c2eda64e74455fa1878a5c221f25d22fe626abd0078a26a9fc0f829e0921639012103b1e544ce2d860219bc91314b5483421a553a7b33044659eff0be9214ed58adddffffffff02bcf60100000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac74c3e90b000000001976a91483762a373935ca241d557dfce89171d582b486de88ac08ebb061000000000000000000000000000000",
"tx_hash": "fcb49167c79e8e014143643b94878866f7e80b26c5a5dcf693010543da70b5bc"
}
}
},
{
"timestamp": 1638984457822,
"event": {
"type": "MakerPaymentReceived",
"data": {
"tx_hex": "0400008085202f8901c41fdf6b9d8aea4b472f83e4fa0d99dfafc245e897d681fd2ca7df30707fbf48020000006b483045022100c7b294bd46cbf3b13530879a43c5cf67414047266d8b64c3c7263b5e75b989ba02201974f38d688b184bc44e628806c6ab2ac9092f394729d0ce838f14e1e76117c001210315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732ffffffff03a2296b050000000017a91491c45f69e1760c12a1f90fb2a811f6dfde35cc35870000000000000000166a144da9e7080175e8e10842e0e161b33cd298cab30bac503d64000000001976a9141462c3dd3f936d595c9af55978003b27c250441f88ac09ebb061000000000000000000000000000000",
"tx_hash": "6287e0d30951cd859bfb837eb1e5409f7596e75ffeb2e61fd6df1843bfd0203d"
}
}
},
{
"timestamp": 1638984457826,
"event": {
"type": "MakerPaymentWaitConfirmStarted"
}
},
{
"timestamp": 1638984503611,
"event": {
"type": "MakerPaymentWaitConfirmFailed",
"data": {
"error": "An error"
}
}
},
{
"timestamp": 1638984503615,
"event": {
"type": "Finished"
}
}
],
"maker_amount": "0.9090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909091",
"maker_coin": "RICK",
"taker_amount": "1",
"taker_coin": "MORTY",
"gui": "atomicDEX 0.5.1 iOS",
"mm_version": "1b065636a",
"success_events": [
"Started",
"Negotiated",
"TakerFeeSent",
"MakerPaymentReceived",
"MakerPaymentWaitConfirmStarted",
"MakerPaymentValidatedAndConfirmed",
"TakerPaymentSent",
"TakerPaymentSpent",
"MakerPaymentSpent",
"Finished"
],
"error_events": [
"StartFailed",
"NegotiateFailed",
"TakerFeeSendFailed",
"MakerPaymentValidateFailed",
"MakerPaymentWaitConfirmFailed",
"TakerPaymentTransactionFailed",
"TakerPaymentWaitConfirmFailed",
"TakerPaymentDataSendFailed",
"TakerPaymentWaitForSpendFailed",
"MakerPaymentSpendFailed",
"TakerPaymentWaitRefundStarted",
"TakerPaymentRefunded",
"TakerPaymentRefundFailed"
]
}' Response{
"mmrpc": "2.0",
"result": {
"swap": {
"type": "Maker",
"uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"my_order_uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"events": [
{
"timestamp": 1638984440546,
"event": {
"type": "Started",
"data": {
"taker_coin": "MORTY",
"maker_coin": "RICK",
"taker": "b1e544ce2d860219bc91314b5483421a553a7b33044659eff0be9214ed58addd",
"secret": "0000000000000000000000000000000000000000000000000000000000000000",
"secret_hash": "4da9e7080175e8e10842e0e161b33cd298cab30b",
"my_persistent_pub": "0315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732",
"lock_duration": 7800,
"maker_amount": "0.9090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909091",
"taker_amount": "1",
"maker_payment_confirmations": 1,
"maker_payment_requires_nota": false,
"taker_payment_confirmations": 1,
"taker_payment_requires_nota": false,
"maker_payment_lock": 1639000040,
"uuid": "f87fa9ce-0820-4675-b85d-db18c7bc9fb4",
"started_at": 1638984440,
"maker_coin_start_block": 1207822,
"taker_coin_start_block": 1222573,
"maker_payment_trade_fee": null,
"taker_payment_spend_trade_fee": null
}
}
},
{
"timestamp": 1638984456603,
"event": {
"type": "Negotiated",
"data": {
"taker_payment_locktime": 1638992240,
"taker_pubkey": "03b1e544ce2d860219bc91314b5483421a553a7b33044659eff0be9214ed58addd",
"maker_coin_swap_contract_addr": null,
"taker_coin_swap_contract_addr": null
}
}
},
{
"timestamp": 1638984457822,
"event": {
"type": "TakerFeeValidated",
"data": {
"tx_hex": "0400008085202f89016383e8aced2256378bb126a1ca1a41e2f344d9295f65b3ea4b99055c5eb4a6cb000000006a47304402201c7e661e0dbeb9b3eb6e4e9e3194010e5772227017772b2e48c1b8d48ed3b21f02201c2eda64e74455fa1878a5c221f25d22fe626abd0078a26a9fc0f829e0921639012103b1e544ce2d860219bc91314b5483421a553a7b33044659eff0be9214ed58adddffffffff02bcf60100000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ac74c3e90b000000001976a91483762a373935ca241d557dfce89171d582b486de88ac08ebb061000000000000000000000000000000",
"tx_hash": "fcb49167c79e8e014143643b94878866f7e80b26c5a5dcf693010543da70b5bc"
}
}
},
{
"timestamp": 1638984457822,
"event": {
"type": "MakerPaymentSent",
"data": {
"tx_hex": "0400008085202f8901c41fdf6b9d8aea4b472f83e4fa0d99dfafc245e897d681fd2ca7df30707fbf48020000006b483045022100c7b294bd46cbf3b13530879a43c5cf67414047266d8b64c3c7263b5e75b989ba02201974f38d688b184bc44e628806c6ab2ac9092f394729d0ce838f14e1e76117c001210315d9c51c657ab1be4ae9d3ab6e76a619d3bccfe830d5363fa168424c0d044732ffffffff03a2296b050000000017a91491c45f69e1760c12a1f90fb2a811f6dfde35cc35870000000000000000166a144da9e7080175e8e10842e0e161b33cd298cab30bac503d64000000001976a9141462c3dd3f936d595c9af55978003b27c250441f88ac09ebb061000000000000000000000000000000",
"tx_hash": "6287e0d30951cd859bfb837eb1e5409f7596e75ffeb2e61fd6df1843bfd0203d"
}
}
},
{
"timestamp": 1638984503611,
"event": {
"type": "TakerPaymentValidateFailed",
"data": {
"error": "Origin Taker error event: MakerPaymentWaitConfirmFailed(SwapError { error: \"An error\" })"
}
}
},
{
"timestamp": 1638984503611,
"event": {
"type": "MakerPaymentWaitRefundStarted",
"data": {
"wait_until": 1639003740
}
}
}
],
"maker_amount": "0.9090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909091",
"maker_coin": "RICK",
"taker_amount": "1",
"taker_coin": "MORTY",
"gui": "nogui",
"mm_version": "",
"success_events": [
"Started",
"Negotiated",
"TakerFeeValidated",
"MakerPaymentSent",
"TakerPaymentReceived",
"TakerPaymentWaitConfirmStarted",
"TakerPaymentValidatedAndConfirmed",
"TakerPaymentSpent",
"TakerPaymentSpendConfirmStarted",
"TakerPaymentSpendConfirmed",
"Finished"
],
"error_events": [
"StartFailed",
"NegotiateFailed",
"TakerFeeValidateFailed",
"MakerPaymentTransactionFailed",
"MakerPaymentDataSendFailed",
"MakerPaymentWaitConfirmFailed",
"TakerPaymentValidateFailed",
"TakerPaymentWaitConfirmFailed",
"TakerPaymentSpendFailed",
"TakerPaymentSpendConfirmFailed",
"MakerPaymentWaitRefundStarted",
"MakerPaymentRefunded",
"MakerPaymentRefundFailed"
]
}
},
"id": null
} To import the result swap user can use the |
What is the minimum level of info in Most support queries so far, we get as little as an address and amount, and from there we can find usually find related txids. If we're lucky, user has the uuid also. |
Yes, the algorithm determines the input data because
At this moment, we can recreate an opposite swap file using info from seednodes. The opposite swap file must contain a correct locktime, a secret hash, etc. There are many fields that we actually can extract from the swap transactions, but know we expect only complete swap data requested from seednodes :( |
@sergeyboyko0791 @gcharang @smk762 Can we close this one? |
We have seen many times that a user's local data directory gets wiped for various reasons
On desktop: HDD/computer crash can cause corruption, and a user might delete the data-dir
On mobile: a user might reinstall the app for any number of reasons, which causes their local app data to be lost
Currently, if the user had an unfinished swap that had their coins locked in a p2sh address, they have no way to automatically continue and finish the swap after they reinstall and recover their wallet. Also, a slightly less serious concern is that even if there was no ongoing swap, wiping the data-dir causes past trade history to be lost, which might be important to some serious users who want to track them for tax or other bookkeeping reasons
The most straightforward solution I can think of for this issue is to integrate cloud service providers like Backblaze b2, AWS s3 (both support the s3 API), Dropbox or iCloud, and Google drive specifically for mobile. Swaps can be updated in the cloud after each swap event or once every 20 seconds or any other appropriate batching strategy. We could even use a versioning system like git for maximum recoverability. And, we can encrypt the data with a key derived from the user's seed words for maximum safety. This approach will allow recovery of ongoing swaps and also preserves the complete trade history in case of local data loss.
While straightforward, this solution might be very tedious/time-taking to implement in a user/dev-friendly manner across platforms. A minimalist alternative that shouldn't take a lot of additional work can be to store not just error data, but also ongoing swap JSONs on seed nodes. Not sure what the storage requirements will look like for seed nodes after this feature's addition though. The seed node's code can delete a swap's data as soon as it is finished (both parties claimed their coins) automatically. And, maybe by default, seed nodes can store the data of unfinished swaps for at least 15 days before deleting them. Or maybe 7 days for sure and more if they don't have a lot more than 1 GB of unfinished swap data in storage. Any new swap JSON added that makes the total data to spill over 1 GB could make the oldest swap JSON be deleted if it was more than 7 days old. Of course, the numbers or whole system seems like a sensible default to me but can be changed/exposed to a config, etc., allowing seed node runners the option to tweak them.
We can't store all of the ongoing swap JSONs on seed nodes, making them almost public, as it could lead to loss of privacy/coins if the other party of trade becomes aware of them(like secret hash), we could encrypt those particular fields with the user's (privkey or a hashed equivalent) as the encryption key.
This can allow easy reconstruction of ongoing swap data with a single query sent to seed nodes if the wallets were restored within a "reasonable" time frame
There is one last alternative that is to create an RPC method that takes as input: all the info a user can piece together from block explorer like their address, dexfee txn, taker payment txn/maker payment txn/any spend transactions and the networks they were done, etc., and try to intelligently output: a swap JSON that can be used by mm2 to recover/finish the swap. This is just automating one part of the manual resolution process of today. In my personal opinion, this option shouldn't be the only implemented solution for this issue. This is because even if GUI devs are able to translate each possible option of this RPC into an easily understandable interface for the end-users, 99% of them won't have the necessary knowledge to search through the block explorers and input the necessary details correctly. This will cause workload on our team and we may not be able to meet it if the user base becomes even 10x of what it is right now. Also, as @sergeyboyko0791 could explain more, it may not even be possible to recreate a swap JSON automatically and reliably for every user's case.
TLDR:
To solve the issue of lost local data causing swaps being unrecoverable without our manual help, there can be backups (cloud/seednode) or automatic recreation of swap JSON based on data a user/support-tech were able to piece together. I recommend the backup option for the long term to have the least friction. And cloud backup should be the ultimate goal.
cc: @ca333 @artemii235 @tonymorony @sergeyboyko0791
The text was updated successfully, but these errors were encountered: