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] ## [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 ### Added
- A changelog file. - A changelog file.

View File

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

View File

@ -37,8 +37,15 @@ pub struct Arguments {
pub enum Command { pub enum Command {
/// Start a XMR for BTC swap /// Start a XMR for BTC swap
BuyXmr { 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)] #[structopt(flatten)]
connect_params: AliceConnectParams, alice_multi_addr: AliceMultiaddress,
#[structopt(long = "electrum-rpc", #[structopt(long = "electrum-rpc",
help = "Provide the Bitcoin Electrum RPC URL", help = "Provide the Bitcoin Electrum RPC URL",
@ -60,7 +67,7 @@ pub enum Command {
swap_id: Uuid, swap_id: Uuid,
#[structopt(flatten)] #[structopt(flatten)]
connect_params: AliceConnectParams, alice_multi_addr: AliceMultiaddress,
#[structopt(long = "electrum-rpc", #[structopt(long = "electrum-rpc",
help = "Provide the Bitcoin Electrum RPC URL", help = "Provide the Bitcoin Electrum RPC URL",
@ -108,14 +115,7 @@ pub enum Command {
} }
#[derive(structopt::StructOpt, Debug)] #[derive(structopt::StructOpt, Debug)]
pub struct AliceConnectParams { pub struct AliceMultiaddress {
#[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,
#[structopt( #[structopt(
long = "seller-addr", long = "seller-addr",
default_value = DEFAULT_ALICE_MULTIADDR, default_value = DEFAULT_ALICE_MULTIADDR,

View File

@ -3,10 +3,12 @@ pub use bob::Bob;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use itertools::Itertools; use itertools::Itertools;
use libp2p::PeerId;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::fmt::Display;
use std::path::Path; use std::path::Path;
use std::str::FromStr;
use uuid::Uuid; use uuid::Uuid;
mod alice; mod alice;
@ -63,7 +65,10 @@ impl Swap {
} }
} }
pub struct Database(sled::Db); pub struct Database {
swaps: sled::Tree,
peers: sled::Tree,
}
impl Database { impl Database {
pub fn open(path: &Path) -> Result<Self> { pub fn open(path: &Path) -> Result<Self> {
@ -72,22 +77,51 @@ impl Database {
let db = let db =
sled::open(path).with_context(|| format!("Could not open the DB at {:?}", path))?; 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<()> { pub async fn insert_latest_state(&self, swap_id: Uuid, state: Swap) -> Result<()> {
let key = serialize(&swap_id)?; let key = serialize(&swap_id)?;
let new_value = serialize(&state).context("Could not serialize new state value")?; 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)) .compare_and_swap(key, old_value, Some(new_value))
.context("Could not write in the DB")? .context("Could not write in the DB")?
.context("Stored swap somehow changed, aborting saving")?; .context("Stored swap somehow changed, aborting saving")?;
// TODO: see if this can be done through sled config self.swaps
self.0
.flush_async() .flush_async()
.await .await
.map(|_| ()) .map(|_| ())
@ -98,7 +132,7 @@ impl Database {
let key = serialize(&swap_id)?; let key = serialize(&swap_id)?;
let encoded = self let encoded = self
.0 .swaps
.get(&key)? .get(&key)?
.ok_or_else(|| anyhow!("Swap with id {} not found in database", swap_id))?; .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)>> { 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 (key, value) = item.context("Failed to retrieve swap from DB")?;
let swap_id = deserialize::<Uuid>(&key)?; let swap_id = deserialize::<Uuid>(&key)?;
@ -277,4 +311,52 @@ mod tests {
assert_eq!(err.downcast_ref::<NotBob>().unwrap(), &NotBob); 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(())
}
} }