Alice validates that incoming encsig messages are coming from the peer-id that is associated with the swap.
Encsig message from a peer-id different to the one associated with the swap are ignored.
This PR does a few things.
* It adds a TorTransport which either dials through Tor's socks5 proxy or via clearnet.
* It enables ASB to register hidden services for each network it is listening on. We assume that we only care about different ports and re-use the same onion-address for all of them. The ASB requires to have access to Tor's control port.
* It adds support to dial through a local Tor socks5 proxy. We assume that Tor is always available on localhost. Swap cli only requires Tor to be running so that it can send messages via Tor's socks5 proxy.
* It adds a new e2e test which swaps through Tor. For this we assume that Tor is currently running on localhost. All other tests are running via clear net.
442: Minor cleanups towards implementing a Monero wallet for local signing r=thomaseizinger a=thomaseizinger
Extracted out of #434.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
A `RequestResponseCodec` for pull-based protocols where the response is encoded using JSON.
This was added to more properly express the behavior of the quote protocol, where the dialer
doesn't send any message and expects the listener to directly send the response.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
- Listen on both tcp and websockets as default
- Listening addresses in config as array
- Configure fallback transport using `or_transport` - if listening on a given address fails on WS, we fall back to TCP.
Instead of forwarding every error, we deliberately ignore certain
variants that are not worth being printed to the log. In particular,
this concerns "UnsupportedProtocols" and "ResponseOmission".
To make this less verbose we introduce a macro for mapping a
`RequestResponseEvent` to `{alice,bob}::OutEvent`. We use a macro
because those `OutEvent`s are different types and the only other
way of abstracting over them would be to introduce traits that we
implement on both of them.
To make the macro easier to use, we move all the `From` implementations
that convert between the protocol and the more high-level behaviour
into the actual protocol module.
405: Concurrent swaps with same peer r=da-kami a=da-kami
Fixes#367
- [x] Concurrent swaps with same peer
Not sure how much more time I should invest into this. We could just merge the current state and then do improvements on top...?
Improvements:
- [x] Think `// TODO: Remove unnecessary swap-id check` through and remove it
- [x] Add concurrent swap test, multiple swaps with same Bob
- [ ] Save swap messages without matching swap in execution in the database
- [ ] Assert the balances in the new concurrent swap tests
- [ ] ~~Add concurrent swap test, multiple swaps with different Bobs~~
- [ ] ~~Send swap-id in separate message, not on top of `Message0`~~
Co-authored-by: Daniel Karzel <daniel@comit.network>
- Swap-id is exchanged during execution setup. CLI (Bob) sends the swap-id to be used in his first message.
- Transfer poof and encryption signature messages include the swap-id so it can be properly associated with the correct swap.
- ASB: Encryption signatures are associated with swaps by swap-id, not peer-id.
- ASB: Transfer proofs are still associated to peer-ids (because they have to be sent to the respective peer), but the ASB can buffer multiple
- CLI: Incoming transfer proofs are checked for matching swap-id. If a transfer proof with a different swap-id than the current executing swap is received it will be ignored. We can change this to saving into the database.
Includes concurrent swap tests with the same Bob.
- One test that pauses and starts an additional swap after the transfer proof was received. Results in both swaps being redeemed after resuming the first swap.
- One test that pauses and starts an additional swap before the transfer proof is sent (just after BTC locked). Results in the second swap redeeming and the first swap being refunded (because the transfer proof on Bob's side is lost). Once we store transfer proofs that we receive during executing a different swap into the database both swaps should redeem.
Note that the monero harness was adapted to allow creating wallets with multiple outputs, which is needed for Alice.
397: Always log at debug level to file r=rishflab a=rishflab
WILL SQUASH DOWN TO 3 COMMITS WHEN APPROVED!
Log at debug level to file
EnvFilter is applied globally. This means you cannot log at INFO level
to the terminal and at DEBUG level to log files. To get a around this
limitation I had to implement the layer trait on a new type and filter
in the on_event() trait method. Each swap has its own log file denoted
by its swap_id. The logger appends to the existing file when resuming a
swap.
Closes#278
I think the `DebugTerminalPritner` and `InfoTerminalPrinter` could be consolidated with some effort with some generics wizardry. It works for now and I think it can be done later. I wish in general there was a cleaner way to do this.
Co-authored-by: rishflab <rishflab@hotmail.com>
EnvFilter is applied globally. This means you cannot log at INFO level
to the terminal and at DEBUG level to log files. To get a around this
limitation I had to implement the layer trait on a new type and filter
in the on_event() trait method. Each swap has its own log file denoted
by its swap_id. The logger appends to the existing file when resuming a
swap.
Closes#278
396: Remove default connection details from CLI r=thomaseizinger a=rishflab
Connecting buyers to us by default is not consistent with our vision of
a decentralised network of sellers.
Closes#395
Co-authored-by: rishflab <rishflab@hotmail.com>
387: Improve the resilience of the network layer r=thomaseizinger a=thomaseizinger
We improve the resilience in two ways:
1. Use a timeout on Bob's side for the execution-setup.
2. Use the `bmrng` library to model the communication between Alice and Bob.
See commit messages for details.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Edge cases of UTXOs where value < fee cause the BDK's `coin_select` calculation to panic.
This issue was fixed upstream thus we point the BDK dependency against the commit of the merged fix.
It might very well be that the cancel transaction is already published.
If that is the case, there is no point in failing the command. We simply
transition to cancel and exit normally.
The reason this comes up now is because Alice now properly waits for
the cancel timelock as well and publishes the cancel transaction first.
Ultimately, she should not do that because there is no benefit to her
unless she can also publish the punish transaction.
Sending the transfer proof might never resolve because Bob doesn't
come back online. In that case, we need to make sure we bail out
as soon as the timelock expires.
We use the "precondition" feature of the `tokio::select!` macro to
avoid polling certain futures. In particular, we skip polling all
futures that - when resolved - require us to send a message to Alice.
This allows us to delay the ACKing of the encrypted signature up until
the swap has actually requested it.
Similarly, it allows us to wait for the ACK of the transfer proof within
the swap before continuing.
bmrng is a library providing a request-response channel that allows
the receiving end of the channel to send a response back to the sender.
This allows us to more accurately implement the functions on the
`EventLoopHandle`. In particular, we now _wait_ for the ACK of specific
messages from the other party before resolving the future.
For example, when sending the encrypted signature, the async function
on the `EventLoopHandle` does not resolve until we received the ACK
from the other party.
We also delete the `Channels` abstraction in favor of directly creating
bmrng channels. This allows us to directly control the channel buffer
which we set to 1 because we don't need more than that on Bob's side.
There is no point in first checking for the expired timelocks and
then constructing a `select!` that also watches for the timelock to
expiry.
We can simply only have the select! invocation to achieve the same
effect. In case the timelock is already expired, this future will
resolve immediately.
Normally, the polling order of `select!` is pseudo-random. We
configure it to be _biased_ here to make sure the futures are polled
in order.
The execution setup is our only libp2p protocol that doesn't have
a timeout built-in. Hence, if anything fails on Alice's side, we
would wait here forever.
Wrapping the future in a timeout ensures that we fail eventually
if this protocol doesn't succeed.
We don't need to hide the fields of this Behaviour as the only reason
for why this struct exists is because libp2p forces us to compose our
NetworkBehaviours into a new struct.
This allows loading the seller-peer-id from the database upon resuming a swap.
Thus, the parameters `--seller-peer-id` is removed for the `resume` command.
Other than the peer-id the multi address of a seller can change and thus is
still a parameter. This parameter might become optional once we add DHT support.
Awaiting the confirmations in an earlier state can cause trouble with resuming
swaps with short cancel expiries (test scenarios).
Since it is the responsibility of the refund state to ensure that the XMR can
be sweeped, we now ensure that the lock transaction has 10 confirmations before
refunding the XMR using generate_from_keys.
Sending the transfer transaction in a distinct state helps ensuring
that we do not send the Monero lock transaction twice in a restart
scenario.
Waiting for the first transaction confirmation in a separate state
helps ensuring that we send the transfer proof in a restart scenario.
Once we resume unfinished swaps upon startup we have to ensure that
it is safe for Alice to act.
If Bob has locked BTC it is only make sense for Alice to lock up the
XMR as long as no timelock has expired. Hence we abort if the BTC is
locked, but any timelock expired already.
In order for the re-construction of TxLock to be meaningful, we limit
`Message2` to the PSBT instead of the full struct. This is a breaking
change in the network layer.
The PSBT is valid if:
- It has at most two outputs (we allow a change output)
- One of the outputs pays the agreed upon amount to a shared output script
Resolves#260.
This allows us to construct instances of bitcoin::Wallet for test
purposes that use a different blockchain and database implementation.
We also parameterize the electrum-client to make it possible to
construct a bitcoin::Wallet for tests that doesn't have one. This
is necessary because the client validates the connection as it is
constructed and we don't want to provide an Electrum backend for
unit tests.
This allows us to remove all visibility modifiers from the message
fields because child modules (in this case {alice,bob}::state) can
always access private fields of structs.
It also moves the messages into a more natural place. Previously,
they were defined within the network layer even though they are
independent of the libp2p implementation.
To achieve this, we need to add some pure helpers to the state structs.
This has the added benefit that we can reduce the amount of code within
the swap function.
If TxLock does not confirm in a reasonable amount of time, Alice should
give up on the swap rather than waiting forever. Watching for TxLock in
the mempool is not required and it causes unnecessary complexity. What
if Alice does not see the transaction in mempool but it is already
confirmed? She will abort the swap for no reason.
Instead of watching for status changes directly on bitcoin::Wallet,
we return a Subscription object back to the caller. This subscription
object can be re-used multiple times.
Among other things, this now allows callers of `broadcast` to decide
on what to wait for given the returned Subscription object.
The new API is also more concise which allows us to remove some of
the functions on the actor states in favor of simple inline calls.
Co-authored-by: rishflab <rishflab@hotmail.com>
This also formats `log` events more nicely. Instead of
```
Mar 29 09:46:16.775 INFO log: Found message after comparing 82 lines log.target="testcontainers::core::wait_for_message" log.module_path="testcontainers::core::wait_for_message" log.file="/home/thomas/.cargo/registry/src/github.com-1ecc6299db9ec823/testcontainers-0.12.0/src/core/wait_for_message.rs" log.line=35
```
We now have
```
Mar 29 09:57:15.860 INFO testcontainers::core::wait_for_message: Found message after comparing 81 lines
```
Our strategy of searching for a english string to determine if
monero_wallet_rpc is ready is not compatible with languages other than
english. Instead we assume the monero rpc is ready if it has stopped
writing to stdout. We make a json rpc request to confirm this. A better
solution would have been to configure the monero_wallet_rpc to always
output in english but there is not command line argument to configure
the language.
Closes#353.
359: Bump bdk from 0.4.0 to 0.5.0 r=thomaseizinger a=dependabot[bot]
Bumps [bdk](https://github.com/bitcoindevkit/bdk) from 0.4.0 to 0.5.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/bitcoindevkit/bdk/blob/master/CHANGELOG.md">bdk's changelog</a>.</em></p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="f7944e871b"><code>f7944e8</code></a> Bump version to 0.5.0</li>
<li><a href="2fea1761c1"><code>2fea176</code></a> Bump deps version</li>
<li><a href="fa27ae210f"><code>fa27ae2</code></a> Update version in lib.rs</li>
<li><a href="46fa41470e"><code>46fa414</code></a> Update CHANGELOG with the new release tag</li>
<li><a href="8ebe7f0ea5"><code>8ebe7f0</code></a> Merge commit 'refs/pull/308/head' of github.com:bitcoindevkit/bdk into releas...</li>
<li><a href="eb85390846"><code>eb85390</code></a> Merge commit 'refs/pull/309/head' of github.com:bitcoindevkit/bdk into releas...</li>
<li><a href="dc83db273a"><code>dc83db2</code></a> better derivation path building</li>
<li><a href="201bd6ee02"><code>201bd6e</code></a> better derivation path building</li>
<li><a href="396ffb42f9"><code>396ffb4</code></a> handle descriptor xkey origin</li>
<li><a href="9cf62ce874"><code>9cf62ce</code></a> [ci] Manually install libclang-common-10-dev to 'check-wasm' job</li>
<li>Additional commits viewable in <a href="https://github.com/bitcoindevkit/bdk/compare/v0.4.0...v0.5.0">compare view</a></li>
</ul>
</details>
<br />
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bdk&package-manager=cargo&previous-version=0.4.0&new-version=0.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
370: No Bitcoin deposit for Alice r=da-kami a=da-kami
The message to deposit Bitcoin only applies to Bob, not Alice.
Alice does not require any initial Bitcoin.
Co-authored-by: Daniel Karzel <daniel@comit.network>
The request-response behaviour that is used for sending the transfer
proof actually has a functionality for buffering a message if we
are currently not connected. However, the request-response behaviour
also emits a dial attempt and **drops** all buffered messages if this
dial attempt fails. For us, the dial attempt will very likely always
fail because Bob is very likely behind NAT and we have to wait for
him to reconnect to us.
To mitigate this, we build our own buffer within the EventLoop and
send transfer proofs as soon as we are connected again.
Resolves#348.
The swap should not be concerned with connection handling. This is
the responsibility of the overall application.
All but the execution-setup NetworkBehaviour are `request-response`
behaviours. These have built-in functionality to automatically emit
a dial attempt in case we are not connected at the time we want to
send a message. We remove all of the manual dialling code from the
swap in favor of this behaviour.
Additionally, we make sure to establish a connection as soon as the
EventLoop gets started. In case we ever loose the connection to Alice,
we try to re-establish it.
Decomposing a RequestResponseEvent is quite verbose. We can introduce
a helper function that does the matching for us and delegates to
specific `From` implementations for the protocol specific bits.
339: Bump dependency versions r=thomaseizinger a=thomaseizinger
Otherwise it will take a long time for dependabot to update all of
these.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
351: Show the actual BTC amount and fee to be swapped r=da-kami a=da-kami
We got user feedback, that it is confusing that the amount "found" in the wallet does not match the amount actually being swapped, thus with this PR we explicitly display the amount swapped and fees.
Co-authored-by: Daniel Karzel <daniel@comit.network>
319: Alice sweeps refunded funds into default wallet r=da-kami a=da-kami
Alice's refund scenario starts with generating the temporary wallet
from keys to claim the XMR which results in Alice' unloading the wallet.
Alice then loads her original wallet to be able to handle more swaps.
Since Alice is in the role of the long running daemon handling concurrent
swaps, the operation to close, claim and re-open her default wallet must
be atomic.
This PR adds an additional step, that sweeps all the refunded XMR back into
the default wallet. In order to ensure that this is possible, Alice has to
ensure that the locked XMR got enough confirmations.
These changes allow us to assert Alice's balance after refunding.
Co-authored-by: Daniel Karzel <daniel@comit.network>
If we enter a punish scenario we can be sure the punish timelock is expired.
Thus, we must be able to punish unless Bob published the refund transaction.
There is no benefit in racing punish against refund here, because we cannot recover from a punish tx failure anyway.
The logic was changed to:
Try to broadcast punish tx and await finality.
If either punish broadcasting of finality fails, try to fetch the refund transaction.
If it is available extract Bob's Monero key part and transition to refund.
If refund tx is not available fail without a status update.
Note that we do not distinguish different errors upon failure of punish, because
we cannot recover anyway. If we fail to retrieve Bob's refund tx, we just exit without
a status update so punish can be retried by resuming the swap.
Since Alice's refund scenario starts with generating the temporary wallet
from keys to claim the XMR which results in Alice' unloading the wallet.
Alice then loads her original wallet to be able to handle more swaps.
Since Alice is in the role of the long running daemon handling concurrent
swaps, the operation to close, claim and re-open her default wallet must
be atomic.
This PR adds an additional step, that sweeps all the refunded XMR back into
the default wallet. In order to ensure that this is possible, Alice has to
ensure that the locked XMR got enough confirmations.
These changes allow us to assert Alice's balance after refunding.
To achieve this, we decompose `watch_for_locked_xmr` into two parts:
1. A non-self-consuming function to construct a `WatchRequest`
2. A state transition that can now consume `self` again because
it is only called once within the whole select! expression.
Ideally, we would move more logic onto this state transition (like
comparing the actual amounts and fail the transition if it is not
valid). Doing so would have an unfortunate side-effect: We would
always wait for the full confirmations before checking whether or
not we actually receive enough XMR.
This allows us to have state transitions that consume self.
Instead of calling this function in all the branches, we can simply
make the whole match statement evaluate to the new state and perform
this functionality at the very end.
This allows us to move critical crypto logic onto `State3` which
holds all the necessary data which consequently allows us to get
rid of `lock_xmr` altogether by inlining it into the swap function.
The reduced indirection improves readability.
321: Properly handle concurrent messages to and from peers r=thomaseizinger a=thomaseizinger
Previously, we were forwarding incoming messages from peers to all
swaps that were currently running. That is obviously wrong. The new
design scopes an `EventLoopHandle` to a specific PeerId to avoid
this problem.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
322: Refactor `ExecutionParams` and harmonize sync intervals of wallets r=thomaseizinger a=thomaseizinger
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Previously, we were forwarding incoming messages from peers to all
swaps that were currently running. That is obviously wrong. The new
design scopes an `EventLoopHandle` to a specific PeerId to avoid
this problem.
We define the sync interval as 1/10th of the blocktime. For the
special case of our tests, we however check at max once per second.
The tests have a super fast blocktime. As such we shouldn't hammer
the nodes with a request every 100ms.
Bob does not care whether tx lock is confirmed. That is alice's problem.
This wait was introduced to remedy a bug in status_of_script() which was
failing when called on a transaction with no confirmations.
320: Fix env filter for asb r=thomaseizinger a=thomaseizinger
1. The asb didn't log any if the statements within main.rs
2. We were initializing unnecessary filters that don't make any sense
for the asb. warp and http are not used and the harness-es are for
test only.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
317: Fix monero refresh interval r=thomaseizinger a=thomaseizinger
The comparison should be the MAXIMUM of the two values, not the
minimum, otherwise we always refresh at an interval of 1 second.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
A non-interactive terminal is likely something along the lines of
journalctl which captures a timestamp by itself. In theory, it could
also be just a logfile but we rather accept this limitation and keep
the configuration surface simple rather than exposing another config
switch.
1. The asb didn't log any if the statements within main.rs
2. We were initializing unnecessary filters that don't make any sense
for the asb. warp and http are not used and the harness-es are for
test only.
We have a repeated pattern where we construct one of our
Tx{Cancel,Redeem,Punish,Refund,Lock} transactions and wait until
the status of this transaction changes. We can make this more
ergonomic by creating and implementing a `Watchable` trait that
gives access to the TxId and relevant script for this transaction.
This allows us to remove a parameter from the `watch_until_status`
function.
Additionally, there is a 2nd pattern: "Completing" one of these
transaction and waiting until they are confirmed with the configured
number of blocks for finality. We can make this more ergonomic by
returning a future from `broadcast` that callers can await in case
they want to wait for the broadcasted transaction to reach finality.
The execution params don't change throughout the lifetime of the
program. They can be set in the wallet at the very beginning.
This simplifies the interface of the wallet functions.
We achieve our optimizations in three ways:
1. Batching calls instead of making them individually.
To get access to the batch calls, we replace all our
calls to the HTTP interface with RPC calls.
2. Never directly make network calls based on function
calls on the wallet.
Instead, inquiring about the status of a script always
just returns information based on local data. With every
call, we check when we last refreshed the local data and
do so if the data is considered to be too old. This
interval is configurable.
3. Use electrum's notification feature to get updated
with the latest blockheight.
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Rishab Sharma <rishflab@hotmail.com>
We reduce indirection by constructing TxPunish directly based off
`State3` and make the type itself more powerful by moving the logic
of completing it with a signature onto it.
Instead of spawning the swap inside the event loop we send the swap back
to the caller to be spawned. This means we no longer need the remote handle
that was only used in the tests.
This now properly logs the swap results in production.
It also gives us more control over Alice's swap in the tests.
This allows us to have access to RedeemTx from within the scope
of the state transition which we are going to need for more
efficient watching of what happens to this TX on the blockchain.
The CLI has sensible default values for all parameters,
thus a config file is not really an advantage but just
keeps getting in our way, so re remove it.
306: Fix logging and retrying of Monero transaction watching r=thomaseizinger a=thomaseizinger
Hopefully, this should also reduce the load because I am not asking the node every second.
Related: https://github.com/comit-network/xmr-btc-swap/issues/202
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Daniel Karzel <daniel@comit.network>
Instead, we use a regular loop and extract everything into a function
that can be independently tested.
`backoff` would be useful to retry the actual call to the node.
This config setting makes backoff stop retrying if we didn't get an
error within this timeframe.
For us, this results in backoff not actually doing anything.
The connection to kraken is very long-running. It might be active
for hours without failing. However, the default value for
`max_elapsed_time` is set to 15 minutes. As such, once the connection
fails any time after that, backoff doesn't actually retry the operation
but just gives up.
Fixes#303.
In order to be able to re-connect on certain errors, we model
connection errors separately from parsing errors. We also change
the API of the whole module to no longer forward all errors to
the subscribers but instead, only update the subscribers with
either a latest rate or a permanent failure in case we exhausted
all our options to re-connect the websocket.
To model all of this properly, we introduce to sub-modules so that
each submodule can have their own `Error` type.
Resolves#297.
First, we tell the user that we are now waiting for Alice to lock
the monero. Additionally, we tell them once we received the
transfer proof which will lead directly into the
"waiting for confirmations" function.
The type hints are generated from the field names. This has the
unfortunate consequence of the config field becoming file_path which
does not really make sense people working on the codebase.
The bitcoin::Wallet::sync_wallet function doesn't do anything else
other than delegating. As such, we have just as much information
about what went wrong inside this function as we have outside.
By moving the .context call into the function, we can avoid repeating
us on every call-site.
288: Switch to public stagenet node that works r=rishflab a=rishflab
The xmr.to node has been unreliable lately. The exan.tech node seems to
working.
@da-kami is following up with making this configurable. Lets get this in so we can get a release on Friday.
Co-authored-by: rishflab <rishflab@hotmail.com>
Instead of leaking the tokio::sync:⌚:Receiver type in our
return value, we create a newtype that implements the desired
interface. This allows us to get rid of the `RateService` structs
and instead implement `LatestRate` directly on top of this struct.
Given that `LatestRate` is only used within the event_loop module,
we move the definition of this type into there.
271: Bob can verify that the XMR lock tx was published r=da-kami a=da-kami
The Monero `txhash` log was removed. I feel the user should have the possibility to verify that the transaction was actually published so I added the tx-hash to the confirmation output.
We could potentially print the tx-hash when receiving the transfer proof already, but that might not add much value compared to printing it with the confirmations.
Additionally we should allow the user to at least know when the XMR can be expected in the user's wallet, otherwise the swap ends like this:
```
2021-03-04 13:49:19 INFO Monero lock tx received 5 out of 5 confirmations
```
This is just not very informative - yes, the final transaction is an implementation detail, but I don't think we should hide the transactions from the user. By printing the tx-hash for spending from the lock-tx into the user wallet we ensure the user knows that the XMR can now be expected in the user wallet.
---
To add context, here the complete log (with debug enabled) **before** this change:
```
2021-03-04 13:30:46 DEBUG Database and seed will be stored in /Users/dakami/Library/Application Support/xmr-btc-swap
2021-03-04 13:30:46 DEBUG Starting monero-wallet-rpc on port 56145
2021-03-04 13:30:51 DEBUG Requesting quote
2021-03-04 13:30:51 INFO Received quote: 1 XMR = 0.00433500 BTC
2021-03-04 13:30:51 INFO Still got 0.01018746 BTC left in wallet, swapping ...
2021-03-04 13:30:51 INFO Spot price for 0.00500000 BTC is 1.153402537485 XMR
2021-03-04 13:30:52 DEBUG Starting execution setup with 12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi
2021-03-04 13:30:55 INFO Published Bitcoin 3a6690a962191529892318819fb20e7f1ac4625400e64ee734056a9b2a17ad8f transaction as lock
2021-03-04 13:41:13 DEBUG Received Transfer Proof from 12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi
2021-03-04 13:42:11 INFO Monero lock tx received 1 out of 5 confirmations
2021-03-04 13:45:33 INFO Monero lock tx received 2 out of 5 confirmations
2021-03-04 13:47:49 INFO Monero lock tx received 3 out of 5 confirmations
2021-03-04 13:48:56 INFO Monero lock tx received 4 out of 5 confirmations
2021-03-04 13:49:19 INFO Monero lock tx received 5 out of 5 confirmations
2021-03-04 13:49:19 DEBUG Encrypted signature sent
2021-03-04 13:49:19 DEBUG Alice acknowledged encrypted signature
2021-03-04 13:49:19 DEBUG watching for tx: e5569d3f0bcccac95252dffaebe74ead0360c09b76bc762de890aaa0e51afbcf
2021-03-04 13:49:20 DEBUG Received protocol error "missing transaction" from Electrum, retrying...
2021-03-04 13:49:22 DEBUG Received protocol error "missing transaction" from Electrum, retrying...
```
Co-authored-by: Daniel Karzel <daniel@comit.network>