From c3dc6c64cd8c8c7458fdc2ffe487889cd4c2076e Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Mon, 9 Sep 2024 16:34:54 +0100 Subject: [PATCH] save dev state - WalletConnect client impl --- Cargo.lock | 1 + mm2src/kdf_walletconnect/src/error.rs | 23 +- mm2src/kdf_walletconnect/src/handler.rs | 10 +- mm2src/kdf_walletconnect/src/lib.rs | 249 +++++++++++--------- mm2src/kdf_walletconnect/src/session_key.rs | 24 +- mm2src/mm2_core/Cargo.toml | 3 +- mm2src/mm2_core/src/mm_ctx.rs | 9 +- mm2src/mm2_main/src/rpc.rs | 2 +- 8 files changed, 174 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42a5e0ae52..233c319211 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4486,6 +4486,7 @@ dependencies = [ "gstuff", "hex", "instant", + "kdf_walletconnect", "lazy_static", "mm2_err_handle", "mm2_event_stream", diff --git a/mm2src/kdf_walletconnect/src/error.rs b/mm2src/kdf_walletconnect/src/error.rs index 67761fe678..5c369ab7d8 100644 --- a/mm2src/kdf_walletconnect/src/error.rs +++ b/mm2src/kdf_walletconnect/src/error.rs @@ -2,24 +2,25 @@ use derive_more::Display; use pairing_api::PairingClientError; use relay_client::error::{ClientError, Error}; use relay_rpc::rpc::PublishError; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Display)] +#[derive(Debug, Display, Serialize, Deserialize)] pub enum WalletConnectClientError { - PairingError(PairingClientError), + PairingError(String), EncodeError(String), - PublishError(Error), - ClientError(ClientError), - PairingNotFound(String) + PublishError(String), + ClientError(String), + PairingNotFound(String), } impl From for WalletConnectClientError { - fn from(value: PairingClientError) -> Self { - WalletConnectClientError::PairingError(value) - } + fn from(error: PairingClientError) -> Self { WalletConnectClientError::PairingError(error.to_string()) } } impl From for WalletConnectClientError { - fn from(value: ClientError) -> Self { - WalletConnectClientError::ClientError(value) - } + fn from(error: ClientError) -> Self { WalletConnectClientError::ClientError(error.to_string()) } +} + +impl From> for WalletConnectClientError { + fn from(error: Error) -> Self { WalletConnectClientError::PublishError(format!("{error:?}")) } } diff --git a/mm2src/kdf_walletconnect/src/handler.rs b/mm2src/kdf_walletconnect/src/handler.rs index cdd69d0910..3288bed03c 100644 --- a/mm2src/kdf_walletconnect/src/handler.rs +++ b/mm2src/kdf_walletconnect/src/handler.rs @@ -1,8 +1,10 @@ use std::time::Duration; -use futures::channel::mpsc::UnboundedSender; -use relay_client::{error::ClientError, websocket::{CloseFrame, ConnectionHandler, PublishedMessage}, ConnectionOptions}; use common::log::info; +use futures::channel::mpsc::UnboundedSender; +use relay_client::{error::ClientError, + websocket::{CloseFrame, ConnectionHandler, PublishedMessage}, + ConnectionOptions}; use relay_rpc::auth::{ed25519_dalek::SigningKey, AuthToken}; pub struct Handler { @@ -11,9 +13,7 @@ pub struct Handler { } impl Handler { - pub fn new(name: &'static str, sender: UnboundedSender) -> Self { - Self { name, sender } - } + pub fn new(name: &'static str, sender: UnboundedSender) -> Self { Self { name, sender } } } impl ConnectionHandler for Handler { diff --git a/mm2src/kdf_walletconnect/src/lib.rs b/mm2src/kdf_walletconnect/src/lib.rs index bc984785f3..f83811824e 100644 --- a/mm2src/kdf_walletconnect/src/lib.rs +++ b/mm2src/kdf_walletconnect/src/lib.rs @@ -1,24 +1,39 @@ -use std::{collections::HashMap, sync::Arc, time::Duration}; +mod error; +mod handler; +mod session_key; +use common::log::info; use error::WalletConnectClientError; -use futures::{channel::mpsc::{unbounded, UnboundedReceiver}, lock::Mutex, StreamExt}; +use futures::{channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + lock::Mutex, + StreamExt}; use handler::Handler; use mm2_err_handle::prelude::MmResult; +use mm2_err_handle::prelude::*; use pairing_api::{Methods, PairingClient}; -use relay_client::{websocket::{Client, PublishedMessage}, ConnectionOptions, MessageIdGenerator}; -use relay_rpc::{domain::{MessageId, SubscriptionId, Topic}, rpc::{params::{IrnMetadata, Metadata}, Params, Payload, Request, Response, SuccessfulResponse, JSON_RPC_VERSION_STR}}; +use relay_client::{websocket::{Client, PublishedMessage}, + ConnectionOptions, MessageIdGenerator}; +use relay_rpc::{domain::{MessageId, SubscriptionId, Topic}, + rpc::{params::{IrnMetadata, Metadata}, + Params, Payload, Request, Response, SuccessfulResponse, JSON_RPC_VERSION_STR}}; use session_key::SessionKey; -use mm2_err_handle::prelude::*; +use std::{collections::HashMap, sync::Arc, time::Duration}; use wc_common::{encrypt_and_encode, EnvelopeType}; -use common::log::info; - - -mod session_key; -mod handler; -mod error; -pub const RELAY_ADDRESS: &str = "wss://relay.walletconnect.com"; -pub const PROJECT_ID: &str = "86e916bcbacee7f98225dde86b697f5b"; +const RELAY_ADDRESS: &str = "wss://relay.walletconnect.com"; +const PROJECT_ID: &str = "86e916bcbacee7f98225dde86b697f5b"; +const SUPPORTED_PROTOCOL: &str = "irn"; +const SUPPORTED_METHODS: &[&str] = &[ + "eth_sendTransaction", + "eth_signTransaction", + "eth_sign", + "personal_sign", + "eth_signTypedData", + "eth_signTypedData_v4", +]; +const SUPPORTED_CHAINS: &[&str] = &["eip155:1", "eip155:5"]; +const SUPPORTED_EVENTS: &[&str] = &["chainChanged", "accountsChanged"]; +const SUPPORTED_ACCOUNTS: &[&str] = &["eip155:5:0xBA5BA3955463ADcc7aa3E33bbdfb8A68e0933dD8"]; #[derive(Debug)] pub struct Session { @@ -28,136 +43,148 @@ pub struct Session { pub session_key: SessionKey, } -#[derive(Debug)] pub struct WalletConnectClient { - pub client: Arc, - pub pairing: Arc, - pub sessions: HashMap, - pub handler: Arc>> + pub client: Client, + pub pairing: PairingClient, + pub sessions: Arc>>, + pub handler: Arc>>, + pub rpc_handler: Arc>>, + pub rpc_sender: UnboundedSender, } impl Default for WalletConnectClient { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } impl WalletConnectClient { pub fn new() -> Self { let (msg_sender, msg_receiver) = unbounded(); + let (rpc_sender, rpc_receiver) = unbounded::(); let pairing = PairingClient::new(); - let client = Arc::new(Client::new(Handler::new("Komodefi", msg_sender))); + let client = Client::new(Handler::new("Komodefi", msg_sender)); Self { client, pairing, - sessions: HashMap::new(), - handler: Arc::new(Mutex::new(msg_receiver)) + rpc_sender, + sessions: Arc::new(Mutex::new(HashMap::new())), + handler: Arc::new(Mutex::new(msg_receiver)), + rpc_handler: Arc::new(Mutex::new(rpc_receiver)), } } - pub async fn create_pairing(&self, metadata: Metadata, methods: Option) -> MmResult<(Topic, String), WalletConnectClientError> { - Ok(self.pairing.create(metadata, methods, &self.client).await?) + pub async fn create_pairing( + &self, + metadata: Metadata, + methods: Option, + ) -> MmResult<(Topic, String), WalletConnectClientError> { + Ok(self.pairing.create(metadata, methods, &self.client).await?) } pub async fn connect_to_pairing(&self, url: &str, activate: bool) -> MmResult { - Ok(self.pairing.pair(url, activate, &self.client).await?) + Ok(self.pairing.pair(url, activate, &self.client).await?) } pub async fn connect(&self, opts: &ConnectionOptions) -> MmResult<(), WalletConnectClientError> { Ok(self.client.connect(opts).await?) } - /// Private function to publish a request. - async fn publish_request( - &self, - topic: &str, - params: Params, - irn_metadata: IrnMetadata - ) -> MmResult<(), WalletConnectClientError> { - let message_id = MessageIdGenerator::new().next(); - let request = Request::new(message_id, params); - // Publish the encrypted message - self.publish_payload(topic, irn_metadata, Payload::Request(request)) - .await?; - - info!("Otbound request sent!\n"); - - Ok(()) - } - - /// Private function to publish a request response. - async fn publish_response( - &self, - topic: &str, - params: Params, - irn_metadata: IrnMetadata, - message_id: MessageId, - ) -> MmResult<(), WalletConnectClientError> { - let response = Response::Success(SuccessfulResponse { - id: message_id, - jsonrpc: JSON_RPC_VERSION_STR.into(), - result: serde_json::to_value(params) - .map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?, - }); - - // Publish the encrypted message - self.publish_payload(topic, irn_metadata, Payload::Response(response)) - .await?; - - println!("\nOutbound request sent!"); - - Ok(()) - } - - - /// Private function to publish a payload. - async fn publish_payload( - &self, - topic: &str, - irn_metadata: IrnMetadata, - payload: Payload, - ) -> MmResult<(), WalletConnectClientError> { - // try to extend session before updating local store. - let sym_key = { - let pairings = self.pairing.pairings.lock().await; - let pairing = pairings - .get(topic) - .ok_or_else(|| WalletConnectClientError::PairingNotFound(format!("Pariring not found for topic:{topic}")))?; - hex::decode(pairing.sym_key.clone()).map_to_mm(|err| { - WalletConnectClientError::EncodeError(format!("Failed to decode sym_key: {:?}", err)) - })? - }; - - let payload = serde_json::to_string(&payload) - .map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?; - let message = encrypt_and_encode(EnvelopeType::Type0, payload, &sym_key) - .map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?; - - // Publish the encrypted message - { - self.client - .publish( - topic.into(), - message, - None, - irn_metadata.tag, - Duration::from_secs(irn_metadata.ttl), - irn_metadata.prompt, - ) - .await - .map_to_mm(WalletConnectClientError::PublishError)?; - }; - - Ok(()) - } + async fn publish_request( + &self, + topic: &str, + params: Params, + irn_metadata: IrnMetadata, + ) -> MmResult<(), WalletConnectClientError> { + let message_id = MessageIdGenerator::new().next(); + let request = Request::new(message_id, params); + // Publish the encrypted message + self.publish_payload(topic, irn_metadata, Payload::Request(request)) + .await?; + + info!("Otbound request sent!\n"); + + Ok(()) + } + + /// Private function to publish a request response. + async fn publish_response( + &self, + topic: &str, + params: Params, + irn_metadata: IrnMetadata, + message_id: MessageId, + ) -> MmResult<(), WalletConnectClientError> { + let response = Response::Success(SuccessfulResponse { + id: message_id, + jsonrpc: JSON_RPC_VERSION_STR.into(), + result: serde_json::to_value(params) + .map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?, + }); + + // Publish the encrypted message + self.publish_payload(topic, irn_metadata, Payload::Response(response)) + .await?; + + println!("\nOutbound request sent!"); + + Ok(()) + } + + /// Private function to publish a payload. + async fn publish_payload( + &self, + topic: &str, + irn_metadata: IrnMetadata, + payload: Payload, + ) -> MmResult<(), WalletConnectClientError> { + // try to extend session before updating local store. + let sym_key = { + let pairings = self.pairing.pairings.lock().await; + let pairing = pairings.get(topic).ok_or_else(|| { + WalletConnectClientError::PairingNotFound(format!("Pariring not found for topic:{topic}")) + })?; + hex::decode(pairing.sym_key.clone()).map_to_mm(|err| { + WalletConnectClientError::EncodeError(format!("Failed to decode sym_key: {:?}", err)) + })? + }; + + let payload = + serde_json::to_string(&payload).map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?; + let message = encrypt_and_encode(EnvelopeType::Type0, payload, &sym_key) + .map_to_mm(|err| WalletConnectClientError::EncodeError(err.to_string()))?; + + // Publish the encrypted message + { + self.client + .publish( + topic.into(), + message, + None, + irn_metadata.tag, + Duration::from_secs(irn_metadata.ttl), + irn_metadata.prompt, + ) + .await?; + }; + + Ok(()) + } } pub async fn published_message_event_loop(client: Arc) { let mut recv = client.handler.lock().await; - while let Some(data) = recv.next().await { + while let Some(msg) = recv.next().await { + info!("Pulished Message: {msg:?}"); + todo!() + } +} + +pub async fn wc_rpc_event_loop(receiver: Arc>>) { + let mut recv = receiver.lock().await; + while let Some(param) = recv.next().await { + info!("Params: {param:?}"); todo!() } } diff --git a/mm2src/kdf_walletconnect/src/session_key.rs b/mm2src/kdf_walletconnect/src/session_key.rs index 84e7958bee..ecab541f6f 100644 --- a/mm2src/kdf_walletconnect/src/session_key.rs +++ b/mm2src/kdf_walletconnect/src/session_key.rs @@ -1,10 +1,8 @@ -use { - hkdf::Hkdf, - rand::{rngs::OsRng, CryptoRng, RngCore}, - sha2::{Digest, Sha256}, - std::fmt::Debug, - x25519_dalek::{EphemeralSecret, PublicKey}, -}; +use {hkdf::Hkdf, + rand::{rngs::OsRng, CryptoRng, RngCore}, + sha2::{Digest, Sha256}, + std::fmt::Debug, + x25519_dalek::{EphemeralSecret, PublicKey}}; /// Session key and topic derivation errors. #[derive(Debug, thiserror::Error)] @@ -35,8 +33,8 @@ impl SessionKey { /// Performs Diffie-Hellman symmetric key derivation. pub fn diffie_hellman(csprng: T, sender_public_key: &[u8; 32]) -> Result - where - T: RngCore + CryptoRng, + where + T: RngCore + CryptoRng, { let single_use_private_key = EphemeralSecret::random_from_rng(csprng); let public_key = PublicKey::from(&single_use_private_key); @@ -55,14 +53,10 @@ impl SessionKey { } /// Gets symmetic key reference. - pub fn symmetric_key(&self) -> &[u8; 32] { - &self.sym_key - } + pub fn symmetric_key(&self) -> &[u8; 32] { &self.sym_key } /// Gets "our" public key used in symmetric key derivation. - pub fn diffie_public_key(&self) -> &[u8; 32] { - self.public_key.as_bytes() - } + pub fn diffie_public_key(&self) -> &[u8; 32] { self.public_key.as_bytes() } /// Generates new session topic. pub fn generate_topic(&self) -> String { diff --git a/mm2src/mm2_core/Cargo.toml b/mm2src/mm2_core/Cargo.toml index 943dcac280..47c9d4270f 100644 --- a/mm2src/mm2_core/Cargo.toml +++ b/mm2src/mm2_core/Cargo.toml @@ -16,6 +16,7 @@ db_common = { path = "../db_common" } derive_more = "0.99" futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } hex = "0.4.2" +kdf_walletconnect = { path = "../kdf_walletconnect" } lazy_static = "1.4" mm2_err_handle = { path = "../mm2_err_handle" } mm2_event_stream = { path = "../mm2_event_stream" } @@ -32,7 +33,7 @@ uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] } [target.'cfg(target_arch = "wasm32")'.dependencies] gstuff = { version = "0.7", features = ["nightly"] } instant = { version = "0.1.12", features = ["wasm-bindgen"] } -mm2_rpc = { path = "../mm2_rpc", features = [ "rpc_facilities" ] } +mm2_rpc = { path = "../mm2_rpc", features = ["rpc_facilities"] } wasm-bindgen-test = { version = "0.3.2" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/mm2src/mm2_core/src/mm_ctx.rs b/mm2src/mm2_core/src/mm_ctx.rs index e891f0b649..8b95f8bf0d 100644 --- a/mm2src/mm2_core/src/mm_ctx.rs +++ b/mm2src/mm2_core/src/mm_ctx.rs @@ -5,6 +5,7 @@ use common::executor::{abortable_queue::{AbortableQueue, WeakSpawner}, use common::log::{self, LogLevel, LogOnError, LogState}; use common::{cfg_native, cfg_wasm32, small_rng}; use gstuff::{try_s, Constructible, ERR, ERRL}; +use kdf_walletconnect::WalletConnectClient; use lazy_static::lazy_static; use mm2_event_stream::{controller::Controller, Event, EventStreamConfiguration}; use mm2_metrics::{MetricsArc, MetricsOps}; @@ -84,7 +85,7 @@ pub struct MmCtx { pub event_stream_configuration: Option, /// True if the MarketMaker instance needs to stop. pub stop: Constructible, - /// Unique context identifier, allowing us to more easily pass the context through the FFI boundaries. + /// Unique context identifier, allowing us to more easily pass the context through the FFI boundaries. /// 0 if the handler ID is allocated yet. pub ffi_handle: Constructible, /// The context belonging to the `ordermatch` mod: `OrdermatchContext`. @@ -142,6 +143,7 @@ pub struct MmCtx { /// asynchronous handle for rusqlite connection. #[cfg(not(target_arch = "wasm32"))] pub async_sqlite_connection: Constructible>>, + pub wallect_connect: Arc, } impl MmCtx { @@ -191,6 +193,7 @@ impl MmCtx { nft_ctx: Mutex::new(None), #[cfg(not(target_arch = "wasm32"))] async_sqlite_connection: Constructible::default(), + wallect_connect: Arc::new(WalletConnectClient::default()), } } @@ -293,7 +296,7 @@ impl MmCtx { db_root.join(wallet_name.to_string() + ".dat") } - /// MM database path. + /// MM database path. /// Defaults to a relative "DB". /// /// Can be changed via the "dbdir" configuration field, for example: @@ -565,7 +568,7 @@ impl MmArc { } } - /// Tries getting access to the MM context. + /// Tries getting access to the MM context. /// Fails if an invalid MM context handler is passed (no such context or dropped context). #[track_caller] pub fn from_ffi_handle(ffi_handle: u32) -> Result { diff --git a/mm2src/mm2_main/src/rpc.rs b/mm2src/mm2_main/src/rpc.rs index 2709d76f32..6003f9f6c2 100644 --- a/mm2src/mm2_main/src/rpc.rs +++ b/mm2src/mm2_main/src/rpc.rs @@ -51,7 +51,7 @@ mod dispatcher_legacy; pub mod lp_commands_legacy; mod rate_limiter; -/// Lists the RPC method not requiring the "userpass" authentication. +/// Lists the RPC method not requiring the "userpass" authentication. /// None is also public to skip auth and display proper error in case of method is missing const PUBLIC_METHODS: &[Option<&str>] = &[ // Sorted alphanumerically (on the first letter) for readability.