diff --git a/src/api.rs b/src/api.rs index b9f3a1d..066ee64 100644 --- a/src/api.rs +++ b/src/api.rs @@ -772,6 +772,76 @@ fn confirm_prd(prd: &Prd, shared_secret: &AnkSharedSecretHash) -> AnyhowResult AnyhowResult> { + let new_state_commitments = new_state.pcd_commitment.as_object().ok_or(AnyhowError::msg("new_state commitments is not an object"))?; + + let device = lock_local_device()?; + let our_id = device.to_member(); + let is_pairing = device.get_pairing_commitment().is_none(); + + let fields_to_validate = if new_state.encrypted_pcd != Value::Null { + new_state.get_fields_to_validate_for_member(&our_id)? + } else { + vec![] + }; + + let new_state_root = &new_state.merkle_root; + let new_state_decrypted = match new_state.decrypt_pcd() { + Ok(val) => val, + Err(_) => Map::new() + }; + + let mut diffs = vec![]; + if let Some(prev_state) = process.get_latest_commited_state() { + // We first decrypt as much as we can of the prev_state + let clear_prev_state = prev_state.decrypt_pcd()?; + // We just make a diff for values that are different from previous state + for (field, prev_hash) in prev_state.pcd_commitment.as_object().unwrap() { + if let Some(new_hash) = new_state_commitments.get(field.as_str()) { + if new_hash.as_str() == prev_hash.as_str() { + continue; + } else { + // There's a diff + let previous_value = clear_prev_state.get(field.as_str()).unwrap().clone(); + let new_value = if let Some(val) = new_state_decrypted.get(field.as_str()) { val.clone() } else { Value::Null }; + let need_validation = if (is_pairing && field.as_str() == "roles" && new_value != Value::Null) || fields_to_validate.contains(field) { true } else { false }; + diffs.push(UserDiff { + new_state_merkle_root: new_state_root.to_owned(), + value_commitment: new_hash.as_str().unwrap().to_string(), + field: field.to_owned(), + previous_value, + new_value, + notify_user: false, + need_validation, + validation_status: DiffStatus::None, + }); + } + } else { + // We're missing a hash + return Err(AnyhowError::msg(format!("No commitment for field {} in new state", field))); + } + } + } else { + // All fields need a diff + for (field, hash) in new_state_commitments { + let new_value = if let Some(val) = new_state_decrypted.get(field.as_str()) { val.clone() } else { Value::Null }; + let need_validation = if (is_pairing && field.as_str() == "roles" && new_value != Value::Null) || fields_to_validate.contains(field) { true } else { false }; + diffs.push(UserDiff { + new_state_merkle_root: new_state_root.to_owned(), + value_commitment: hash.as_str().unwrap().to_string(), + field: field.to_owned(), + previous_value: Value::Null, + new_value, + notify_user: false, + need_validation, + validation_status: DiffStatus::None, + }); + } + } + + Ok(diffs) +} + fn handle_prd_connect(prd: Prd, secret: AnkSharedSecretHash) -> AnyhowResult { let local_device = lock_local_device()?; let local_member = local_device.to_member();