use near_client::{near_primitives_light::views::AccessKeyView, near_units, prelude::*}; use serde::{Deserialize, Serialize}; use std::str::FromStr; use url::Url; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; use wasm_bindgen_test::*; use web_client::{KeyProvisioner, Meeting, ProvisionerConfig}; wasm_bindgen_test_configure!(run_in_browser); const ALICE_BYTES: [u8; 32] = [1; 32]; const BOB_BYTES: [u8; 32] = [2; 32]; const KARL_BYTES: [u8; 32] = [3; 32]; const MIKE_BYTES: [u8; 32] = [4; 32]; const CONTRACT_BYTES: [u8; 32] = [5; 32]; #[derive(Serialize, Deserialize)] struct ValidatorKey { account_id: AccountId, public_key: Ed25519PublicKey, secret_key: String, } async fn create_account( client: &NearClient, validator: &Signer, sk_bytes: &[u8; 32], id_str: &str, ) -> Signer { let id = AccountId::from_str(id_str).unwrap(); let sk = Ed25519SecretKey::try_from_bytes(sk_bytes).unwrap(); let pk = Ed25519PublicKey::from(&sk); let amount = near_units::near::parse("200 N").unwrap(); client .create_account(validator, &id, pk, amount) .commit(Finality::Final) .await .unwrap(); let nonce = client .view_access_key(&id, &pk, Finality::None) .await .unwrap() .nonce; Signer::from_secret(sk, id, nonce) } async fn drop_created_account( client: &NearClient, beneficiary_acc_id: &AccountId, sk_bytes: &[u8; 32], id_str: &str, ) { let id = AccountId::from_str(id_str).unwrap(); let sk = Ed25519SecretKey::try_from_bytes(sk_bytes).unwrap(); let pk = Ed25519PublicKey::from(&sk); if let Ok(AccessKeyView { nonce, .. }) = client.view_access_key(&id, &pk, Finality::Final).await { let signer = Signer::from_secret(sk, id.clone(), nonce); signer.update_nonce(nonce); client .delete_account(&signer, &id, beneficiary_acc_id) .commit(Finality::Final) .await .unwrap(); } } async fn validator_key() -> ValidatorKey { reqwest::get("http://sandbox-artifact:3032/validator_key.json") .await .unwrap() .json::() .await .unwrap() } async fn validator_account(client: &NearClient, validator_key: ValidatorKey) -> Signer { let key_nonce = client .view_access_key( &validator_key.account_id, &validator_key.public_key, Finality::Final, ) .await .unwrap() .nonce; Signer::from_secret_str( &validator_key.secret_key, validator_key.account_id.clone(), key_nonce, ) .unwrap() } async fn meeting_room_contract() -> Vec { reqwest::get("http://contract-artifact:3033/meet.wasm") .await .unwrap() .bytes() .await .unwrap() .to_vec() } #[wasm_bindgen_test] async fn key_exchange() { let rpc_url = Url::parse("http://sandbox:3030").unwrap(); let client = NearClient::new(rpc_url).unwrap(); let validator_key = validator_key().await; let validator = validator_account(&client, validator_key).await; // delete accounts if any to be sure there are no duplicates drop_created_account( &client, validator.account(), &ALICE_BYTES, "alice.test.near", ) .await; drop_created_account(&client, validator.account(), &BOB_BYTES, "bob.test.near").await; drop_created_account(&client, validator.account(), &KARL_BYTES, "karl.test.near").await; drop_created_account(&client, validator.account(), &MIKE_BYTES, "mike.test.near").await; drop_created_account( &client, validator.account(), &CONTRACT_BYTES, "contract.test.near", ) .await; // create account for key-exchange // Alice is an initiator account let alice_sg = create_account(&client, &validator, &ALICE_BYTES, "alice.test.near").await; let bob_sg = create_account(&client, &validator, &BOB_BYTES, "bob.test.near").await; let karl_sg = create_account(&client, &validator, &KARL_BYTES, "karl.test.near").await; let mike_sg = create_account(&client, &validator, &MIKE_BYTES, "mike.test.near").await; let contract_sg = create_account(&client, &validator, &CONTRACT_BYTES, "contract.test.near").await; // Contract byte code let contract_wasm = meeting_room_contract().await; let contract_id = contract_sg.account().clone(); client .deploy_contract(&contract_sg, &contract_id, contract_wasm) .commit(Finality::Final) .await .unwrap(); // config with endpoints let config = ProvisionerConfig::new( contract_id.to_string(), "http://sandbox:3030", "http://key-exchange:3000", ) .unwrap(); // Create provisioners for each user let keypair_str = alice_sg.secret_key().string(); let provisioner = KeyProvisioner::new( keypair_str, alice_sg.nonce(), alice_sg.account().to_string(), config.clone(), ) .unwrap(); let arr = JsValue::from( &[ bob_sg.account().to_string(), karl_sg.account().to_string(), mike_sg.account().to_string(), ] .into_iter() .map(|it| JsValue::from_str(&it)) .collect::(), ); let set = js_sys::Set::new(&arr); let Meeting { meet_id, .. } = serde_wasm_bindgen::from_value::( JsFuture::from(provisioner.init_meeting(set, 3000)) .await .unwrap(), ) .unwrap(); // Then each user push own key to blockchain let provisioner_bob = KeyProvisioner::new( bob_sg.secret_key().string(), bob_sg.nonce(), bob_sg.account().to_string(), config.clone(), ) .unwrap(); let provisioner_karl = KeyProvisioner::new( karl_sg.secret_key().string(), karl_sg.nonce(), karl_sg.account().to_string(), config.clone(), ) .unwrap(); let provisioner_mike = KeyProvisioner::new( mike_sg.secret_key().string(), mike_sg.nonce(), mike_sg.account().to_string(), config.clone(), ) .unwrap(); let _ = JsFuture::from(provisioner.send_keys(meet_id.to_string(), 300)).await; gloo_timers::future::sleep(std::time::Duration::from_millis(400)).await; let _ = JsFuture::from(provisioner_bob.get_key(meet_id.to_string(), 200)).await; let _ = JsFuture::from(provisioner_karl.get_key(meet_id.to_string(), 200)).await; let _ = JsFuture::from(provisioner_mike.get_key(meet_id.to_string(), 200)).await; // Send encrypted keys to participants // timeout in millis let alice_key = JsFuture::from(provisioner.send_keys(meet_id.to_string(), 4000)) .await .unwrap() .as_string() .unwrap(); // Fetch encrypted keys let bob_key = JsFuture::from(provisioner_bob.get_key(meet_id.to_string(), 1000)) .await .unwrap() .as_string() .unwrap(); let karl_key = JsFuture::from(provisioner_karl.get_key(meet_id.to_string(), 1000)) .await .unwrap() .as_string() .unwrap(); let mike_key = JsFuture::from(provisioner_mike.get_key(meet_id.to_string(), 1000)) .await .unwrap() .as_string() .unwrap(); assert!(&[mike_key, bob_key, karl_key] .into_iter() .all(|key| key == alice_key)); // delete all accounts drop_created_account( &client, validator.account(), &ALICE_BYTES, "alice.test.near", ) .await; drop_created_account(&client, validator.account(), &BOB_BYTES, "bob.test.near").await; drop_created_account(&client, validator.account(), &KARL_BYTES, "karl.test.near").await; drop_created_account(&client, validator.account(), &MIKE_BYTES, "mike.test.near").await; drop_created_account( &client, validator.account(), &CONTRACT_BYTES, "contract.test.near", ) .await; }