Add sqlite database implementation

This commit is contained in:
rishflab 2021-09-21 09:14:33 +10:00
parent d588e9636e
commit 13075c1064
7 changed files with 778 additions and 9 deletions

248
Cargo.lock generated
View File

@ -49,6 +49,17 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]]
name = "ahash"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
dependencies = [
"getrandom 0.2.2",
"once_cell",
"version_check 0.9.3",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
@ -128,6 +139,15 @@ dependencies = [
"pin-project-lite 0.2.6", "pin-project-lite 0.2.6",
] ]
[[package]]
name = "atoi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "atomic" name = "atomic"
version = "0.5.0" version = "0.5.0"
@ -610,7 +630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"nom", "nom 5.1.2",
"serde", "serde",
"toml", "toml",
] ]
@ -691,6 +711,21 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crc"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10c2722795460108a7872e1cd933a85d6ec38abc4baecad51028f702da28889f"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.2.1" version = "1.2.1"
@ -723,6 +758,16 @@ dependencies = [
"scopeguard", "scopeguard",
] ]
[[package]]
name = "crossbeam-queue"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.5" version = "0.8.5"
@ -937,6 +982,12 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]] [[package]]
name = "ecdsa_fun" name = "ecdsa_fun"
version = "0.6.2-alpha.0" version = "0.6.2-alpha.0"
@ -977,6 +1028,9 @@ name = "either"
version = "1.6.1" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "electrum-client" name = "electrum-client"
@ -1175,6 +1229,17 @@ dependencies = [
"num_cpus", "num_cpus",
] ]
[[package]]
name = "futures-intrusive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e"
dependencies = [
"futures-core",
"lock_api 0.4.5",
"parking_lot 0.11.2",
]
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.17" version = "0.3.17"
@ -1362,7 +1427,25 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [ dependencies = [
"ahash", "ahash 0.4.7",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash 0.7.4",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown 0.11.2",
] ]
[[package]] [[package]]
@ -1614,7 +1697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
dependencies = [ dependencies = [
"autocfg 1.0.1", "autocfg 1.0.1",
"hashbrown", "hashbrown 0.9.1",
] ]
[[package]] [[package]]
@ -2088,6 +2171,17 @@ dependencies = [
"libsecp256k1-core", "libsecp256k1-core",
] ]
[[package]]
name = "libsqlite3-sys"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.2" version = "1.1.2"
@ -2148,7 +2242,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f374d42cdfc1d7dbf3d3dec28afab2eb97ffbf43a3234d795b5986dbf4b90ba" checksum = "1f374d42cdfc1d7dbf3d3dec28afab2eb97ffbf43a3234d795b5986dbf4b90ba"
dependencies = [ dependencies = [
"hashbrown", "hashbrown 0.9.1",
] ]
[[package]] [[package]]
@ -2231,6 +2325,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "minimal-lexical"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d"
[[package]] [[package]]
name = "miniscript" name = "miniscript"
version = "5.1.0" version = "5.1.0"
@ -2429,6 +2529,17 @@ dependencies = [
"version_check 0.9.3", "version_check 0.9.3",
] ]
[[package]]
name = "nom"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1"
dependencies = [
"memchr",
"minimal-lexical",
"version_check 0.9.3",
]
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.3.6" version = "0.3.6"
@ -3536,6 +3647,7 @@ version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
dependencies = [ dependencies = [
"indexmap",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@ -3804,12 +3916,122 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "sqlformat"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
dependencies = [
"itertools 0.10.1",
"nom 7.0.0",
"unicode_categories",
]
[[package]]
name = "sqlx"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4b94ab0f8c21ee4899b93b06451ef5d965f1a355982ee73684338228498440"
dependencies = [
"sqlx-core",
"sqlx-macros",
]
[[package]]
name = "sqlx-core"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec28b91a01e1fe286d6ba66f68289a2286df023fc97444e1fd86c2fd6d5dc026"
dependencies = [
"ahash 0.7.4",
"atoi",
"bitflags",
"byteorder",
"bytes 1.0.1",
"crc",
"crossbeam-channel",
"crossbeam-queue",
"crossbeam-utils",
"either",
"futures-channel",
"futures-core",
"futures-intrusive",
"futures-util",
"hashlink",
"hex 0.4.3",
"itoa",
"libc",
"libsqlite3-sys",
"log 0.4.14",
"memchr",
"once_cell",
"parking_lot 0.11.2",
"percent-encoding 2.1.0",
"rustls 0.19.0",
"serde",
"sha2",
"smallvec",
"sqlformat",
"sqlx-rt",
"stringprep",
"thiserror",
"tokio-stream",
"url 2.2.2",
"webpki",
"webpki-roots 0.21.0",
"whoami",
]
[[package]]
name = "sqlx-macros"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc33c35d54774eed73d54568d47a6ac099aed8af5e1556a017c131be88217d5"
dependencies = [
"dotenv",
"either",
"futures",
"heck",
"hex 0.4.3",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
"sha2",
"sqlx-core",
"sqlx-rt",
"syn",
"url 2.2.2",
]
[[package]]
name = "sqlx-rt"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14302b678d9c76b28f2e60115211e25e0aabc938269991745a169753dc00e35c"
dependencies = [
"once_cell",
"tokio",
"tokio-rustls",
]
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringprep"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.8.0"
@ -3895,6 +4117,7 @@ dependencies = [
"bitcoin", "bitcoin",
"bitcoin-harness", "bitcoin-harness",
"bmrng", "bmrng",
"chrono",
"comfy-table", "comfy-table",
"config", "config",
"conquer-once", "conquer-once",
@ -3930,6 +4153,7 @@ dependencies = [
"sigma_fun", "sigma_fun",
"sled", "sled",
"spectral", "spectral",
"sqlx",
"structopt", "structopt",
"strum", "strum",
"tempfile", "tempfile",
@ -4508,6 +4732,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "unicode_categories"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]] [[package]]
name = "universal-hash" name = "universal-hash"
version = "0.4.0" version = "0.4.0"
@ -4790,6 +5020,16 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "whoami"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7741161a40200a867c96dfa5574544efa4178cf4c8f770b62dd1cc0362d7ae1"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "widestring" name = "widestring"
version = "0.4.3" version = "0.4.3"

View File

@ -19,6 +19,7 @@ bdk = "0.10"
big-bytes = "1" big-bytes = "1"
bitcoin = { version = "0.26", features = [ "rand", "use-serde" ] } bitcoin = { version = "0.26", features = [ "rand", "use-serde" ] }
bmrng = "0.5" bmrng = "0.5"
chrono = "0.4"
comfy-table = "4.1.1" comfy-table = "4.1.1"
config = { version = "0.11", default-features = false, features = [ "toml" ] } config = { version = "0.11", default-features = false, features = [ "toml" ] }
conquer-once = "0.3" conquer-once = "0.3"
@ -48,6 +49,7 @@ serde_json = "1"
serde_with = { version = "1", features = [ "macros" ] } serde_with = { version = "1", features = [ "macros" ] }
sha2 = "0.9" sha2 = "0.9"
sigma_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "ed25519", "serde" ] } sigma_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "ed25519", "serde" ] }
sqlx = { version = "0.5", features = ["sqlite", "runtime-tokio-rustls", "offline"] }
sled = "0.34" sled = "0.34"
structopt = "0.3" structopt = "0.3"
strum = { version = "0.21", features = [ "derive" ] } strum = { version = "0.21", features = [ "derive" ] }
@ -60,6 +62,7 @@ tokio-util = { version = "0.6", features = [ "io" ] }
toml = "0.5" toml = "0.5"
torut = { version = "0.2", default-features = false, features = [ "v3", "control" ] } torut = { version = "0.2", default-features = false, features = [ "v3", "control" ] }
tracing = { version = "0.1", features = [ "attributes" ] } tracing = { version = "0.1", features = [ "attributes" ] }
tracing-appender = "0.1" tracing-appender = "0.1"
tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] } tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] }
tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log", "json" ] } tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log", "json" ] }

View File

@ -0,0 +1,25 @@
CREATE TABLE if NOT EXISTS swap_states
(
id INTEGER PRIMARY KEY autoincrement NOT NULL,
swap_id TEXT NOT NULL,
entered_at TEXT NOT NULL,
state TEXT NOT NULL
);
CREATE TABLE if NOT EXISTS monero_addresses
(
swap_id TEXT PRIMARY KEY NOT NULL,
address TEXT NOT NULL
);
CREATE TABLE if NOT EXISTS peers
(
swap_id TEXT PRIMARY KEY NOT NULL,
peer_id TEXT NOT NULL
);
CREATE TABLE if NOT EXISTS peer_addresses
(
peer_id TEXT NOT NULL,
address TEXT NOT NULL
);

6
swap/sqlite_dev_setup.sh Normal file
View File

@ -0,0 +1,6 @@
# crated temporary DB
# run the migration scripts to create the tables
# prepare the sqlx-data.json rust mappings
DATABASE_URL=sqlite:tempdb cargo sqlx database create
DATABASE_URL=sqlite:tempdb cargo sqlx migrate run
DATABASE_URL=sqlite:./swap/tempdb cargo sqlx prepare -- --bin swap

139
swap/sqlx-data.json Normal file
View File

@ -0,0 +1,139 @@
{
"db": "SQLite",
"081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6": {
"query": "\n SELECT peer_id\n FROM peers\n WHERE swap_id = ?\n ",
"describe": {
"columns": [
{
"name": "peer_id",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
}
},
"0ab84c094964968e96a3f2bf590d9ae92227d057386921e0e57165b887de3c75": {
"query": "\n insert into peer_addresses (\n peer_id,\n address\n ) values (?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
}
},
"1ec38c85e7679b2eb42b3df75d9098772ce44fdb8db3012d3c2410d828b74157": {
"query": "\n SELECT swap_id, state\n FROM (\n SELECT max(id), swap_id, state\n FROM swap_states\n GROUP BY swap_id\n )\n ",
"describe": {
"columns": [
{
"name": "swap_id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "state",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false
]
}
},
"2a356078a41b321234adf2aa385b501749f907f7c422945a8bdda2b6274f5225": {
"query": "\n insert into peers (\n swap_id,\n peer_id\n ) values (?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
}
},
"50a5764546f69c118fa0b64120da50f51073d36257d49768de99ff863e3511e0": {
"query": "\n insert into monero_addresses (\n swap_id,\n address\n ) values (?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
}
},
"88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf": {
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n ",
"describe": {
"columns": [
{
"name": "state",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
}
},
"a0eb85d04ee3842c52291dad4d225941d1141af735922fcbc665868997fce304": {
"query": "\n SELECT address\n FROM peer_addresses\n WHERE peer_id = ?\n ",
"describe": {
"columns": [
{
"name": "address",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
}
},
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
"query": "\n insert into swap_states (\n swap_id,\n entered_at,\n state\n ) values (?, ?, ?);\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
}
},
"ce270dd4a4b9615695a79864240c5401e2122077365e5e5a19408c068c7f9454": {
"query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n ",
"describe": {
"columns": [
{
"name": "address",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
}
}
}

View File

@ -1,16 +1,20 @@
use crate::database::alice::Alice;
use crate::database::bob::Bob;
use crate::protocol::State;
use std::fmt::Display; use std::fmt::Display;
use crate::protocol::bob::BobState;
use crate::protocol::alice::AliceState;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::database::alice::Alice;
use crate::database::bob::Bob;
use crate::protocol::alice::AliceState;
use crate::protocol::bob::BobState;
use crate::protocol::State;
mod sled; mod sled;
mod sqlite;
mod alice; mod alice;
mod bob; mod bob;
pub use self::sled::SledDatabase; pub use self::sled::SledDatabase;
pub use sqlite::SqliteDatabase;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum Swap { pub enum Swap {

352
swap/src/database/sqlite.rs Normal file
View File

@ -0,0 +1,352 @@
use uuid::Uuid;
use sqlx::sqlite::Sqlite;
use anyhow::Result;
use chrono::Utc;
use crate::protocol::{State, Database};
use std::path::PathBuf;
use libp2p::{PeerId, Multiaddr};
use crate::monero::Address;
use sqlx::{SqlitePool, Pool};
use async_trait::async_trait;
use crate::database::Swap;
use std::str::FromStr;
use anyhow::Context;
use std::collections::HashMap;
pub struct SqliteDatabase {
pool: Pool<Sqlite>
}
impl SqliteDatabase {
pub async fn open(path: PathBuf) -> Result<Self> where Self: std::marker::Sized {
let path_str = format!("sqlite:{}", path.as_path().display());
let pool = SqlitePool::connect(&path_str)
.await?;
Ok(Self { pool })
}
pub async fn run_migrations(&mut self) -> anyhow::Result<()> {
sqlx::migrate!("./migrations").run(&self.pool).await?;
Ok(())
}
}
#[async_trait]
impl Database for SqliteDatabase {
async fn insert_peer_id(&self, swap_id: Uuid, peer_id: PeerId) -> Result<()> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let peer_id = peer_id.to_string();
sqlx::query!(
r#"
insert into peers (
swap_id,
peer_id
) values (?, ?);
"#,
swap_id, peer_id
).execute(&mut conn).await?;
Ok(())
}
async fn get_peer_id(&self, swap_id: Uuid) -> Result<PeerId> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let row = sqlx::query!(r#"
SELECT peer_id
FROM peers
WHERE swap_id = ?
"#,
swap_id
).fetch_one(&mut conn). await?;
let peer_id = PeerId::from_str(&row.peer_id)?;
Ok(peer_id)
}
async fn insert_monero_address(&self, swap_id: Uuid, address: Address) -> Result<()> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let address = address.to_string();
sqlx::query!(
r#"
insert into monero_addresses (
swap_id,
address
) values (?, ?);
"#,
swap_id, address
).execute(&mut conn).await?;
Ok(())
}
async fn get_monero_address(&self, swap_id: Uuid) -> Result<Address> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let row = sqlx::query!(r#"
SELECT address
FROM monero_addresses
WHERE swap_id = ?
"#,
swap_id
).fetch_one(&mut conn). await?;
let address = row.address.parse()?;
Ok(address)
}
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()> {
let mut conn = self.pool.acquire().await?;
let peer_id = peer_id.to_string();
let address = address.to_string();
sqlx::query!(
r#"
insert into peer_addresses (
peer_id,
address
) values (?, ?);
"#,
peer_id, address
).execute(&mut conn).await?;
Ok(())
}
async fn get_addresses(&self, peer_id: PeerId) -> Result<Vec<Multiaddr>> {
let mut conn = self.pool.acquire().await?;
let peer_id = peer_id.to_string();
let rows = sqlx::query!(r#"
SELECT address
FROM peer_addresses
WHERE peer_id = ?
"#,
peer_id,
).fetch_all(&mut conn). await?;
let addresses = rows.iter()
.map(|row| {
let multiaddr = Multiaddr::from_str(&row.address)?;
Ok(multiaddr)
})
.collect::<Result<Vec<Multiaddr>>>();
addresses
}
async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()> {
let mut conn = self.pool.acquire().await?;
let entered_at = Utc::now();
let swap_id = swap_id.to_string();
let swap = serde_json::to_string(&Swap::from(state))?;
let entered_at = entered_at.to_string();
sqlx::query!(
r#"
insert into swap_states (
swap_id,
entered_at,
state
) values (?, ?, ?);
"#,
swap_id,
entered_at,
swap
).execute(&mut conn).await?;
Ok(())
}
async fn get_state(&self, swap_id: Uuid) -> Result<State> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
let row = sqlx::query!(
r#"
SELECT state
FROM swap_states
WHERE swap_id = ?
ORDER BY id desc
LIMIT 1;
"#,
swap_id
).fetch_all(&mut conn).await?;
let row = row.first().context(format!("No state in database for swap: {}", swap_id))?;
let swap: Swap = serde_json::from_str(&row.state)?;
Ok(swap.into())
}
async fn all(&self) -> Result<Vec<(Uuid, State)>> {
let mut conn = self.pool.acquire().await?;
let rows = sqlx::query!(
r#"
SELECT swap_id, state
FROM (
SELECT max(id), swap_id, state
FROM swap_states
GROUP BY swap_id
)
"#
).fetch_all(&mut conn).await?;
let result = rows.iter().map(|row|{
let swap_id = Uuid::from_str(&row.swap_id)?;
let state = match serde_json::from_str::<Swap>(&row.state) {
Ok(a) => Ok(State::from(a)),
Err(e) => Err(e)
}?;
Ok((swap_id, state))
}).collect::<Result<Vec<(Uuid, State)>>>();
result
}
async fn unfinished(&self, unfinished: fn(State) -> bool) -> Result<HashMap<Uuid, State>> {
Ok(self.all()
.await?
.into_iter()
.filter(|(_swap_id, state)| unfinished(state.clone()))
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use tempfile::tempdir;
use crate::protocol::alice::AliceState;
use crate::protocol::bob::BobState;
#[tokio::test]
async fn test_insert_and_load_state() {
let db = setup_test_db().await.unwrap();
let state_1 = State::Alice(AliceState::BtcRedeemed);
let swap_id_1 = Uuid::new_v4();
db.insert_latest_state( swap_id_1, state_1).await.unwrap();
let state_1 = State::Alice(AliceState::BtcRedeemed);
db.insert_latest_state( swap_id_1, state_1.clone()).await.unwrap();
let state_1_loaded = db.get_state( swap_id_1).await.unwrap();
assert_eq!(state_1, state_1_loaded);
}
#[tokio::test]
async fn test_retrieve_all_latest_states() {
let db = setup_test_db().await.unwrap();
let state_1 = State::Alice(AliceState::BtcRedeemed);
let state_2 = State::Alice(AliceState::BtcPunished);
let state_3 = State::Alice(AliceState::SafelyAborted);
let state_4 = State::Bob(BobState::SafelyAborted);
let swap_id_1 = Uuid::new_v4();
let swap_id_2 = Uuid::new_v4();
db.insert_latest_state(swap_id_1, state_1.clone()).await.unwrap();
db.insert_latest_state(swap_id_1, state_2.clone()).await.unwrap();
db.insert_latest_state(swap_id_1, state_3.clone()).await.unwrap();
db.insert_latest_state(swap_id_2, state_4.clone()).await.unwrap();
let latest_loaded = db.all().await.unwrap();
assert_eq!(latest_loaded.len(), 2);
assert!(latest_loaded.contains(&(swap_id_1, state_3)));
assert!(latest_loaded.contains(&(swap_id_2, state_4)));
assert!(!latest_loaded.contains(&(swap_id_1, state_1)));
assert!(!latest_loaded.contains(&(swap_id_1, state_2)));
}
#[tokio::test]
async fn test_insert_load_monero_address() -> Result<()> {
let db = setup_test_db().await?;
let swap_id = Uuid::new_v4();
let monero_address = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a".parse()?;
db.insert_monero_address(swap_id, monero_address).await?;
let loaded_monero_address = db.get_monero_address(swap_id).await?;
assert_eq!(monero_address, loaded_monero_address);
Ok(())
}
#[tokio::test]
async fn test_insert_and_load_multiaddr() -> Result<()> {
let db = setup_test_db().await?;
let peer_id = PeerId::random();
let multiaddr1 = "/ip4/127.0.0.1".parse::<Multiaddr>()?;
let multiaddr2 = "/ip4/127.0.0.2".parse::<Multiaddr>()?;
db.insert_address(peer_id, multiaddr1.clone()).await?;
db.insert_address(peer_id, multiaddr2.clone()).await?;
let loaded_multiaddr = db.get_addresses(peer_id).await?;
assert!(loaded_multiaddr.contains(&multiaddr1));
assert!(loaded_multiaddr.contains(&multiaddr2));
assert_eq!(loaded_multiaddr.len(), 2);
Ok(())
}
#[tokio::test]
async fn test_insert_and_load_peer_id() -> Result<()> {
let db = setup_test_db().await?;
let peer_id = PeerId::random();
let multiaddr1 = "/ip4/127.0.0.1".parse::<Multiaddr>()?;
let multiaddr2 = "/ip4/127.0.0.2".parse::<Multiaddr>()?;
db.insert_address(peer_id, multiaddr1.clone()).await?;
db.insert_address(peer_id, multiaddr2.clone()).await?;
let loaded_multiaddr = db.get_addresses(peer_id).await?;
assert!(loaded_multiaddr.contains(&multiaddr1));
assert!(loaded_multiaddr.contains(&multiaddr2));
assert_eq!(loaded_multiaddr.len(), 2);
Ok(())
}
async fn setup_test_db() -> Result<SqliteDatabase> {
let temp_db = tempdir().unwrap().into_path().join("tempdb");
// file has to exist in order to connect with sqlite
File::create(temp_db.clone()).unwrap();
let mut db = SqliteDatabase::open(temp_db).await?;
db.run_migrations().await.unwrap();
Ok(db)
}
}