Update parse_cipher for prd update

This commit is contained in:
Sosthene 2024-12-11 23:34:00 +01:00
parent 1bf8d91991
commit 3ed94c7538

View File

@ -893,36 +893,37 @@ fn handle_prd(
..Default::default()
})
}
PrdType::Update | PrdType::TxProposal | PrdType::Message => {
// Those all have some new data we don't know about yet
// We send a Confirm to get the pcd
// Add the prd to our list of actions for this process
relevant_process.insert_impending_request(prd.clone());
let member: Member = serde_json::from_str(&prd.sender)?;
let mut ciphers = vec![];
for address in member.get_addresses() {
if let Some(shared_secret) = lock_shared_secrets()?.get_secret_for_address(address.as_str().try_into()?) {
let cipher = confirm_prd(&prd, &shared_secret)?;
ciphers.push(cipher);
} else {
// For now we don't fail if we're missing an address for a member but maybe we should
warn!("Failed to find secret for address {}", address);
}
PrdType::Update => {
// Compute the merkle tree root for the proposed new state to see if we already know about it
let update_merkle_root = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?.to_lower_hex_string();
if relevant_process.get_state_for_commitments_root(&update_merkle_root).is_ok() {
// We already know about that state
return Err(AnyhowError::msg("Received update for a state we already know"));
}
// This should never happen
if ciphers.is_empty() {
return Err(anyhow::Error::msg(format!("No available secrets for member {:?}", member)));
}
let new_state = ProcessState {
commited_in: OutPoint::from_str(&prd.root_commitment)?,
pcd_commitment: prd.pcd_commitments,
merkle_root: update_merkle_root.clone(),
keys: prd.keys,
..Default::default()
};
// Compute the diffs
// At this point we don't have the encrypted values
// But it can still be useful to track diffs
let diffs = create_diffs(&relevant_process, &new_state)?;
relevant_process.insert_concurrent_state(new_state);
let updated_process = UpdatedProcess {
commitment_tx: outpoint,
current_process: relevant_process.clone(),
new_diffs: diffs,
..Default::default()
};
return Ok(ApiReturn {
ciphers_to_send: ciphers,
updated_process: Some(updated_process),
..Default::default()
});
@ -936,9 +937,6 @@ fn handle_prd(
})
.ok_or(anyhow::Error::msg("Original request not found"))?;
// Once we found the prd update, we can add the received proofs as validation tokens
let previous_state = to_update.clone();
to_update
.validation_tokens
.extend(prd.validation_tokens);
@ -949,7 +947,7 @@ fn handle_prd(
let updated_process = UpdatedProcess {
commitment_tx: OutPoint::from_str(&prd.root_commitment)?,
current_process: relevant_process.clone(),
modified_state: Some((previous_state, updated_state.clone())),
modified_state: Some(updated_state.merkle_root),
..Default::default()
};
@ -962,54 +960,6 @@ fn handle_prd(
}
}
fn handle_pcd(pcd: Value) -> AnyhowResult<ApiReturn> {
// We received an encrypted pcd, so we can compute the merkle root of a tree where all the encrypted values are the leaves
// We pass an empty Outpoint as salt, as there's no point salting hash of encrypted values
let encrypted_pcd_commitments = pcd.hash_all_fields(OutPoint::null())?;
let encrypted_pcd_root = <Value as Pcd>::create_merkle_tree(&Value::Object(encrypted_pcd_commitments))?.root().unwrap().to_lower_hex_string();
let mut processes = lock_processes()?;
let updated_prd: Prd;
for (outpoint, process) in processes.iter_mut() {
// We check all pending requests and match the payload with the hash of this pcd
if let Some(prd) = process
.get_impending_requests_mut()
.into_iter()
.find(|r| *r.payload == encrypted_pcd_root)
{
// We update the process and return it
prd.payload = pcd.to_string();
updated_prd = prd.clone();
// We can now safely mark the prd to be remove from the process
prd.prd_type = PrdType::None;
} else {
continue;
}
let new_state = ProcessState {
commited_in: *outpoint,
pcd_commitment: updated_prd.pcd_commitments,
encrypted_pcd: pcd,
keys: updated_prd.keys,
validation_tokens: vec![]
};
process.insert_concurrent_state(new_state.clone())?;
process.prune_impending_requests();
let udpated_process = UpdatedProcess {
commitment_tx: *outpoint,
current_process: process.clone(),
new_state: Some(new_state),
..Default::default()
};
return Ok(ApiReturn {
updated_process: Some(udpated_process),
..Default::default()
});
}
Err(anyhow::Error::msg("Failed to find matching prd"))
}
fn handle_decrypted_message(
secret: AnkSharedSecretHash,
plain: Vec<u8>,
@ -1017,13 +967,99 @@ fn handle_decrypted_message(
let local_address: SilentPaymentAddress = lock_local_device()?.get_wallet().get_client().get_receiving_address().try_into()?;
if let Ok(prd) = Prd::extract_from_message(&plain, local_address) {
handle_prd(prd, secret)
} else if let Ok(pcd) = Value::from_str(&String::from_utf8(plain)?) {
handle_pcd(pcd)
} else {
Err(anyhow::Error::msg("Failed to handle decrypted message"))
}
}
#[wasm_bindgen]
/// Use the provided Map to update a state
/// The map uses hash commitment as keys, as in storage
pub fn update_process_state(init_commitment: String, state_id: String, hash2values: String) -> ApiResult<ApiReturn> {
let hash2values_map = serde_json::from_str::<Value>(&hash2values)?.to_value_object()?;
// Get the process
let outpoint = OutPoint::from_str(&init_commitment)?;
let mut processes = lock_processes()?;
{
// First a mutable borrow of the process
let process = processes.get_mut(&outpoint)
.ok_or(ApiError::new("Unknown process".to_owned()))?;
// Get the state
let state = process.get_latest_concurrent_states_mut()?
.into_iter()
.find(|state| state.merkle_root == state_id)
.ok_or(ApiError::new("Unknown state".to_owned()))?;
// Update each value
// Check if there's already something
// If we have the key, decrypt and compare to the commitment
if state.encrypted_pcd.as_object().is_some() && !state.encrypted_pcd.as_object().unwrap().is_empty() {
return Err(ApiError::new("State already existing".to_owned()));
}
let state_commitments = state.pcd_commitment.to_value_object()?;
let mut new_encrypted_pcd: Map<String, Value> = Map::with_capacity(hash2values_map.len());
for (hash, value) in hash2values_map {
// Check the hash in pcd_commitment, get the corresponding field name
let (field, _) = state_commitments.iter().find(|(field, commitment)| *hash == **commitment)
.ok_or(ApiError::new(format!("Failed to find the commitment {}", hash)))?;
new_encrypted_pcd.insert(field.clone(), value);
}
// decrypt all we can and check it matches the commitment
state.encrypted_pcd = Value::Object(new_encrypted_pcd);
let commited_in = serialize(&state.commited_in);
let clear_pcd = state.decrypt_pcd()?;
for (i, (key, value)) in clear_pcd.iter().enumerate() {
// hash each value, and check the result against commitments
if let Some(expected) = state_commitments.get(key.as_str()) {
// value can already be the commitment, if we don't have the encryption key
if value.is_hex_string(Some(32)).is_ok() {
// check if the clear value is the commitment
if expected.as_str().unwrap() == value.as_str().unwrap() { continue; }
}
// Otherwise we hash the value whatever it is, it must match the commitment
let mut value_bin = value.to_string().into_bytes();
value_bin.push(i.try_into().unwrap());
let tagged_hash = AnkPcdHash::from_value_with_outpoint(&value_bin, &commited_in);
if tagged_hash.as_byte_array().to_lower_hex_string() != expected.as_str().unwrap() {
// We set the encrypted pcd back to empty
state.encrypted_pcd = Value::Object(Map::new());
return Err(ApiError::new(format!("Retrieved value for {} doesn't match the commitment", key)));
}
} else {
// This shouldn't be possible
state.encrypted_pcd = Value::Object(Map::new());
return Err(ApiError::new(format!("Missing commitment for key {}", key)));
}
}
}
// If every value we can decrypt is valid, then we return the new state and diffs
// We borrow it again immutably
let process = processes.get(&outpoint).unwrap();
let state = process.get_latest_concurrent_states()?.into_iter().find(|s| s.merkle_root == state_id).unwrap();
let diffs = create_diffs(&process, &state)?;
let udpated_process = UpdatedProcess {
commitment_tx: outpoint,
current_process: process.clone(),
new_diffs: diffs,
..Default::default()
};
Ok(ApiReturn {
updated_process: Some(udpated_process),
..Default::default()
})
}
#[wasm_bindgen]
pub fn parse_cipher(cipher_msg: String) -> ApiResult<ApiReturn> {
// Check that the cipher is not empty or too long