diff --git a/src/api.rs b/src/api.rs index bf801a9..9e02341 100644 --- a/src/api.rs +++ b/src/api.rs @@ -997,6 +997,116 @@ fn handle_prd( ..Default::default() }); } + PrdType::Request => { + // We are being requested encrypted data for one or more states, to be uploaded on storage + let states: Vec<[u8; 32]> = serde_json::from_str(&prd.payload)?; + let requester: Member = serde_json::from_str(&prd.sender)?; + + // diffs will trigger upload of the encrypted data on storage + let mut diffs = vec![]; + // This will notify the requester and provide relevant information if needed + let mut ciphers = vec![]; + + for state_id in states { + let state = match relevant_process.get_state_for_id(&state_id.to_lower_hex_string()) { + Ok(state) => state, + Err(_) => { + debug!("Ignoring request for unknown state {}", state_id.to_lower_hex_string()); + continue; + } + }; + let decrypted_map = state.decrypt_pcd()?; + + let roles = Value::Object(decrypted_map.clone()).extract_roles()?; + + let local_device = lock_local_device()?; + + let sp_wallet = local_device.get_wallet(); + let local_address = sp_wallet.get_client().get_receiving_address(); + + let mut relevant_fields: HashSet = HashSet::new(); + let shared_secrets = lock_shared_secrets()?; + for (name, role) in &roles { + if !role.members.contains(&requester) { + // This role doesn't concern requester + continue; + } + let fields: Vec = role + .validation_rules + .iter() + .flat_map(|rule| rule.fields.clone()) + .collect(); + if let Some(no_secret_address) = requester.get_addresses().iter() + .find(|a| shared_secrets.get_secret_for_address(a.as_str().try_into().unwrap()).is_none()) + { + // We ignore it if we don't have a secret with ourselves + if *no_secret_address != local_address { + // for now we return an error to keep it simple + return Err(AnyhowError::msg(format!("No shared secret for all addresses of {:?}\nPlease first connect", requester))); + } + } + relevant_fields.extend(fields); + } + + let sender: Member = local_device + .to_member(); + + let mut full_prd = Prd::new_update( + outpoint, + sender, + roles, + state.keys.clone(), + state.pcd_commitment.clone(), + ); + + full_prd.filter_keys(&relevant_fields); + let prd_msg = full_prd.to_network_msg(sp_wallet)?; + + let addresses = requester.get_addresses(); + for sp_address in addresses.into_iter() { + // We skip our own device address, no point sending ourself a cipher + if sp_address == local_address { + continue; + } + + // We shouldn't ever have error here since we already checked above + let shared_secret = shared_secrets.get_secret_for_address(sp_address.as_str().try_into()?).unwrap(); + + let cipher = encrypt_with_key(shared_secret.as_byte_array(), prd_msg.as_bytes())?; + ciphers.push(cipher.to_lower_hex_string()); + } + + let pcd_commitment: HashMap = serde_json::from_value(state.pcd_commitment.clone())?; + for (field, hash) in pcd_commitment { + // We only need field that are visible by requester + if !relevant_fields.contains(&field) { + continue; + } + let diff = UserDiff { + process_id: outpoint.to_string(), + state_id: state_id.to_lower_hex_string(), + value_commitment: hash, + new_value: decrypted_map[&field].clone(), + field, + ..Default::default() + }; + diffs.push(diff); + } + } + + let updated_process = Some(UpdatedProcess { + process_id: outpoint, + current_process: relevant_process.clone(), + diffs, + ..Default::default() + }); + + return Ok(ApiReturn { + updated_process, + ciphers_to_send: ciphers, + ..Default::default() + }); + } _ => unimplemented!(), } }