您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

lib.rs 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. mod contract;
  2. mod crypto;
  3. pub mod error;
  4. mod exchange_client;
  5. pub use contract::{
  6. add_participant, init_meeting, is_meet_participant, view_meet_participants,
  7. view_moderator_account,
  8. };
  9. use common_api::api::{Data, ExchangeMessage};
  10. use crypto::{decrypt, encrypt, secret};
  11. use error::ApiError;
  12. use exchange_client::{exchange, public_keys, receive};
  13. use futures::{select, FutureExt};
  14. use gloo_timers::future::TimeoutFuture;
  15. use itertools::Itertools;
  16. use js_sys::Promise;
  17. use log::{info, warn};
  18. use near_primitives_core::{account::id::AccountId, hash::CryptoHash, types::Nonce};
  19. use near_rpc::client::Signer;
  20. use serde::{Deserialize, Serialize};
  21. use std::{collections::HashSet, str::FromStr, sync::Arc};
  22. use url::Url;
  23. use uuid::Uuid;
  24. use wasm_bindgen::prelude::*;
  25. type Result<T> = std::result::Result<T, ApiError>;
  26. #[wasm_bindgen(start)]
  27. pub fn start() -> Result<()> {
  28. console_error_panic_hook::set_once();
  29. console_log::init().unwrap();
  30. Ok(())
  31. }
  32. fn to_value<T: Serialize>(value: &T) -> JsValue {
  33. match serde_wasm_bindgen::to_value(value) {
  34. Ok(value) => value,
  35. Err(err) => err.into(),
  36. }
  37. }
  38. pub struct Handler {
  39. contract_id: AccountId,
  40. secret: [u8; 32],
  41. exchange_url: url::Url,
  42. rpc_url: url::Url,
  43. }
  44. impl Handler {
  45. pub fn add_path(&self, path: &str) -> url::Url {
  46. let mut url = self.exchange_url.clone();
  47. url.set_path(path);
  48. url
  49. }
  50. }
  51. #[derive(Clone, Debug, Serialize, Deserialize)]
  52. pub struct Meeting {
  53. pub meet_id: Uuid,
  54. pub transaction_id: CryptoHash,
  55. }
  56. #[wasm_bindgen]
  57. #[derive(Debug, Clone)]
  58. pub struct ProvisionerConfig {
  59. contract_id: AccountId,
  60. rpc_url: Url,
  61. exchange_url: Url,
  62. }
  63. #[wasm_bindgen]
  64. impl ProvisionerConfig {
  65. #[wasm_bindgen(constructor)]
  66. pub fn new(
  67. contract_id: String,
  68. rpc_url: &str,
  69. exchange_url: &str,
  70. ) -> Result<ProvisionerConfig> {
  71. let rpc_url = Url::from_str(rpc_url)
  72. .map_err(|err| ApiError::Other(format!("Bad rpc url, cause {err}")))?;
  73. let exchange_url = Url::from_str(exchange_url)
  74. .map_err(|err| ApiError::Other(format!("Bad exchange url, cause {err}")))?;
  75. let contract_id = AccountId::from_str(&contract_id).map_err(ApiError::from)?;
  76. Ok(Self {
  77. contract_id,
  78. rpc_url,
  79. exchange_url,
  80. })
  81. }
  82. }
  83. #[wasm_bindgen]
  84. pub struct KeyProvisioner {
  85. signer: Arc<Signer>,
  86. handler: Arc<Handler>,
  87. }
  88. impl KeyProvisioner {
  89. fn handler(&self) -> Arc<Handler> {
  90. Arc::clone(&self.handler)
  91. }
  92. fn signer(&self) -> Arc<Signer> {
  93. Arc::clone(&self.signer)
  94. }
  95. }
  96. #[wasm_bindgen]
  97. impl KeyProvisioner {
  98. #[wasm_bindgen(constructor)]
  99. pub fn new(
  100. keypair_str: String,
  101. nonce: Nonce,
  102. account_id: String,
  103. ProvisionerConfig {
  104. contract_id,
  105. rpc_url,
  106. exchange_url,
  107. }: ProvisionerConfig,
  108. ) -> Result<KeyProvisioner> {
  109. let signer = Arc::new(Signer::from_secret_str(
  110. &keypair_str,
  111. AccountId::from_str(&account_id)?,
  112. nonce,
  113. )?);
  114. let handler = Arc::new(Handler {
  115. contract_id,
  116. secret: secret(),
  117. exchange_url,
  118. rpc_url,
  119. });
  120. Ok(Self { signer, handler })
  121. }
  122. /// Initializes meeting by calling the contract and providing there a set of participants' keys
  123. ///
  124. /// Arguments
  125. ///
  126. /// - participants_set - The [`js_sys::Set`] represents hash set of participants' keys
  127. /// - timeout_ms - The [`u32`] that represents milliseconds that were given not to be exceeded
  128. pub fn init_meeting(&self, participants_set: js_sys::Set, timeout_ms: u32) -> Promise {
  129. let handler = self.handler();
  130. let signer = self.signer();
  131. let init_meeting = async move {
  132. let participants: HashSet<AccountId> = participants_set
  133. .values()
  134. .into_iter()
  135. .map_ok(|it| {
  136. let Some(acc_id) = it.as_string() else {
  137. return Err(ApiError::Other("Set item type isn't a string".to_owned()));
  138. };
  139. AccountId::from_str(&acc_id).map_err(ApiError::from)
  140. })
  141. .flatten_ok()
  142. .try_collect()?;
  143. let (meet_id, transaction_id) = init_meeting(&handler, &signer, participants).await?;
  144. Ok(to_value(&Meeting {
  145. meet_id,
  146. transaction_id,
  147. }))
  148. };
  149. wasm_bindgen_futures::future_to_promise(async move {
  150. select! {
  151. meet = init_meeting.fuse() => meet,
  152. _ = TimeoutFuture::new(timeout_ms).fuse() => {
  153. Err(ApiError::CallTimeout("The initialization has been timed out".to_owned()).into())
  154. }
  155. }
  156. })
  157. }
  158. /// Sends participants' keys to the keys exchange server
  159. ///
  160. /// Arguments
  161. ///
  162. /// - meeting_id - The [`String`] that indicates ID of the meeting room
  163. /// - timeout_ms - The [`u32`] that represents milliseconds that were given not to be exceeded
  164. pub fn send_keys(&self, meeting_id: String, timeout_ms: u32) -> Promise {
  165. let handler = self.handler();
  166. let signer = self.signer();
  167. let send_keys = async move {
  168. let meet_id = Uuid::from_str(&meeting_id).map_err(ApiError::from)?;
  169. let mut participants = view_meet_participants(&handler, meet_id)
  170. .await?
  171. .ok_or_else(|| {
  172. ApiError::InvalidSessionUuid(format!("Wrong Session ID: {meet_id}"))
  173. })?;
  174. info!("Get a meeting participants {participants:?}");
  175. while !participants.is_empty() {
  176. let infos =
  177. match public_keys(&handler, &signer, meet_id, participants.clone()).await {
  178. Ok(infos) => infos,
  179. Err(err) => {
  180. warn!("Failed to fetch a public keys, cause {err:?}");
  181. continue;
  182. }
  183. };
  184. // remove infos that is already processed
  185. for key in &infos {
  186. participants.remove(&key.account_id);
  187. }
  188. let messages = infos
  189. .into_iter()
  190. .map(|info| {
  191. let sk = signer.secret_key();
  192. let other_pk = info.public_key;
  193. let msg = encrypt(sk, other_pk, meet_id, &handler.secret)?;
  194. Ok::<ExchangeMessage, JsValue>(ExchangeMessage {
  195. account_id: info.account_id,
  196. message: Data {
  197. data: msg,
  198. moderator_pk: signer.public_key().to_owned(),
  199. },
  200. })
  201. })
  202. .try_collect()?;
  203. exchange(&handler, &signer, meet_id, messages).await?;
  204. }
  205. Ok(JsValue::from_str(&base64::encode(handler.secret)))
  206. };
  207. wasm_bindgen_futures::future_to_promise(async move {
  208. select! {
  209. aes_secret = send_keys.fuse() => aes_secret,
  210. _ = TimeoutFuture::new(timeout_ms).fuse() => {
  211. Err(ApiError::CallTimeout("The send keys operation has been timed out".to_owned()).into())
  212. }
  213. }
  214. })
  215. }
  216. /// Get participant's key from a server
  217. ///
  218. /// Arguments
  219. ///
  220. /// - meeting_id - The [`String`] that indicates ID of the meeting room
  221. /// - timeout_ms - The [`u32`] that represents milliseconds that were given not to be exceeded
  222. pub fn get_key(&self, meeting_id: String, timeout_ms: u32) -> Promise {
  223. let handler = self.handler();
  224. let signer = self.signer();
  225. let get_key = async move {
  226. let meet_id = Uuid::from_str(&meeting_id).map_err(ApiError::from)?;
  227. let data = receive(&handler, &signer, meet_id).await?;
  228. let secret = decrypt(signer.secret_key(), data.moderator_pk, meet_id, data.data)?;
  229. Ok(JsValue::from_str(&base64::encode(secret)))
  230. };
  231. wasm_bindgen_futures::future_to_promise(async move {
  232. select! {
  233. key = get_key.fuse() => key,
  234. _ = TimeoutFuture::new(timeout_ms).fuse() => {
  235. Err(ApiError::CallTimeout("The getting key operation has been timed out".to_owned()).into())
  236. }
  237. }
  238. })
  239. }
  240. }