Save Alice's peer-id in the db for Bob

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.
This commit is contained in:
Daniel Karzel 2021-03-30 15:40:59 +11:00 committed by Thomas Eizinger
parent bc442bcad3
commit d90496931b
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
4 changed files with 116 additions and 26 deletions

View File

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- The `resume` command of the `swap` CLI no longer require the `--seller-peer-id` parameter.
This information is now saved in the database.
### Added
- A changelog file.

View File

@ -21,7 +21,7 @@ use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use swap::bitcoin::{Amount, TxLock};
use swap::cli::command::{AliceConnectParams, Arguments, Command, Data, MoneroParams};
use swap::cli::command::{AliceMultiaddress, Arguments, Command, Data, MoneroParams};
use swap::database::Database;
use swap::env::{Config, GetConfig};
use swap::network::quote::BidQuote;
@ -81,9 +81,9 @@ async fn main() -> Result<()> {
match args.cmd {
Command::BuyXmr {
connect_params:
AliceConnectParams {
peer_id: alice_peer_id,
alice_peer_id,
alice_multi_addr:
AliceMultiaddress {
multiaddr: alice_addr,
},
monero_params:
@ -131,9 +131,12 @@ async fn main() -> Result<()> {
)
.await?;
let swap_id = Uuid::new_v4();
db.insert_peer_id(swap_id, alice_peer_id).await?;
let swap = Builder::new(
db,
Uuid::new_v4(),
swap_id,
bitcoin_wallet.clone(),
Arc::new(monero_wallet),
env_config,
@ -167,9 +170,8 @@ async fn main() -> Result<()> {
}
Command::Resume {
swap_id,
connect_params:
AliceConnectParams {
peer_id: alice_peer_id,
alice_multi_addr:
AliceMultiaddress {
multiaddr: alice_addr,
},
monero_params:
@ -189,6 +191,7 @@ async fn main() -> Result<()> {
init_monero_wallet(data_dir, monero_daemon_host, env_config).await?;
let bitcoin_wallet = Arc::new(bitcoin_wallet);
let alice_peer_id = db.get_peer_id(swap_id)?;
let mut swarm = swarm::new::<Behaviour>(&seed)?;
swarm.add_address(alice_peer_id, alice_addr);

View File

@ -37,8 +37,15 @@ pub struct Arguments {
pub enum Command {
/// Start a XMR for BTC swap
BuyXmr {
#[structopt(
long = "seller-peer-id",
default_value = DEFAULT_ALICE_PEER_ID,
help = "The peer id of a specific swap partner can be optionally provided"
)]
alice_peer_id: PeerId,
#[structopt(flatten)]
connect_params: AliceConnectParams,
alice_multi_addr: AliceMultiaddress,
#[structopt(long = "electrum-rpc",
help = "Provide the Bitcoin Electrum RPC URL",
@ -60,7 +67,7 @@ pub enum Command {
swap_id: Uuid,
#[structopt(flatten)]
connect_params: AliceConnectParams,
alice_multi_addr: AliceMultiaddress,
#[structopt(long = "electrum-rpc",
help = "Provide the Bitcoin Electrum RPC URL",
@ -108,14 +115,7 @@ pub enum Command {
}
#[derive(structopt::StructOpt, Debug)]
pub struct AliceConnectParams {
#[structopt(
long = "seller-peer-id",
default_value = DEFAULT_ALICE_PEER_ID,
help = "The peer id of a specific swap partner can be optionally provided"
)]
pub peer_id: PeerId,
pub struct AliceMultiaddress {
#[structopt(
long = "seller-addr",
default_value = DEFAULT_ALICE_MULTIADDR,

View File

@ -3,10 +3,12 @@ pub use bob::Bob;
use anyhow::{anyhow, bail, Context, Result};
use itertools::Itertools;
use libp2p::PeerId;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::path::Path;
use std::str::FromStr;
use uuid::Uuid;
mod alice;
@ -63,7 +65,10 @@ impl Swap {
}
}
pub struct Database(sled::Db);
pub struct Database {
swaps: sled::Tree,
peers: sled::Tree,
}
impl Database {
pub fn open(path: &Path) -> Result<Self> {
@ -72,22 +77,51 @@ impl Database {
let db =
sled::open(path).with_context(|| format!("Could not open the DB at {:?}", path))?;
Ok(Database(db))
let swaps = db.open_tree("swaps")?;
let peers = db.open_tree("peers")?;
Ok(Database { swaps, peers })
}
pub async fn insert_peer_id(&self, swap_id: Uuid, peer_id: PeerId) -> Result<()> {
let peer_id_str = peer_id.to_string();
let key = serialize(&swap_id)?;
let value = serialize(&peer_id_str).context("Could not serialize peer-id")?;
self.peers.insert(key, value)?;
self.peers
.flush_async()
.await
.map(|_| ())
.context("Could not flush db")
}
pub fn get_peer_id(&self, swap_id: Uuid) -> Result<PeerId> {
let key = serialize(&swap_id)?;
let encoded = self
.peers
.get(&key)?
.ok_or_else(|| anyhow!("No peer-id found for swap id {} in database", swap_id))?;
let peer_id: String = deserialize(&encoded).context("Could not deserialize peer-id")?;
Ok(PeerId::from_str(peer_id.as_str())?)
}
pub async fn insert_latest_state(&self, swap_id: Uuid, state: Swap) -> Result<()> {
let key = serialize(&swap_id)?;
let new_value = serialize(&state).context("Could not serialize new state value")?;
let old_value = self.0.get(&key)?;
let old_value = self.swaps.get(&key)?;
self.0
self.swaps
.compare_and_swap(key, old_value, Some(new_value))
.context("Could not write in the DB")?
.context("Stored swap somehow changed, aborting saving")?;
// TODO: see if this can be done through sled config
self.0
self.swaps
.flush_async()
.await
.map(|_| ())
@ -98,7 +132,7 @@ impl Database {
let key = serialize(&swap_id)?;
let encoded = self
.0
.swaps
.get(&key)?
.ok_or_else(|| anyhow!("Swap with id {} not found in database", swap_id))?;
@ -129,7 +163,7 @@ impl Database {
}
fn all_swaps_iter(&self) -> impl Iterator<Item = Result<(Uuid, Swap)>> {
self.0.iter().map(|item| {
self.swaps.iter().map(|item| {
let (key, value) = item.context("Failed to retrieve swap from DB")?;
let swap_id = deserialize::<Uuid>(&key)?;
@ -277,4 +311,52 @@ mod tests {
assert_eq!(err.downcast_ref::<NotBob>().unwrap(), &NotBob);
}
#[tokio::test]
async fn can_save_swap_state_and_peer_id_with_same_swap_id() -> Result<()> {
let db_dir = tempfile::tempdir().unwrap();
let db = Database::open(db_dir.path()).unwrap();
let alice_id = Uuid::new_v4();
let alice_state = Alice::Done(AliceEndState::BtcPunished);
let alice_swap = Swap::Alice(alice_state);
let peer_id = PeerId::random();
db.insert_latest_state(alice_id, alice_swap.clone()).await?;
db.insert_peer_id(alice_id, peer_id).await?;
let loaded_swap = db.get_state(alice_id)?;
let loaded_peer_id = db.get_peer_id(alice_id)?;
assert_eq!(alice_swap, loaded_swap);
assert_eq!(peer_id, loaded_peer_id);
Ok(())
}
#[tokio::test]
async fn test_reopen_db() -> Result<()> {
let db_dir = tempfile::tempdir().unwrap();
let alice_id = Uuid::new_v4();
let alice_state = Alice::Done(AliceEndState::BtcPunished);
let alice_swap = Swap::Alice(alice_state);
let peer_id = PeerId::random();
{
let db = Database::open(db_dir.path()).unwrap();
db.insert_latest_state(alice_id, alice_swap.clone()).await?;
db.insert_peer_id(alice_id, peer_id).await?;
}
let db = Database::open(db_dir.path()).unwrap();
let loaded_swap = db.get_state(alice_id)?;
let loaded_peer_id = db.get_peer_id(alice_id)?;
assert_eq!(alice_swap, loaded_swap);
assert_eq!(peer_id, loaded_peer_id);
Ok(())
}
}