use std::collections::HashMap; use sdk_client::api::{ create_device_from_sp_wallet, create_process_from_template, create_process_init_transaction, get_address, get_outputs, pair_device, reset_device, setup }; use sdk_client::lock_processes; use sdk_common::network::CachedMessage; use sdk_common::pcd::{Member, Pcd}; use sdk_common::prd::Prd; use sdk_common::sp_client::bitcoin::OutPoint; use sdk_common::sp_client::spclient::OwnedOutput; use sdk_common::uuid::Uuid; use sdk_common::log::debug; use serde_json::{self, json}; use tsify::JsValueSerdeExt; use wasm_bindgen_test::*; mod utils; use utils::*; wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test] fn test_pairing() { setup(); debug!("==============================================\nStarting test_pairing\n=============================================="); // ========================= Alice reset_device().unwrap(); create_device_from_sp_wallet(ALICE_LOGIN_WALLET.to_owned()).unwrap(); // we get our own address let device_address = get_address().unwrap(); // we scan the qr code or get the address by any other means let paired_device = helper_get_bob_address(); // Alice creates the new member with Bob address let new_member = Member::new( vec![ device_address.as_str().try_into().unwrap(), paired_device.as_str().try_into().unwrap(), ] ).unwrap(); // We get the template for the pairing // We don't really care how we get it, we can even imagine user writing it himself // It just have to respect the basic Process struct, i.e. have all the fields below and the right type for the value let pairing_template = json!({ "uuid": "", "html": "", "script": "", "style": "", "init_state": { "roles": { "owner": { "members": [ new_member ], "validation_rules": [ { "quorum": 0.0, "fields": [ "roles", "pairing_tx" ], "min_sig_member": 0.0 } ] } }, "pairing_tx": "", }, "commited_in": OutPoint::null() }); let new_process = create_process_from_template(pairing_template.to_string()).unwrap(); // we can update our local device now pair_device(new_process.uuid.clone(), vec![helper_get_bob_address()]).unwrap(); debug!("Alice sends a transaction commiting to an init prd to Bob"); let alice_pairing_return = create_process_init_transaction(new_process, 1).unwrap(); let get_outputs_result = get_outputs().unwrap(); let alice_outputs: HashMap = get_outputs_result.into_serde().unwrap(); let alice_pairing_tweak_data = helper_get_tweak_data(&alice_pairing_return.transaction, alice_outputs); // Alice parse her own transaction helper_parse_transaction(&alice_pairing_return.transaction, &alice_pairing_tweak_data).id; // Notify user that we're waiting for confirmation from the other device // ======================= Bob reset_device().unwrap(); create_device_from_sp_wallet(BOB_LOGIN_WALLET.to_owned()).unwrap(); // Bob receives Alice pairing transaction debug!("Bob parses Alice pairing transaction"); helper_parse_transaction(&alice_pairing_return.transaction, &alice_pairing_tweak_data); debug!("Bob receives the prd"); let mut bob_retrieved_prd = CachedMessage::default(); for message in alice_pairing_return.new_messages.iter() { for cipher in message.cipher.iter() { match helper_parse_cipher(cipher.clone()) { Ok(res) => bob_retrieved_prd = res, Err(_) => continue } } } if bob_retrieved_prd == CachedMessage::default() { panic!("Bob failed to retrieve Alice message"); } debug!("Bob receives the pcd"); let mut bob_retrieved_pcd = CachedMessage::default(); for message in alice_pairing_return.new_messages { for cipher in message.cipher { match helper_parse_cipher(cipher) { Ok(res) => bob_retrieved_pcd = res, Err(_) => continue } } } if bob_retrieved_pcd == CachedMessage::default() { panic!("Bob failed to retrieve Alice message"); } // At this point, user must validate the pairing proposal received from Alice debug!("Bob pairs device with Alice"); let process = lock_processes().unwrap(); let prd: Prd = serde_json::from_str(&bob_retrieved_prd.prd.unwrap()).unwrap(); let relevant_process = process.get(&Uuid::parse_str(&prd.process_uuid).unwrap()).unwrap(); // decrypt the pcd and update bob device if let Some(initial_state) = relevant_process.get_status_at(0) { let keys = initial_state.keys; let mut pcd = initial_state.encrypted_pcd; pcd.decrypt_fields(&keys).unwrap(); pair_device(relevant_process.get_process().uuid, vec![device_address]).unwrap(); } // To make the pairing effective, alice and bob must now spend their respective output into a new transaction // login(); // Once we know this tx id, we can commit to the relay }