diff --git a/Cargo.lock b/Cargo.lock index 2f0b269..ebf12a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1654,13 +1654,13 @@ dependencies = [ "magic-crypt", "parity-scale-codec", "rand 0.8.5", + "rocksdb", "rust_decimal", "serde", "serde_json", "sp-core", "sp-io", "sp-keyring", - "sqlx", "thiserror", "tokio", "tokio-stream", @@ -4778,8 +4778,6 @@ dependencies = [ "rand 0.8.5", "rsa", "rust_decimal", - "rustls", - "rustls-pemfile", "sha1", "sha2 0.10.6", "smallvec 1.10.0", @@ -4787,9 +4785,7 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", - "tokio-stream", "url", - "webpki-roots", ] [[package]] @@ -4816,11 +4812,6 @@ name = "sqlx-rt" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls", -] [[package]] name = "ss58-registry" diff --git a/sidecar.toml.example b/sidecar.toml.example index eb3db73..51c0fb2 100644 --- a/sidecar.toml.example +++ b/sidecar.toml.example @@ -1,3 +1,3 @@ -db = "mysql://username:password@localhost:3306/fxdx" +db_dir = "/tmp/sidecar" prover = "127.0.0.1:8097" bind_addr = "127.0.0.1:8096" diff --git a/sidecar/Cargo.toml b/sidecar/Cargo.toml index 6fbc4b8..960a91c 100644 --- a/sidecar/Cargo.toml +++ b/sidecar/Cargo.toml @@ -26,7 +26,7 @@ parity-scale-codec = { version = "3", features = ["derive"] } env_logger = "0.10.1" log = { version = "0.4", features = ["serde"] } x25519-dalek = "1.1.1" -sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "mysql", "decimal", "chrono"] } +rocksdb = "0.21" hex = "0.4" rand = "0.8.5" hyper = "0.14" diff --git a/sidecar/src/config.rs b/sidecar/src/config.rs index c4eec84..6796455 100644 --- a/sidecar/src/config.rs +++ b/sidecar/src/config.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Config { pub prover: String, - pub db: String, + pub db_dir: String, pub bind_addr: String, } @@ -27,46 +27,14 @@ pub struct Config { pub struct Cli { #[arg(short('c'), long("config"), required = true, value_name = "FILE")] pub file: std::path::PathBuf, - #[arg(long)] - pub skip_decrypt: bool, -} - -impl Config { - fn decrypt(&mut self, key: &str) -> anyhow::Result<()> { - use magic_crypt::MagicCryptTrait; - let mc = magic_crypt::new_magic_crypt!(key, 64); - let dec = mc.decrypt_base64_to_string(&self.db)?; - self.db.replace_range(.., &dec); - Ok(()) - } - - #[allow(dead_code)] - fn encrypt(&mut self, key: &str) -> anyhow::Result<()> { - use magic_crypt::MagicCryptTrait; - let mc = magic_crypt::new_magic_crypt!(key, 64); - let enc = mc.encrypt_str_to_base64(&self.db); - self.db.replace_range(.., &enc); - Ok(()) - } } pub fn init_config_file() -> anyhow::Result { let opts = Cli::parse(); - if opts.skip_decrypt { - init_config(&std::fs::read_to_string(&opts.file)?, None) - } else { - let key = std::env::var_os("MAGIC_KEY").ok_or(anyhow::anyhow!("env MAGIC_KEY not set"))?; - init_config( - &std::fs::read_to_string(&opts.file)?, - key.to_str().map(|s| s.to_string()), - ) - } + init_config(&std::fs::read_to_string(&opts.file)?) } -fn init_config(toml: &str, key: Option) -> anyhow::Result { - let mut cfg: Config = toml::from_str(toml)?; - if let Some(key) = key { - cfg.decrypt(&key)?; - } +fn init_config(toml: &str) -> anyhow::Result { + let cfg: Config = toml::from_str(toml)?; Ok(cfg) } diff --git a/sidecar/src/context.rs b/sidecar/src/context.rs index 6bb444b..470cce8 100644 --- a/sidecar/src/context.rs +++ b/sidecar/src/context.rs @@ -12,26 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::errors::CustomRpcError; use crate::{ backend::BackendConnection, config::Config, - // TODO remove db, endpoint::{PendingOrderWrapper, TradingCommand}, - AccountId32, - Sr25519Pair, - Sr25519Public, - Sr25519Signature, + errors::CustomRpcError, + AccountId32, Sr25519Pair, Sr25519Public, Sr25519Signature, }; use dashmap::DashMap; use galois_engine::{core::*, fusotao::OffchainSymbol, orders::PendingOrder}; use hyper::{Body, Request, Response}; use parity_scale_codec::{Decode, Encode}; +use rocksdb::DB; use rust_decimal::Decimal; use sp_core::crypto::{Pair as Crypto, Ss58Codec}; -use sqlx::mysql::MySqlConnectOptions; -use sqlx::{ConnectOptions, MySql, Pool}; use std::{ collections::BTreeSet, error::Error, @@ -52,7 +47,7 @@ use x25519_dalek::StaticSecret; pub struct Context { pub backend: BackendConnection, pub x25519: StaticSecret, - pub db: Pool, + pub db: DB, pub subscribers: Arc>>, pub session_nonce: Arc>, pub markets: Arc, OffchainSymbol)>>, @@ -64,12 +59,7 @@ impl Context { let backend = BackendConnection::new(config.prover, broadcast); let conn = backend.clone(); let x25519 = futures::executor::block_on(async move { conn.get_x25519().await }).unwrap(); - let db = futures::executor::block_on(async { - let mut option: MySqlConnectOptions = config.db.parse()?; - option.disable_statement_logging(); - Pool::connect_with(option).await - }) - .unwrap(); + let db = DB::open_default(&config.db_dir).unwrap(); let subscribers = Arc::new(DashMap::< String, UnboundedSender<(String, PendingOrderWrapper)>, @@ -120,13 +110,6 @@ impl Context { } } - pub async fn get_trading_key(&self, user_id: &String) -> anyhow::Result> { - db::query_trading_key(&self.db, user_id) - .await - .map(|k| crate::hexstr_to_vec(&k)) - .flatten() - } - pub async fn get_user_nonce(&self, user_id: &String) -> anyhow::Result { let session = self .session_nonce @@ -135,20 +118,22 @@ impl Context { Ok(session.value().get_nonce().await) } + // FIXME maybe we could calculate the shared secret on each request + /// the users' curve25519 pubkey is one-time, so is the trading key(shared secret of curve25519) pub async fn verify_trading_signature( &self, data: &[u8], - user_id: &String, + user_id: &AccountId32, sig: &[u8], nonce: &[u8], ) -> anyhow::Result<()> { let mut decode = nonce.clone(); let n = u32::decode(&mut decode)?; - let key = self.get_trading_key(user_id).await?; + let key = db::query_trading_key(&self.db, user_id)?; // FIXME when sidecar reboot, the session_nonce will be empty let session = self .session_nonce - .get(user_id) + .get(&user_id.to_ss58check()) .ok_or(CustomRpcError::user_not_found())?; session.value().try_occupy_nonce(n).await?; let mut to_be_signed = vec![]; diff --git a/sidecar/src/db.rs b/sidecar/src/db.rs index e416be0..5e4d129 100644 --- a/sidecar/src/db.rs +++ b/sidecar/src/db.rs @@ -12,33 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use serde::{Deserialize, Serialize}; -use sqlx::{MySql, Pool}; +use crate::AccountId32; -#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, sqlx::FromRow)] -pub struct TradingKey { - pub f_user_id: String, - pub f_trading_key: String, +pub fn query_trading_key(db: &rocksdb::DB, user_id: &AccountId32) -> anyhow::Result> { + db.get(user_id)?.ok_or(anyhow::anyhow!("Key expired")) } -pub async fn query_trading_key(pool: &Pool, user_id: &String) -> anyhow::Result { - let r = - sqlx::query_as::<_, TradingKey>("select * from t_trading_key where f_user_id=? limit 1") - .bind(user_id) - .fetch_one(pool) - .await?; - Ok(r.f_trading_key) -} - -pub async fn save_trading_key( - pool: &Pool, - user_id: &String, - key: &String, +pub fn save_trading_key( + db: &rocksdb::DB, + user_id: &AccountId32, + key: [u8; 32], ) -> anyhow::Result<()> { - sqlx::query("replace into t_trading_key(f_user_id,f_trading_key) values(?,?)") - .bind(user_id) - .bind(key) - .execute(pool) - .await?; + db.put(user_id, key)?; Ok(()) } diff --git a/sidecar/src/endpoint.rs b/sidecar/src/endpoint.rs index ab813c8..c7748cb 100644 --- a/sidecar/src/endpoint.rs +++ b/sidecar/src/endpoint.rs @@ -29,7 +29,7 @@ pub fn export_rpc(context: Context) -> RpcModule { .register_async_method("query_pending_orders", |p, ctx| async move { let (symbol, user_id, signature, nonce) = p.parse::<(String, String, String, String)>()?; - let user_id = crate::try_into_ss58(user_id)?; + let user_id = crate::try_into_account(user_id)?; let symbol = crate::hexstr_to_vec(&symbol)?; let signature = crate::hexstr_to_vec(&signature)?; let nonce = crate::hexstr_to_vec(&nonce)?; @@ -39,7 +39,7 @@ pub fn export_rpc(context: Context) -> RpcModule { let symbol = Symbol::decode(&mut symbol.as_slice()) .map_err(|_| anyhow::anyhow!("invalid symbol"))?; ctx.backend - .query_pending_orders(symbol, &user_id) + .query_pending_orders(symbol, &user_id.to_ss58check()) .await .map(|r| { r.into_iter() @@ -52,14 +52,14 @@ pub fn export_rpc(context: Context) -> RpcModule { module .register_async_method("query_account", |p, ctx| async move { let (user_id, signature, nonce) = p.parse::<(String, String, String)>()?; - let user_id = crate::try_into_ss58(user_id)?; + let user_id = crate::try_into_account(user_id)?; let signature = crate::hexstr_to_vec(&signature)?; let nonce = crate::hexstr_to_vec(&nonce)?; ctx.verify_trading_signature(&[], &user_id, &signature, &nonce) .await .map_err(handle_error)?; ctx.backend - .get_account(&user_id) + .get_account(&user_id.to_ss58check()) .await .map(|r| { r.into_iter() @@ -73,7 +73,8 @@ pub fn export_rpc(context: Context) -> RpcModule { .register_async_method("trade", |p, ctx| async move { let (user_id, cmd, signature, nonce, relayer) = p.parse::<(String, String, String, String, String)>()?; - let user_id = crate::try_into_ss58(user_id)?; + let user_id = crate::try_into_account(user_id)?; + let ss58 = user_id.to_ss58check(); let signature = crate::hexstr_to_vec(&signature)?; let nonce = crate::hexstr_to_vec(&nonce)?; let hex = crate::hexstr_to_vec(&cmd)?; @@ -82,11 +83,9 @@ pub fn export_rpc(context: Context) -> RpcModule { ctx.verify_trading_signature(&hex, &user_id, &signature, &nonce) .await .map_err(handle_error)?; - ctx.validate_cmd(&user_id, &cmd) - .await - .map_err(handle_error)?; + ctx.validate_cmd(&ss58, &cmd).await.map_err(handle_error)?; ctx.backend - .submit_trading_command(user_id, cmd, relayer) + .submit_trading_command(ss58, cmd, relayer) .await .map(|id| crate::to_hexstr(id)) .map_err(handle_error) @@ -121,8 +120,7 @@ pub fn export_rpc(context: Context) -> RpcModule { .map_err(|_| anyhow::anyhow!("Invalid public key"))?; let user_x25519_pub = x25519_dalek::PublicKey::from(user_x25519_pub); let key = ctx.x25519.diffie_hellman(&user_x25519_pub).to_bytes(); - let key = format!("0x{}", hex::encode(&key)); - db::save_trading_key(&ctx.db, &user_id.to_ss58check(), &key).await?; + db::save_trading_key(&ctx.db, &user_id, key)?; let init_nonce = rand::thread_rng().gen_range(1..10000); ctx.session_nonce .insert(user_id.to_ss58check(), Session::new(init_nonce)); @@ -154,8 +152,7 @@ pub fn export_rpc(context: Context) -> RpcModule { .map_err(|_| anyhow::anyhow!("Invalid public key"))?; let bot_x25519_pub = x25519_dalek::PublicKey::from(bot_x25519_pub); let key = ctx.x25519.diffie_hellman(&bot_x25519_pub).to_bytes(); - let key = format!("0x{}", hex::encode(&key)); - db::save_trading_key(&ctx.db, &sub_id.to_ss58check(), &key).await?; + db::save_trading_key(&ctx.db, &sub_id, key)?; let init_nonce = rand::thread_rng().gen_range(1..10000); ctx.session_nonce .insert(sub_id.to_ss58check(), Session::new(init_nonce)); @@ -176,7 +173,7 @@ pub fn export_rpc(context: Context) -> RpcModule { .register_async_method("append_user", |p, ctx| async move { let (user_id, signature, nonce, relayer) = p.parse::<(String, String, String, String)>()?; - let user_id = crate::try_into_ss58(user_id)?; + let user_id = crate::try_into_account(user_id)?; let signature = crate::hexstr_to_vec(&signature)?; let nonce = crate::hexstr_to_vec(&nonce)?; ctx.verify_trading_signature(&[], &user_id, &signature, &nonce) @@ -187,7 +184,7 @@ pub fn export_rpc(context: Context) -> RpcModule { .get(&format!("broker:{}", relayer)) .map(|b| b.value().clone()) .ok_or_else(|| anyhow::anyhow!("Broker not initialized."))?; - ctx.subscribers.insert(user_id, tx); + ctx.subscribers.insert(user_id.to_ss58check(), tx); Ok(()) }) .unwrap();