Update parse_cipher for prd update
This commit is contained in:
parent
1bf8d91991
commit
3ed94c7538
184
src/api.rs
184
src/api.rs
@ -893,36 +893,37 @@ fn handle_prd(
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
PrdType::Update | PrdType::TxProposal | PrdType::Message => {
|
PrdType::Update => {
|
||||||
// Those all have some new data we don't know about yet
|
// Compute the merkle tree root for the proposed new state to see if we already know about it
|
||||||
// We send a Confirm to get the pcd
|
let update_merkle_root = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?.to_lower_hex_string();
|
||||||
// Add the prd to our list of actions for this process
|
if relevant_process.get_state_for_commitments_root(&update_merkle_root).is_ok() {
|
||||||
relevant_process.insert_impending_request(prd.clone());
|
// We already know about that state
|
||||||
let member: Member = serde_json::from_str(&prd.sender)?;
|
return Err(AnyhowError::msg("Received update for a state we already know"));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should never happen
|
let new_state = ProcessState {
|
||||||
if ciphers.is_empty() {
|
commited_in: OutPoint::from_str(&prd.root_commitment)?,
|
||||||
return Err(anyhow::Error::msg(format!("No available secrets for member {:?}", member)));
|
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 {
|
let updated_process = UpdatedProcess {
|
||||||
commitment_tx: outpoint,
|
commitment_tx: outpoint,
|
||||||
current_process: relevant_process.clone(),
|
current_process: relevant_process.clone(),
|
||||||
|
new_diffs: diffs,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(ApiReturn {
|
return Ok(ApiReturn {
|
||||||
ciphers_to_send: ciphers,
|
|
||||||
updated_process: Some(updated_process),
|
updated_process: Some(updated_process),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@ -936,9 +937,6 @@ fn handle_prd(
|
|||||||
})
|
})
|
||||||
.ok_or(anyhow::Error::msg("Original request not found"))?;
|
.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
|
to_update
|
||||||
.validation_tokens
|
.validation_tokens
|
||||||
.extend(prd.validation_tokens);
|
.extend(prd.validation_tokens);
|
||||||
@ -949,7 +947,7 @@ fn handle_prd(
|
|||||||
let updated_process = UpdatedProcess {
|
let updated_process = UpdatedProcess {
|
||||||
commitment_tx: OutPoint::from_str(&prd.root_commitment)?,
|
commitment_tx: OutPoint::from_str(&prd.root_commitment)?,
|
||||||
current_process: relevant_process.clone(),
|
current_process: relevant_process.clone(),
|
||||||
modified_state: Some((previous_state, updated_state.clone())),
|
modified_state: Some(updated_state.merkle_root),
|
||||||
..Default::default()
|
..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(
|
fn handle_decrypted_message(
|
||||||
secret: AnkSharedSecretHash,
|
secret: AnkSharedSecretHash,
|
||||||
plain: Vec<u8>,
|
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()?;
|
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) {
|
if let Ok(prd) = Prd::extract_from_message(&plain, local_address) {
|
||||||
handle_prd(prd, secret)
|
handle_prd(prd, secret)
|
||||||
} else if let Ok(pcd) = Value::from_str(&String::from_utf8(plain)?) {
|
|
||||||
handle_pcd(pcd)
|
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow::Error::msg("Failed to handle decrypted message"))
|
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]
|
#[wasm_bindgen]
|
||||||
pub fn parse_cipher(cipher_msg: String) -> ApiResult<ApiReturn> {
|
pub fn parse_cipher(cipher_msg: String) -> ApiResult<ApiReturn> {
|
||||||
// Check that the cipher is not empty or too long
|
// Check that the cipher is not empty or too long
|
||||||
|
Loading…
x
Reference in New Issue
Block a user