diff --git a/src/api.rs b/src/api.rs index 8e742a4..a26eeac 100644 --- a/src/api.rs +++ b/src/api.rs @@ -889,7 +889,6 @@ fn handle_prd( }); } PrdType::Response => { - // We must know of a prd update that the response answers to let mut updated_state = relevant_process .get_latest_concurrent_states_mut()? .into_iter() @@ -1251,7 +1250,7 @@ pub fn update_process( #[wasm_bindgen] pub fn create_update_message( init_commitment: String, - pcd_commitment: String, + merkle_root_hex: String, ) -> ApiResult { let mut processes = lock_processes()?; @@ -1260,18 +1259,17 @@ pub fn create_update_message( let process = processes.get_mut(&outpoint) .ok_or(ApiError::new("Unknown process".to_owned()))?; - let latest_states = process.get_latest_concurrent_states()?; - // This is a map of keys to hash of the clear values - let new_state_commitments = ::from_string(&pcd_commitment)?; + let mut merkle_root_bin = [0u8; 32]; + let merkle_root_vec = Vec::from_hex(&merkle_root_hex)?; - let update_state: &ProcessState; - if let Some(state) = latest_states.into_iter().find(|state| state.pcd_commitment == new_state_commitments) - { - update_state = state; - } else { - return Err(ApiError::new("Can't find the state to update".to_owned())); + if merkle_root_vec.len() != 32 { + return Err(ApiError::new("merkle root must be 32B long".to_owned())); } + merkle_root_bin.copy_from_slice(&merkle_root_vec); + + let update_state = process.get_state_for_commitments_root(merkle_root_bin)?; + // We must have at least the key for the roles field, otherwise we don't know who to send the message to let clear_state = update_state.decrypt_pcd().as_object().unwrap().clone(); @@ -1322,7 +1320,7 @@ pub fn create_update_message( serde_json::to_string(&sender)?, serialize(&encrypted_pcd_merkle_root).to_lower_hex_string(), update_state.keys.clone(), - new_state_commitments + update_state.pcd_commitment.clone(), ); let mut ciphers = vec![]; @@ -1360,9 +1358,54 @@ pub fn create_update_message( } #[wasm_bindgen] -pub fn create_response_message(init_commitment: String, pcd_commitment: String, approval: bool) -> ApiResult { +pub fn validate_state(init_commitment: String, merkle_root_hex: String) -> ApiResult { + add_validation_token(init_commitment, merkle_root_hex, true) +} + +#[wasm_bindgen] +pub fn refuse_state(init_commitment: String, merkle_root_hex: String) -> ApiResult { + add_validation_token(init_commitment, merkle_root_hex, false) +} + +fn add_validation_token(init_commitment: String, merkle_root_hex: String, approval: bool) -> ApiResult { let mut merkle_root = [0u8; 32]; - let pcd_commitment_vec = Vec::from_hex(&pcd_commitment)?; + let pcd_commitment_vec = Vec::from_hex(&merkle_root_hex)?; + if pcd_commitment_vec.len() != 32 { + return Err(ApiError::new("pcd_commitment must be 32B long".to_owned())); + } + merkle_root.copy_from_slice(&pcd_commitment_vec); + + let mut processes = lock_processes()?; + + let outpoint = OutPoint::from_str(&init_commitment)?; + + let process = processes.get_mut(&outpoint) + .ok_or(ApiError::new("Unknown process".to_owned()))?; + + let update_state: &mut ProcessState = process.get_state_for_commitments_root(merkle_root)?; + + let message_hash = if approval { + AnkHash::ValidationYes(AnkValidationYesHash::from_byte_array(merkle_root)) + } else { + AnkHash::ValidationNo(AnkValidationNoHash::from_byte_array(merkle_root)) + }; + + let local_device = lock_local_device()?; + let sp_wallet = local_device.get_wallet(); + let proof = Proof::new(message_hash, sp_wallet.get_client().get_spend_key().try_into()?); + + update_state.validation_tokens.push(proof); + + Ok(ApiReturn { + updated_process: Some((init_commitment, process.clone())), + ..Default::default() + }) +} + +#[wasm_bindgen] +pub fn create_response_prd(init_commitment: String, merkle_root_hex: String) -> ApiResult { + let mut merkle_root = [0u8; 32]; + let pcd_commitment_vec = Vec::from_hex(&merkle_root_hex)?; if pcd_commitment_vec.len() != 32 { return Err(ApiError::new("pcd_commitment must be 32B long".to_owned())); } @@ -1383,7 +1426,6 @@ pub fn create_response_message(init_commitment: String, pcd_commitment: String, let roles = Value::Object(clear_state).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(); @@ -1396,7 +1438,6 @@ pub fn create_response_message(init_commitment: String, pcd_commitment: String, .flat_map(|rule| rule.fields.clone()) .collect(); for member in role.members { - debug!("member: {:?}", member); // Check that we have a shared_secret with all members if let Some(no_secret_address) = member.get_addresses().iter() .find(|a| shared_secrets.get_secret_for_address(a.as_str().try_into().unwrap()).is_none()) @@ -1414,20 +1455,17 @@ pub fn create_response_message(init_commitment: String, pcd_commitment: String, } } + let our_key = SilentPaymentAddress::try_from(local_address.as_str())?.get_spend_key(); + let proof = update_state.validation_tokens.iter().find(|t| t.get_key() == our_key) + .ok_or(ApiError::new("We haven't added our validation token yet".to_owned()))?; + let sender: Member = local_device .to_member(); - let message_hash = if approval { - AnkHash::ValidationYes(AnkValidationYesHash::from_byte_array(merkle_root)) - } else { - AnkHash::ValidationNo(AnkValidationNoHash::from_byte_array(merkle_root)) - }; - let proof = Proof::new(message_hash, sp_wallet.get_client().get_spend_key().try_into()?); - let response_prd = Prd::new_response( outpoint, serde_json::to_string(&sender)?, - vec![proof], + vec![*proof], update_state.pcd_commitment.clone(), ); let prd_msg = response_prd.to_network_msg(sp_wallet)?; @@ -1453,10 +1491,7 @@ pub fn create_response_message(init_commitment: String, pcd_commitment: String, return Err(ApiError::new("Empty ciphers list".to_owned())); } - update_state.validation_tokens.push(proof); - Ok(ApiReturn { - updated_process: Some((outpoint.to_string(), process.clone())), ciphers_to_send: ciphers, ..Default::default() }) diff --git a/tests/pairing.rs b/tests/pairing.rs index 0ab0eb3..8e446ae 100644 --- a/tests/pairing.rs +++ b/tests/pairing.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::str::FromStr; use sdk_client::api::{ - create_device_from_sp_wallet, create_new_process, create_response_message, create_update_message, dump_device, get_address, get_update_proposals, pair_device, parse_cipher, reset_device, restore_device, set_process_cache, set_shared_secrets, setup + create_device_from_sp_wallet, create_new_process, create_response_prd, create_update_message, dump_device, get_address, get_update_proposals, pair_device, parse_cipher, reset_device, restore_device, set_process_cache, set_shared_secrets, setup, validate_state }; use sdk_common::crypto::AnkSharedSecretHash; use sdk_common::log::debug; @@ -185,25 +185,17 @@ fn test_pairing() { // We send the commit_msg to the relay we got the address from // now we create prd update for this new process - let create_update_return = create_update_message(outpoint, commit_msg.pcd_commitment.to_string()).unwrap(); + debug!("Alice sends an update prd to Bob"); + let root = ::create_merkle_tree(&commit_msg.pcd_commitment).unwrap().root().unwrap(); + let create_update_return = create_update_message(outpoint, root.to_lower_hex_string()).unwrap(); let (root_outpoint, alice_init_process) = create_update_return.updated_process.unwrap(); alice_process_cache.insert(root_outpoint.clone(), alice_init_process); debug!("Alice pairs her device"); - // we can update our local device now, first with an empty txid pair_device(root_outpoint, vec![helper_get_bob_address()]).unwrap(); - debug!("Alice sends an update prd to Bob"); - let alice_pairing_return = - create_update_message(alice_process_cache.keys().next().unwrap().to_owned(), commit_msg.pcd_commitment.to_string()).unwrap(); - - // debug!("{:#?}", alice_pairing_return); - - let (root_outpoint, alice_init_process) = alice_pairing_return.updated_process.unwrap(); - alice_process_cache.insert(root_outpoint.clone(), alice_init_process.clone()); - - let alice_to_bob_cipher = &alice_pairing_return.ciphers_to_send[0]; + let alice_to_bob_cipher = &create_update_return.ciphers_to_send[0]; // this is only for testing, as we're playing both parts let alice_device = dump_device().unwrap(); @@ -254,11 +246,13 @@ fn test_pairing() { let root = ::create_merkle_tree(&relevant_state.pcd_commitment).unwrap().root().unwrap(); // Alice can also sign her response and send it to Bob - let alice_response = create_response_message(commit_msg.init_tx, root.to_lower_hex_string(), true).unwrap(); + let validate_state_return = validate_state(commit_msg.init_tx, root.to_lower_hex_string()).unwrap(); - let (outpoint, updated_process) = alice_response.updated_process.unwrap(); + let (outpoint, validated_process) = validate_state_return.updated_process.unwrap(); - alice_process_cache.insert(outpoint, updated_process); + alice_process_cache.insert(outpoint.clone(), validated_process); + + let alice_response = create_response_prd(outpoint, root.to_lower_hex_string()).unwrap(); // ======================= Bob reset_device().unwrap(); @@ -318,16 +312,16 @@ fn test_pairing() { // If user is ok, we can add our own validation token // Get the whole commitment from the process - let bob_response = create_response_message(root_outpoint, pcd_commitment_root.to_string(), true).unwrap(); + let bob_validated_process = validate_state(root_outpoint.clone(), pcd_commitment_root.to_string()).unwrap(); + + let (_, validated_process) = bob_validated_process.updated_process.unwrap(); + + bob_process_cache.insert(root_outpoint.clone(), validated_process); + + let bob_response = create_response_prd(root_outpoint.clone(), pcd_commitment_root.to_string()).unwrap(); - let (root_outpoint, updated_process) = bob_response.updated_process.unwrap(); let ciphers = bob_response.ciphers_to_send; // We would send it to Alice to let her know we agree - bob_process_cache.insert( - root_outpoint.clone(), - updated_process, - ); - debug!("Bob pairs device with Alice"); pair_device(root_outpoint, proposal_members).unwrap();