diff --git a/src/api.rs b/src/api.rs index b90f2e9..7106e89 100644 --- a/src/api.rs +++ b/src/api.rs @@ -52,7 +52,7 @@ use sdk_common::sp_client::silentpayments::{ use sdk_common::{signature, MutexExt, MAX_PRD_PAYLOAD_SIZE}; use serde_json::{Error as SerdeJsonError, Map, Value}; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Serialize}; use tsify::{JsValueSerdeExt, Tsify}; use wasm_bindgen::convert::{FromWasmAbi, VectorFromWasmAbi}; use wasm_bindgen::prelude::*; @@ -96,7 +96,6 @@ pub struct ApiReturn { pub new_tx_to_send: Option, pub ciphers_to_send: Vec, pub commit_to_send: Option, - pub decrypted_pcds: HashMap, } pub type ApiResult = Result; @@ -1572,31 +1571,53 @@ pub fn create_faucet_msg() -> ApiResult { Ok(faucet_msg.to_string()) } +#[derive(Debug, PartialEq, Tsify, Serialize, Deserialize, Default)] +#[tsify(into_wasm_abi)] +#[allow(non_camel_case_types)] +struct UserDiff { + new_state_merkle_root: String, // TODO add a merkle proof that the new_value belongs to that state + field: String, + previous_value: Value, + new_value: Value, + notify_user: bool, + need_validation: bool, + proof: Option, // This is only validation (or refusal) for that specific diff, not the whole state. It can't be commited as such +} + +#[derive(Debug, PartialEq, Tsify, Serialize, Deserialize, Default)] +#[tsify(into_wasm_abi)] +#[allow(non_camel_case_types)] +pub struct PcdUpdates { + pub previous_pcd: Option, // We don't have a previous state for creation + pub decrypted_pcds: HashMap, // Key is the merkle root of the whole state + pub modified_values: Vec, +} + /// Get active update proposals for a given process outpoint /// Returns a vector with the latest commited state first, if any, and all active proposals #[wasm_bindgen] -pub fn get_update_proposals(process_outpoint: String) -> ApiResult { +pub fn get_update_proposals(process_outpoint: String) -> ApiResult { let outpoint = OutPoint::from_str(&process_outpoint)?; let mut processes = lock_processes()?; - // TODO: We clone the process to prevent double borrowing issue, this can certainly be improved let relevant_process = processes .get(&outpoint) .ok_or(ApiError::new("process not found".to_owned()))?; let mut decrypted_pcds = HashMap::new(); + let mut modified_values = Vec::new(); - // We first push the last commited state, if any - match relevant_process.get_latest_commited_state() { + let previous_pcd = match relevant_process.get_latest_commited_state() { Some(state) => { let mut decrypted_pcd = Map::new(); - state.encrypted_pcd.decrypt_fields(&state.keys, &mut decrypted_pcd); - let root = ::create_merkle_tree(&state.pcd_commitment)?.root().unwrap(); - decrypted_pcds.insert(root.to_lower_hex_string(), Value::Object(decrypted_pcd)); + state.encrypted_pcd.decrypt_fields(&state.pcd_commitment.to_value_object()?, &state.keys, &mut decrypted_pcd); + Some(Value::Object(decrypted_pcd)) } - None => () - } + None => None + }; + + let member = lock_local_device()?.to_member(); for state in relevant_process.get_latest_concurrent_states()? { if state.encrypted_pcd == Value::Null { @@ -1604,14 +1625,52 @@ pub fn get_update_proposals(process_outpoint: String) -> ApiResult { continue; } + let fields_to_validate = state.get_fields_to_validate_for_member(&member)?; + let mut decrypted_pcd = Map::new(); - state.encrypted_pcd.decrypt_fields(&state.keys, &mut decrypted_pcd)?; - let root = ::create_merkle_tree(&state.pcd_commitment)?.root().unwrap(); - decrypted_pcds.insert(root.to_lower_hex_string(), Value::Object(decrypted_pcd)); + state.encrypted_pcd.decrypt_fields(&state.pcd_commitment.to_value_object()?, &state.keys, &mut decrypted_pcd)?; + let root = state.pcd_commitment.create_merkle_tree()?.root_hex().unwrap(); + + if let Some(ref previous_state) = previous_pcd { + for (key, value) in &decrypted_pcd { + let previous_value = previous_state.get(key).or_else(|| Some(&Value::Null)).unwrap(); + if previous_value == value { continue; } + let need_validation = if fields_to_validate.iter().any(|f| *key == **f) { true } else { false }; + let notify_user = if need_validation { true } else if !value.is_hex_string() { true } else { false }; + let diff = UserDiff { + new_state_merkle_root: root.clone(), + field: key.to_owned(), + previous_value: previous_value.clone(), + new_value: value.clone(), + need_validation, + notify_user, + proof: None + }; + modified_values.push(diff); + } + } else { + for (key, value) in &decrypted_pcd { + let need_validation = if fields_to_validate.iter().any(|f| *key == **f) { true } else { false }; + let notify_user = if need_validation { true } else if !value.is_hex_string() { true } else { false }; + let diff = UserDiff { + new_state_merkle_root: root.clone(), + field: key.to_owned(), + previous_value: Value::Null, + new_value: value.clone(), + need_validation, + notify_user, + proof: None + }; + modified_values.push(diff); + } + + } + decrypted_pcds.insert(root, Value::Object(decrypted_pcd)); } - Ok(ApiReturn { - decrypted_pcds, - ..Default::default() + Ok(PcdUpdates { + previous_pcd, + decrypted_pcds, + modified_values, }) }