Before this patch Bob is not sending message 3. This is because we are not
polling Bob's swarm correctly. To fix it we can just mimic the other NB's and
bubble up an event when Bob receives message 3 response from Alice, this way we
can `await` upon this event which triggers polling, making Bob's swarm send the
message.
Unfortunately, I had to put the wrap the swarm in Alice's `Network`
struct in an `Arc<Mutex<T>>` in order to be able to use `backoff` to
control the retry mechanism. This is because the stream of events
cannot be turned into a `SharedFuture` (unlike Bob's).
It would be good to find an alternative solution.
In order for Alice to complete the handshake she needs to transition to state 3,
for this she needs message 2 from Bob.
Send `bob::Message2` to Alice and transition to `State3` - completing the
handshake.
Anything that needs to be re-exported by this crate from
`curve25519_dalek` can be re-exported from the `monero` module. In
fact, the `Scalar` type was already being re-exported.
Not doing so means that receiving an invalid encrypted signature from
Bob would make the generator produce a `RedeemBtc` action that should
not be accepted by the node (since Bob's signature would be invalid
after decrypting his encrypted signature).
It's better to fail early and let the user know what went wrong,
rather than let them hit an incomprehensible error when using their
wallet.
Also:
- Move generator functions to `alice` and `bob` modules. This makes
using `tracing` a lot easier, since the context of the file name let's
us differentiate between Alice's and Bob's generator logs more
clearly.
- Accept 0 confirmations when watching for the Monero lock
transaction. This should eventually be configured by the application,
but in the tests it's making things unexpectedly slower.
The database is now bound to a type eg. alice::State or bob::State.
The caller cannot expect to retrieve a type that is different to
the type that was stored.
ECDSAfun's serialisation implementation was already imported
making the custom implementation redundant
Remove remnants of stale comment
Remove redundant conversion
Rename params to be consistent
NOTE: This implementation saves secrets to disk! It is not
secure.
The storage API allows the caller to atomically record the state
of the protocol. The user can retrieve this recorded state and
re-commence the protocol from that point. The state is recorded
using a hard coded key, causing it to overwrite the previously
recorded state. This limitation means that this recovery
mechanism should not be used in a program that simultaneously
manages the execution of multiple swaps.
An e2e test was added to show how to save, recover and resume
protocol execution. This logic could also be integrated into the
run_until functions to automate saving but was not included at
this stage as protocol execution is currently under development.
Serialisation and deserialisation was implemented on the states
to allow the to be stored using the database. Currently the
secret's are also being stored to disk but should be recovered
from a seed or wallets.
- Make it the same for Alice and Bob.
- Make it contain a wallet client instead of the `Monero` struct.
Also:
Remove `Container` from inside `Monero` struct. The caller of `new`
can simply ensure that `Container` is not dropped to keep the
container alive.
This makes the `Monero` struct easier to work with, as it just holds
the data necessary to create the different clients created during
`init`, and does not have any lifetime restrictions.
This does introduce the ability of expressing incorrect combinations
of the enums `SwapFailed` and `Reason`, but these are just internal
to this function and it's terser that way.
This does introduce the ability of expressing incorrect combinations
of the enums `SwapFailed` and `Reason`, but these are just internal
to this function and it's terser that way.
This helps distinguish between the case where the refund timelock is
reached before the bitcoin is locked and the case where the refund
timelock is reached after the bitcoin is locked and before Alice sends
over the transfer proof for locking up the monero. In the first case
we can abort without doing anything, but in the second case we must
instruct the caller to refund the bitcoin.
By having two modules that declare and use the `harness` module we ran
into a situation where not all parts of the `harness` module were
being used by both of these other test modules. This was producing
annoying warnings.
For now this can be solved by marking the `harness` module declaration
as public. This has no real effect since modules under `/tests` cannot
be depended on elsewhere, but it satisfies the compiler. It does mean
that we will not hear about unused functions, but this change should
be temporary and it's a very minor problem given that it's just test
code.
Instead of checking once to see if Monero's `TxLock` has been
published, the new trait should keep looking until the transaction has
been found.
The new trait also allows the caller to set an expected number of
confirmations on the transaction.
The implementation of the trait is currently part of test code, but it
should be similar to what we will eventually do for an application.
Mimics what @thomaseizinger did here [1] and here [2].
This has the advantage that the consumer has more freedom to execute
`Action`s without having to implement particular traits. The error
handling required inside this protocol-executing function is also
reduced.
As discussed with Thomas, for this approach to work well, the
trait functions such as `receive_transfer_proof` should be infallible,
and the implementer should be forced to hide IO errors behind a retry
mechanism.
All of these asynchronous calls need to be "raced" against
the abort condition (determined by the `refund_timelock`), which is
missing in the current state of the implementation.
The initial handshake of the protocol has not been included here,
because it may not be easy to integrate this approach with libp2p, but
a couple of messages still need to exchanged. I need @tcharding to
tell me if it's feasible/good to do it like this.
[1]
https://github.com/comit-network/comit-rs/blob/move-nectar-swap-to-comit/nectar/src/swap/comit/herc20_hbit.rs#L57-L184.
[2] e584d2b14f/nectar/src/swap.rs (L716-L751).
There are no guarantees that send_message and receive_massage do not block
the flow of execution. Therefore they must be paired between Alice/Bob, one
send to one receive in the correct order.
Define Alice to call `receive_message` first, with Bob sending the message. Do
this because we are expecting Alice to be have a well known address, there is no
currently such assumption for Bob.
Previously there was a delay making a get raw transaction call to
give some time for a transaction to be confirmed on the blockchain.
This has been replaced with a loop that waits until the call is
succesful.
Previously we were testing the protocol by manually driving Alice and
Bob's state machines. This logic has now be moved to an async state
transition function that can take any possible state as input. The
state transition function is called in a loop until it returns the
desired state. This allows use to interrupt midway through the protocol
and perform refund and punish tests. This design was chosen over a
generator based implementation because the the generator based
implementation results in a impure state transition function that is
difficult to reason about and prone to bugs.
Test related code was extracted into the tests folder.
The 2b and 4b states were renamed to be consistent with the rest.
Macros were used to reduce code duplication when converting
child states to their parent states and vice versa.
Todos were added were neccessary.