diff --git a/crates/sp_client/Cargo.toml b/crates/sp_client/Cargo.toml index 3a82e7a..25f7965 100644 --- a/crates/sp_client/Cargo.toml +++ b/crates/sp_client/Cargo.toml @@ -21,6 +21,7 @@ tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" } aes-gcm = "0.10.3" aes = "0.8.3" shamir = { git = "https://github.com/Sosthene00/shamir", branch = "master" } +img-parts = "0.3.0" [dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/crates/sp_client/src/api.rs b/crates/sp_client/src/api.rs index 3afa033..130c40f 100644 --- a/crates/sp_client/src/api.rs +++ b/crates/sp_client/src/api.rs @@ -23,6 +23,7 @@ use wasm_bindgen::prelude::*; use sp_backend::spclient::{derive_keys_from_seed, OutputList, SpClient}; use sp_backend::spclient::{SpWallet, SpendKey}; +use crate::images; use crate::network::{BitcoinNetworkMsg, BitcoinTopic}; use crate::silentpayments::check_transaction; use crate::user::{lock_connected_users, User, UserWallets, CONNECTED_USERS}; @@ -173,6 +174,17 @@ pub fn create_user( Ok(generate_user) } +#[wasm_bindgen] +pub fn add_data_to_image(image: Vec, data: Vec, is_revoke: bool) -> ApiResult> { + let mut new_image: Vec; + if is_revoke { + new_image = images::BackUpImage::new_revoke(image, &data)?.to_inner(); + } else { + new_image = images::BackUpImage::new_recover(image, &data)?.to_inner(); + } + Ok(new_image) +} + #[derive(Tsify, Serialize, Deserialize)] #[tsify(into_wasm_abi)] #[allow(non_camel_case_types)] diff --git a/crates/sp_client/src/images.rs b/crates/sp_client/src/images.rs new file mode 100644 index 0000000..f0e11ee --- /dev/null +++ b/crates/sp_client/src/images.rs @@ -0,0 +1,38 @@ +use anyhow::{Error, Result}; +use img_parts::{jpeg::Jpeg, Bytes, ImageEXIF}; +use serde::{Deserialize, Serialize}; +use sp_backend::bitcoin::secp256k1::SecretKey; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BackUpImage(Vec); + +impl BackUpImage { + pub fn new_recover(image: Vec, data: &[u8]) -> Result { + // TODO: sanity check on data + let img = write_exif(image, data)?; + Ok(Self(img)) + } + + pub fn new_revoke(image: Vec, data: &[u8]) -> Result { + // TODO: sanity check on data + let img = write_exif(image, data)?; + Ok(Self(img)) + } + + pub fn to_inner(&self) -> Vec { + self.0.clone() + } + + pub fn as_inner(&self) -> &[u8] { + &self.0 + } +} + +fn write_exif(image: Vec, data: &[u8]) -> Result> { + let mut jpeg = Jpeg::from_bytes(Bytes::from(image))?; + let data_bytes = Bytes::from(data.to_owned()); + jpeg.set_exif(Some(data_bytes)); + let output_image_bytes = jpeg.encoder().bytes(); + let output_image = output_image_bytes.to_vec(); + Ok(output_image) +} diff --git a/crates/sp_client/src/lib.rs b/crates/sp_client/src/lib.rs index 73db7f5..95968cb 100644 --- a/crates/sp_client/src/lib.rs +++ b/crates/sp_client/src/lib.rs @@ -6,6 +6,7 @@ use std::sync::{Mutex, MutexGuard}; mod Prd_list; pub mod api; mod crypto; +mod images; mod network; mod peers; mod process; diff --git a/src/database.ts b/src/database.ts index 7413e88..d8d8041 100644 --- a/src/database.ts +++ b/src/database.ts @@ -9,17 +9,17 @@ class Database { // options: {}, // indices: [] // }, - SpOutputs: { - name: "sp_outputs", - options: {'autoIncrement': true}, - indices: [{ - name: 'by_wallet_fingerprint', - keyPath: 'wallet_fingerprint', - options: { - 'unique': false - } - }] - }, + // SpOutputs: { + // name: "sp_outputs", + // options: {'autoIncrement': true}, + // indices: [{ + // name: 'by_wallet_fingerprint', + // keyPath: 'wallet_fingerprint', + // options: { + // 'unique': false + // } + // }] + // }, AnkUser: { name: "user", options: {'keyPath': 'pre_id'}, diff --git a/src/services.ts b/src/services.ts index e331a5b..7160fbc 100644 --- a/src/services.ts +++ b/src/services.ts @@ -89,7 +89,7 @@ class Services { const password = passwordElement.value; this.current_process = processElement.value; - console.log("JS password: " + password + " process: " + this.current_process); + // console.log("JS password: " + password + " process: " + this.current_process); // To comment if test // if (!Services.instance.isPasswordValid(password)) return; @@ -101,10 +101,14 @@ class Services { let createUserReturn: createUserReturn = services.sdkClient.create_user(password, label, birthday_main, birthday_signet, this.current_process); let user = createUserReturn.user; - const outputs_list = createUserReturn.output_list_vec; const shares = user.shares; + // send the shares on the network const revokeData = user.revoke_data; + if (!revokeData) { + console.error('Failed to get revoke data from wasm'); + return; + } user.shares = []; user.revoke_data = null; @@ -113,7 +117,6 @@ class Services { const indexedDb = await IndexedDB.getInstance(); const db = indexedDb.getDb(); await indexedDb.writeObject(db, indexedDb.getStoreList().AnkUser, user, null); - await indexedDb.writeObject(db, indexedDb.getStoreList().SpOutputs, outputs_list, null); } catch (error) { console.error("Failed to write user object :", error); } @@ -163,17 +166,16 @@ class Services { alert("Recover submit to do ..."); } - public async displayRevokeImage(): Promise { + public async displayRevokeImage(revokeData: Uint8Array): Promise { const services = await Services.getInstance(); await services.injectHtml('REVOKE_IMAGE'); services.attachClickListener("displayupdateanid", services.displayUpdateAnId); let imageBytes = await services.getRecoverImage('assets/4nk_revoke.jpg'); if (imageBytes != null) { - let blob = new Blob([imageBytes], {type: 'image/png'}); var elem = document.getElementById("revoke") as HTMLAnchorElement; if (elem != null) { - elem.href = URL.createObjectURL(blob); + let imageWithData = services.sdkClient.add_data_to_image(imageBytes, revokeData, true); } } } @@ -299,6 +301,16 @@ class Services { } } + public async addProcess(process: Process): Promise { + try { + const indexedDB = await IndexedDB.getInstance(); + const db = indexedDB.getDb(); + await indexedDB.writeObject(db, indexedDB.getStoreList().AnkProcess, process, null); + } catch (error) { + console.log('addProcess failed: ',error); + } + } + public async getAllProcess(): Promise { try { const indexedDB = await IndexedDB.getInstance(); @@ -369,7 +381,7 @@ class Services { try { const processStore = await indexedDB.getObject(db, indexedDB.getStoreList().AnkProcess, process.id); if (!processStore) { - console.error('Add process.id : '+process.id); + console.error('Adding process.id : '+process.id); await indexedDB.writeObject(db, indexedDB.getStoreList().AnkProcess, process, null); } } catch (error) { @@ -391,7 +403,6 @@ class Services { } public async injectHtml(processName: string) { - // console.log("JS html : "+html); const container = document.getElementById('containerId'); if (!container) { @@ -401,8 +412,17 @@ class Services { const services = await Services.getInstance(); - await services.loadProcesses(); + // do we have all processes in db? + const knownProcesses = await services.getAllProcess(); + const processesFromNetwork: Process[] = services.sdkClient.get_processes(); + const processToAdd = processesFromNetwork.filter(processFromNetwork => !knownProcesses.some(knownProcess => knownProcess.id === processFromNetwork.id)); + + processToAdd.forEach(async p => { + await services.addProcess(p); + }) + + // get the process we need const process = await services.getProcessByName(processName); if (process) { container.innerHTML = process.html;