From f9cfc2abe357350dfa6d0b3e5b09dd9c12e98c0a Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Tue, 3 Nov 2020 15:26:47 +1100 Subject: [PATCH] Remove generics from Database --- swap/src/alice.rs | 43 ++++++++----- swap/src/bob.rs | 16 ++--- swap/src/main.rs | 4 +- swap/src/storage.rs | 147 +++++++++++++++++++------------------------ xmr-btc/src/alice.rs | 2 +- xmr-btc/src/bob.rs | 2 +- 6 files changed, 103 insertions(+), 111 deletions(-) diff --git a/swap/src/alice.rs b/swap/src/alice.rs index a314366e..0b89d2b4 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -45,7 +45,7 @@ use xmr_btc::{ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, - db: Database, + db: Database, listen: Multiaddr, transport: SwapTransport, behaviour: Alice, @@ -177,7 +177,7 @@ pub async fn swap( }; let swap_id = Uuid::new_v4(); - db.insert_latest_state(swap_id, &storage::Alice::Handshaken(state3.clone())) + db.insert_latest_state(swap_id, storage::Alice::Handshaken(state3.clone()).into()) .await?; info!("Handshake complete, we now have State3 for Alice."); @@ -205,14 +205,14 @@ pub async fn swap( public_spend_key, public_view_key, }) => { - db.insert_latest_state(swap_id, &storage::Alice::BtcLocked(state3.clone())) + db.insert_latest_state(swap_id, storage::Alice::BtcLocked(state3.clone()).into()) .await?; let (transfer_proof, _) = monero_wallet .transfer(public_spend_key, public_view_key, amount) .await?; - db.insert_latest_state(swap_id, &storage::Alice::XmrLocked(state3.clone())) + db.insert_latest_state(swap_id, storage::Alice::XmrLocked(state3.clone()).into()) .await?; let mut guard = network.as_ref().lock().await; @@ -221,10 +221,14 @@ pub async fn swap( } GeneratorState::Yielded(Action::RedeemBtc(tx)) => { - db.insert_latest_state(swap_id, &storage::Alice::BtcRedeemable { - state: state3.clone(), - redeem_tx: tx.clone(), - }) + db.insert_latest_state( + swap_id, + storage::Alice::BtcRedeemable { + state: state3.clone(), + redeem_tx: tx.clone(), + } + .into(), + ) .await?; let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; @@ -233,8 +237,11 @@ pub async fn swap( let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; } GeneratorState::Yielded(Action::PunishBtc(tx)) => { - db.insert_latest_state(swap_id, &storage::Alice::BtcPunishable(state3.clone())) - .await?; + db.insert_latest_state( + swap_id, + storage::Alice::BtcPunishable(state3.clone()).into(), + ) + .await?; let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; } @@ -242,11 +249,15 @@ pub async fn swap( spend_key, view_key, }) => { - db.insert_latest_state(swap_id, &storage::Alice::BtcRefunded { - state: state3.clone(), - spend_key, - view_key, - }) + db.insert_latest_state( + swap_id, + storage::Alice::BtcRefunded { + state: state3.clone(), + spend_key, + view_key, + } + .into(), + ) .await?; monero_wallet @@ -254,7 +265,7 @@ pub async fn swap( .await?; } GeneratorState::Complete(()) => { - db.insert_latest_state(swap_id, &storage::Alice::SwapComplete) + db.insert_latest_state(swap_id, storage::Alice::SwapComplete.into()) .await?; return Ok(()); diff --git a/swap/src/bob.rs b/swap/src/bob.rs index bbd7533c..f9217dec 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -44,7 +44,7 @@ use xmr_btc::{ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, - db: Database, + db: Database, btc: u64, addr: Multiaddr, mut cmd_tx: Sender, @@ -144,7 +144,7 @@ pub async fn swap( }; let swap_id = Uuid::new_v4(); - db.insert_latest_state(swap_id, &storage::Bob::Handshaken(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::Handshaken(state2.clone()).into()) .await?; swarm.send_message2(alice.clone(), state2.next_message()); @@ -172,11 +172,11 @@ pub async fn swap( let _ = bitcoin_wallet .broadcast_signed_transaction(signed_tx_lock) .await?; - db.insert_latest_state(swap_id, &storage::Bob::BtcLocked(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::BtcLocked(state2.clone()).into()) .await?; } GeneratorState::Yielded(bob::Action::SendBtcRedeemEncsig(tx_redeem_encsig)) => { - db.insert_latest_state(swap_id, &storage::Bob::XmrLocked(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::XmrLocked(state2.clone()).into()) .await?; let mut guard = network.as_ref().lock().await; @@ -196,7 +196,7 @@ pub async fn swap( spend_key, view_key, }) => { - db.insert_latest_state(swap_id, &storage::Bob::BtcRedeemed(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::BtcRedeemed(state2.clone()).into()) .await?; monero_wallet @@ -204,7 +204,7 @@ pub async fn swap( .await?; } GeneratorState::Yielded(bob::Action::CancelBtc(tx_cancel)) => { - db.insert_latest_state(swap_id, &storage::Bob::BtcRefundable(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::BtcRefundable(state2.clone()).into()) .await?; let _ = bitcoin_wallet @@ -212,7 +212,7 @@ pub async fn swap( .await?; } GeneratorState::Yielded(bob::Action::RefundBtc(tx_refund)) => { - db.insert_latest_state(swap_id, &storage::Bob::BtcRefundable(state2.clone())) + db.insert_latest_state(swap_id, storage::Bob::BtcRefundable(state2.clone()).into()) .await?; let _ = bitcoin_wallet @@ -220,7 +220,7 @@ pub async fn swap( .await?; } GeneratorState::Complete(()) => { - db.insert_latest_state(swap_id, &storage::Bob::SwapComplete) + db.insert_latest_state(swap_id, storage::Bob::SwapComplete.into()) .await?; return Ok(()); diff --git a/swap/src/main.rs b/swap/src/main.rs index 3f7a701e..5f57553d 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -156,7 +156,7 @@ async fn create_tor_service( async fn swap_as_alice( bitcoin_wallet: Arc, monero_wallet: Arc, - db: Database, + db: Database, addr: Multiaddr, transport: SwapTransport, behaviour: Alice, @@ -167,7 +167,7 @@ async fn swap_as_alice( async fn swap_as_bob( bitcoin_wallet: Arc, monero_wallet: Arc, - db: Database, + db: Database, sats: u64, alice: Multiaddr, transport: SwapTransport, diff --git a/swap/src/storage.rs b/swap/src/storage.rs index 14c6c896..7f538fde 100644 --- a/swap/src/storage.rs +++ b/swap/src/storage.rs @@ -5,7 +5,14 @@ use uuid::Uuid; use xmr_btc::{alice, bob, monero, serde::monero_private_key}; #[allow(clippy::large_enum_variant)] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub enum Swap { + Alice(Alice), + Bob(Bob), +} + +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum Alice { Handshaken(alice::State3), BtcLocked(alice::State3), @@ -24,7 +31,7 @@ pub enum Alice { SwapComplete, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum Bob { Handshaken(bob::State2), BtcLocked(bob::State2), @@ -34,54 +41,54 @@ pub enum Bob { SwapComplete, } -pub struct Database -where - T: Serialize + DeserializeOwned, -{ - db: sled::Db, - _marker: std::marker::PhantomData, +impl From for Swap { + fn from(from: Alice) -> Self { + Swap::Alice(from) + } } -impl Database -where - T: Serialize + DeserializeOwned, -{ +impl From for Swap { + fn from(from: Bob) -> Self { + Swap::Bob(from) + } +} + +pub struct Database(sled::Db); + +impl Database { pub fn open(path: &Path) -> Result { let db = sled::open(path).with_context(|| format!("Could not open the DB at {:?}", path))?; - Ok(Database { - db, - _marker: Default::default(), - }) + Ok(Database(db)) } // TODO: Add method to update state - pub async fn insert_latest_state(&self, swap_id: Uuid, state: &T) -> Result<()> { + 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.db.get(&key)?; + let old_value = self.0.get(&key)?; - self.db + self.0 .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.db + self.0 .flush_async() .await .map(|_| ()) .context("Could not flush db") } - pub fn get_latest_state(&self, swap_id: Uuid) -> anyhow::Result { + pub fn get_latest_state(&self, swap_id: Uuid) -> anyhow::Result { let key = serialize(&swap_id)?; let encoded = self - .db + .0 .get(&key)? .ok_or_else(|| anyhow!("State does not exist {:?}", key))?; @@ -106,87 +113,61 @@ where #[cfg(test)] mod tests { - #![allow(non_snake_case)] use super::*; - use bitcoin::SigHash; - use rand::rngs::OsRng; - use serde::{Deserialize, Serialize}; - use std::str::FromStr; - use xmr_btc::{cross_curve_dleq, monero, serde::monero_private_key}; - - #[derive(Debug, Serialize, Deserialize, PartialEq)] - pub struct TestState { - A: xmr_btc::bitcoin::PublicKey, - a: xmr_btc::bitcoin::SecretKey, - s_a: cross_curve_dleq::Scalar, - #[serde(with = "monero_private_key")] - s_b: monero::PrivateKey, - S_a_monero: ::monero::PublicKey, - S_a_bitcoin: xmr_btc::bitcoin::PublicKey, - v: xmr_btc::monero::PrivateViewKey, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - btc: ::bitcoin::Amount, - xmr: xmr_btc::monero::Amount, - refund_timelock: u32, - refund_address: ::bitcoin::Address, - transaction: ::bitcoin::Transaction, - tx_punish_sig: xmr_btc::bitcoin::Signature, - } #[tokio::test] - async fn recover_state_from_db() { + async fn can_write_and_read_to_multiple_keys() { let db_dir = tempfile::tempdir().unwrap(); let db = Database::open(db_dir.path()).unwrap(); - let a = xmr_btc::bitcoin::SecretKey::new_random(&mut OsRng); - let s_a = cross_curve_dleq::Scalar::random(&mut OsRng); - let s_b = monero::PrivateKey::from_scalar(monero::Scalar::random(&mut OsRng)); - let v_a = xmr_btc::monero::PrivateViewKey::new_random(&mut OsRng); - let S_a_monero = monero::PublicKey::from_private_key(&monero::PrivateKey { - scalar: s_a.into_ed25519(), - }); - let S_a_bitcoin = s_a.into_secp256k1().into(); - let tx_punish_sig = a.sign(SigHash::default()); + let state_1 = Swap::Alice(Alice::SwapComplete); + let swap_id_1 = Uuid::new_v4(); + db.insert_latest_state(swap_id_1, state_1.clone()) + .await + .expect("Failed to save second state"); - let state = TestState { - A: a.public(), - a, - s_b, - s_a, - S_a_monero, - S_a_bitcoin, - v: v_a, - btc: ::bitcoin::Amount::from_sat(100), - xmr: xmr_btc::monero::Amount::from_piconero(1000), - refund_timelock: 0, - refund_address: ::bitcoin::Address::from_str("1L5wSMgerhHg8GZGcsNmAx5EXMRXSKR3He") - .unwrap(), - transaction: ::bitcoin::Transaction { - version: 0, - lock_time: 0, - input: vec![::bitcoin::TxIn::default()], - output: vec![::bitcoin::TxOut::default()], - }, - tx_punish_sig, - }; + let state_2 = Swap::Bob(Bob::SwapComplete); + let swap_id_2 = Uuid::new_v4(); + db.insert_latest_state(swap_id_2, state_2.clone()) + .await + .expect("Failed to save first state"); + + let recovered_1 = db + .get_latest_state(swap_id_1) + .expect("Failed to recover first state"); + + let recovered_2 = db + .get_latest_state(swap_id_2) + .expect("Failed to recover second state"); + + assert_eq!(recovered_1, state_1); + assert_eq!(recovered_2, state_2); + } + + #[tokio::test] + async fn can_write_twice_to_one_key() { + let db_dir = tempfile::tempdir().unwrap(); + let db = Database::open(db_dir.path()).unwrap(); + + let state = Swap::Alice(Alice::SwapComplete); let swap_id = Uuid::new_v4(); - db.insert_latest_state(swap_id, &state) + db.insert_latest_state(swap_id, state.clone()) .await .expect("Failed to save state the first time"); - let recovered: TestState = db + let recovered = db .get_latest_state(swap_id) .expect("Failed to recover state the first time"); // We insert and recover twice to ensure database implementation allows the // caller to write to an existing key - db.insert_latest_state(swap_id, &recovered) + db.insert_latest_state(swap_id, recovered) .await .expect("Failed to save state the second time"); - let recovered: TestState = db + let recovered = db .get_latest_state(swap_id) .expect("Failed to recover state the second time"); - assert_eq!(state, recovered); + assert_eq!(recovered, state); } } diff --git a/xmr-btc/src/alice.rs b/xmr-btc/src/alice.rs index 99859e8d..b0382e63 100644 --- a/xmr-btc/src/alice.rs +++ b/xmr-btc/src/alice.rs @@ -684,7 +684,7 @@ impl State2 { } } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct State3 { pub a: bitcoin::SecretKey, pub B: bitcoin::PublicKey, diff --git a/xmr-btc/src/bob.rs b/xmr-btc/src/bob.rs index d92303b2..26c26374 100644 --- a/xmr-btc/src/bob.rs +++ b/xmr-btc/src/bob.rs @@ -495,7 +495,7 @@ impl State1 { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct State2 { pub A: bitcoin::PublicKey, pub b: bitcoin::SecretKey,