Merge branch 'get_update_proposals' into dev

This commit is contained in:
Sosthene 2024-12-30 10:41:20 +01:00
commit 4c49cf1596
4 changed files with 1165 additions and 925 deletions

1587
src/api.rs

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,14 @@
use std::collections::HashMap;
use std::str::FromStr;
use sdk_client::api::{
add_validation_token_to_prd, create_commit_message, create_connect_transaction, create_device_from_sp_wallet, create_update_message, dump_device, dump_process_cache, get_address, get_outputs, get_update_proposals, pair_device, parse_cipher, reset_device, reset_shared_secrets, response_prd, restore_device, set_process_cache, set_shared_secrets, setup, ApiReturn
create_connect_transaction, create_device_from_sp_wallet, dump_device, get_address, get_outputs, parse_cipher, reset_device, reset_shared_secrets, restore_device, set_shared_secrets, setup, ApiReturn
};
use sdk_common::log::{debug, info};
use sdk_common::pcd::{Member, RoleDefinition};
use sdk_common::log::debug;
use sdk_common::pcd::Member;
use sdk_common::secrets::SecretsStore;
use sdk_common::sp_client::bitcoin::consensus::deserialize;
use sdk_common::sp_client::bitcoin::hex::FromHex;
use sdk_common::sp_client::bitcoin::{OutPoint, Transaction};
use sdk_common::sp_client::bitcoin::OutPoint;
use sdk_common::sp_client::spclient::OwnedOutput;
use sdk_common::sp_client::silentpayments::utils::SilentPaymentAddress;
use serde_json::{json, Value};
use tsify::JsValueSerdeExt;
use wasm_bindgen_test::*;
@ -24,10 +20,50 @@ use utils::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
/// Tests the connection process between two devices, Alice and Bob, by executing a secure
/// transaction to establish a shared secret for encrypted communication.
///
/// The basics are that one device will initiate the process by sending a transaction that pays another device.
/// The recipient of the transaction as soon as it finds it, can extract a shared secret and send an encrypted
/// message back. Upon receiving this message, the initiator answers with a similar message similarly encrypted.
/// Upon receiving this message, the recipient can be assured that the communication is safe, and start using
/// the secret to communicate.
///
/// The security of the shared secret rest on the soundness of the silent payment protocol for Bitcoin.
/// In its encrypted response, the initiator adds a signature that is proof that it indeed controls the
/// private key for the silent payment address it announced, so recipient knows there's no mitm or impostor.
///
/// # Detailed Process
///
/// ## Alice sends a transaction that pays Bob:
/// - Alice initializes her device from an `sp_wallet` object and sets it as the local device.
/// - She retrieves her own address and obtains Bobs address.
/// - Alice creates a new member using Bobs device address (this is mainly for testing purpose,
/// because `create_connection_transaction` would take members as argument).
/// - She generates a connection transaction (`connect_tx`) targeting Bob's device.
/// - Alice processes her own transaction and stores the derived shared secrets in `alice_secrets_store`,
/// associating the shared secret with Bob's addresses.
///
/// ## Bob parses the transaction:
/// - Bob initializes his device from his own `sp_wallet`.
/// - He parses Alices connection transaction to retrieve the shared secret Alice created for him.
/// - Bob saves these derived shared secrets in `bob_secrets_store` but can't index it with Alice's address yet.
///
/// ## Prd Connect exchange
/// - Bob then responds by sending a prd connect back to Alice encrypted with the shared secret.
/// This prd is very simple and basically contains the following:
/// * All Bob's devices addresses
/// * a commitment to the shared secret
/// * a proof signed with Bob's device spend key
/// - Alice receives and decrypts the message from Bob.
/// - She replies to Bob by encrypting another prd connect which is basically the same, but keeping Bob's proof and adding her own.
/// - **Bobs Confirmation**: Bob receives Alices confirmation message, decrypts it, and updates his secret in `bob_secrets_store`.
///
/// ## Verification:
/// - Finally, the function asserts that Alice and Bob now share the same secrets, confirming successful
/// connection and mutual authentication between the devices.
fn test_connect() {
setup();
// let mut alice_process_cache = HashMap::new();
// let mut bob_process_cache = HashMap::new();
let mut alice_secrets_store = SecretsStore::new();
let mut bob_secrets_store = SecretsStore::new();
@ -70,7 +106,21 @@ fn test_connect() {
let alice_connect_transaction = connect_tx_msg.transaction;
let alice_device = dump_device().unwrap();
alice_secrets_store = alice_connect_return.secrets;
// Below is how to update our secrets store when secrets is Some
let secrets_update = alice_connect_return.secrets.unwrap();
let unconfirmed_secrets = secrets_update.get_all_unconfirmed_secrets();
if !unconfirmed_secrets.is_empty() {
for secret in unconfirmed_secrets {
alice_secrets_store.add_unconfirmed_secret(secret);
}
}
let updated_confirmed_secrets = secrets_update.get_all_confirmed_secrets();
if !updated_confirmed_secrets.is_empty() {
for (address, secret) in updated_confirmed_secrets {
alice_secrets_store.confirm_secret_for_address(secret, address);
}
}
// ======================= Bob
reset_device().unwrap();
@ -83,7 +133,19 @@ fn test_connect() {
let bob_to_alice_cipher = &bob_parsed_transaction_return.ciphers_to_send[0];
let bob_device = dump_device().unwrap();
bob_secrets_store = bob_parsed_transaction_return.secrets;
let updated_secrets = bob_parsed_transaction_return.secrets.unwrap();
let updated_unconfirmed_secrets = updated_secrets.get_all_unconfirmed_secrets();
if !updated_unconfirmed_secrets.is_empty() {
for secret in updated_unconfirmed_secrets {
bob_secrets_store.add_unconfirmed_secret(secret);
}
}
let updated_confirmed_secrets = updated_secrets.get_all_confirmed_secrets();
if !updated_confirmed_secrets.is_empty() {
for (address, secret) in updated_confirmed_secrets {
bob_secrets_store.confirm_secret_for_address(secret, address);
}
}
// ======================= Alice
reset_device().unwrap();
@ -96,7 +158,19 @@ fn test_connect() {
// debug!("alice_parsed_confirm: {:#?}", alice_parsed_confirm);
let alice_to_bob_cipher = alice_parsed_connect.ciphers_to_send.get(0).unwrap();
alice_secrets_store = alice_parsed_connect.secrets;
let secrets_update = alice_parsed_connect.secrets.unwrap();
let unconfirmed_secrets = secrets_update.get_all_unconfirmed_secrets();
if !unconfirmed_secrets.is_empty() {
for secret in unconfirmed_secrets {
alice_secrets_store.add_unconfirmed_secret(secret);
}
}
let updated_confirmed_secrets = secrets_update.get_all_confirmed_secrets();
if !updated_confirmed_secrets.is_empty() {
for (address, secret) in updated_confirmed_secrets {
alice_secrets_store.confirm_secret_for_address(secret, address);
}
}
// ======================= Bob
reset_device().unwrap();
@ -106,7 +180,19 @@ fn test_connect() {
debug!("Bob parses alice prd connect");
let bob_parsed_connect = parse_cipher(alice_to_bob_cipher.clone()).unwrap();
bob_secrets_store = bob_parsed_connect.secrets;
let updated_secrets = bob_parsed_connect.secrets.unwrap();
let updated_unconfirmed_secrets = updated_secrets.get_all_unconfirmed_secrets();
if !updated_unconfirmed_secrets.is_empty() {
for secret in updated_unconfirmed_secrets {
bob_secrets_store.add_unconfirmed_secret(secret);
}
}
let updated_confirmed_secrets = updated_secrets.get_all_confirmed_secrets();
if !updated_confirmed_secrets.is_empty() {
for (address, secret) in updated_confirmed_secrets {
bob_secrets_store.confirm_secret_for_address(secret, address);
}
}
// Assert that Alice and Bob now has the same secret
assert!(alice_secrets_store.get_secret_for_address(bob_address.try_into().unwrap()) == bob_secrets_store.get_secret_for_address(alice_address.try_into().unwrap()));

View File

@ -2,17 +2,16 @@ use std::collections::HashMap;
use std::str::FromStr;
use sdk_client::api::{
add_validation_token_to_prd, create_commit_message, create_device_from_sp_wallet, create_update_transaction, dump_device, dump_process_cache, get_address, get_outputs, get_update_proposals, pair_device, parse_cipher, reset_device, response_prd, restore_device, set_process_cache, setup, ApiReturn
create_device_from_sp_wallet, create_new_process, create_response_prd, create_update_message, dump_device, get_address, pair_device, parse_cipher, reset_device, restore_device, set_process_cache, set_shared_secrets, setup, update_process_state, validate_state
};
use sdk_common::log::{debug, info};
use sdk_common::pcd::{Member, RoleDefinition};
use sdk_common::sp_client::bitcoin::consensus::deserialize;
use sdk_common::crypto::AnkSharedSecretHash;
use sdk_common::log::debug;
use sdk_common::pcd::{Member, Pcd, RoleDefinition};
use sdk_common::secrets::SecretsStore;
use sdk_common::sp_client::bitcoin::hex::FromHex;
use sdk_common::sp_client::bitcoin::{OutPoint, Transaction};
use sdk_common::sp_client::spclient::OwnedOutput;
use serde_json::{json, Value};
use sdk_common::sp_client::bitcoin::OutPoint;
use serde_json::{json, Map, Value};
use tsify::JsValueSerdeExt;
use wasm_bindgen_test::*;
mod utils;
@ -21,11 +20,85 @@ use utils::*;
wasm_bindgen_test_configure!(run_in_browser);
/// # Pairing Process Documentation between Alice and Bob
///
/// This test describes the secure pairing process between two devices, Alice and Bob.
///
/// ## What's pairing?
/// Pairing is a process, and abide by the same rules than any other process. The goal of pairing
/// is to define an identity on the network as a set of devices (defined by their sp_address).
/// Being a process it is public and can be audited by anyone, and be used as one's proof of identity.
/// It also contains a session keypair that is updated as necessary. Since all devices are needed to
/// update the key in the process it can then be used to sign a proof that someone was indeed in control
/// of all the devices for some amount of time in a MFA setup.
/// It contains the following mandatory fields:
/// * `roles`: multiple devices represented as sp adresses linked together in the same member. It is recommended
/// to have one `owner` role with one member which is the actual identity and whose signatures are all
/// needed to modify anything in the process.
/// * `session_privkey`: a private key visible by all devices of the member defined in the process, but
/// not by other members. It *must* be changed at every update of the process. This key will be used
/// to sign documents and validate actions for other processes. It's valid as soon as the commitment
/// transaction for the process udpate is seen and it stays valid for _n_ blocks after the update being mined.
/// * `session_pubkey`: the x-only public key derived from the session private key. It's visible by everyone and
/// used for validation by any third party. Obviously it changes with the private key at any update.
/// * `parity`: the parity of the session_pubkey. We could use 33 bytes compressed public key format
/// but using 32 bytes publick key + parity allows for more standard serialization.
///
/// ## Detailed protocol
/// (Here Alice and Bob are used as a convention, but keep in mind they're not 2 different users, but
/// 2 devices belonging to the same user)
/// ## Step 0 - Preliminary step
/// 1. **Establishing a Shared Secret**: A shared secret is established to secure
/// communication between Alice and Bob (see `connect.rs`).
/// ## Step 1 - Pairing Preparation by Alice
/// 1. **Pairing Status Check**: Alice verifies that it's not already paired.
/// 2. **Adding Bob's Address**: Alice adds Bobs address to her own, setting the base for creating
/// a new `Member` object.
/// 3. **Creation of the pairing process**: Alice initializes pairing by creating a prd update that contains
/// both its address and Bob's, and send it to Bob.
///
/// ## Step 2 - Receiving and Confirming the `prd` by Bob
/// 1. **Receiving and Verifying**: Bob receives and decrypts the update `prd` message sent by Alice.
/// 2. **Updating Process State**: Bob identifies the new process and store it, but it doesn't have access
/// to the actual data for now.
/// 3. **Creating and Sending `Prd Confirm`**: Bob creates a confirmation `prd`, which he then
/// sends to Alice to get the pcd containing the state for this new process.
///
/// ## Step 3 - Alice gets confirmation and answers with a pcd
/// 1. **Receiving and Verifying**: Alice receives the `Prd Confirm` sent by Bob.
/// 2. **Sending PCD**: Alice having confirmation that Bob got the update proposal,
/// it now sends the actual data in a pcd.
/// 3. **User confirmation**: At this step we must get the approval of the user. If user confirms
/// the pairing we create a prd response with a valid signature from Alice spend key and send
/// it to Bob.
///
/// ## Step 4 - Finalizing Pairing by Bob
/// 1. **Receiving and Verifying `pcd`**: Bob received the `pcd` and only now can tell what's the
/// process was about.
/// 2. **Validating Pairing State**: Bob retrieves the latest process state and the state change
/// request, in this case, the pairing. User is prompted for validation, and if confirmed a prd response
/// is created and sent(see the **User confirmation** step for Alice).
///
/// ## Commiting the process state
/// 1. **Creating the `commit_msg`**: The first device that got both validations creates the commit_msg that
/// contains a transaction paying a relay to generate the first outpoint to commit the state of the process,
/// the hash of the encrypted state of the process (relay must have access to roles though, either it is clear
/// all along or it was provided with the encryption keys) and the proofs that all devices validated this state.
/// 2. **Actual commitment**: As soon as the relay validated the proofs it spends the outpoint and puts the hash of
/// the whole prd response (including pcd hash and all the proofs) in an OP_RETURN output. The process is now
/// public and can be used to prove identity for other processes.
#[wasm_bindgen_test]
fn test_pairing() {
const RELAY_ADDRESS: &str = "tsp1qqvfm6wvd55r68ltysdhmagg7qavxrzlmm9a7tujsp8qqy6x2vr0muqajt5p2jdxfw450wyeygevypxte29sxlxzgprmh2gwnutnt09slrcqqy5h4";
setup();
let mut alice_process_cache = HashMap::new();
let mut bob_process_cache = HashMap::new();
let mut alice_secrets_store = SecretsStore::new();
let mut bob_secrets_store = SecretsStore::new();
let mut alice_diff_cache = Vec::new();
let mut bob_diff_cache = Vec::new();
debug!("==============================================\nStarting test_pairing\n==============================================");
@ -35,9 +108,18 @@ fn test_pairing() {
// we get our own address
let alice_address = get_address().unwrap();
debug!("alice address: {}", alice_address);
// we scan the qr code or get the address by any other means
let bob_address = helper_get_bob_address();
debug!("bob_address: {}", bob_address);
// we add some shared_secret in both secrets_store
let shared_secret = AnkSharedSecretHash::from_str("c3f1a64e15d2e8d50f852c20b7f0b47cbe002d9ef80bc79582d09d6f38612d45").unwrap();
alice_secrets_store.confirm_secret_for_address(shared_secret, bob_address.as_str().try_into().unwrap());
bob_secrets_store.confirm_secret_for_address(shared_secret, alice_address.as_str().try_into().unwrap());
set_shared_secrets(serde_json::to_string(&alice_secrets_store).unwrap()).unwrap();
// Alice creates the new member with Bob address
let new_member = Member::new(vec![
@ -46,10 +128,14 @@ fn test_pairing() {
])
.unwrap();
let initial_session_privkey = [0u8; 32];
let initial_session_privkey = [0u8; 32]; // In reality we would generate a random new key here
let initial_session_pubkey = [0u8; 32];
let pairing_init_state = json!({
"html": "",
"js": "",
"style": "",
"zones": [],
"description": "AliceBob",
"roles": {
"owner": {
@ -70,7 +156,8 @@ fn test_pairing() {
],
"min_sig_member": 1.0
}
]
],
"storages": []
}
},
"session_privkey": initial_session_privkey,
@ -78,223 +165,155 @@ fn test_pairing() {
"key_parity": true, // This allows us to use a 32 bytes array in serialization
});
debug!("Alice creates the pairing process");
let create_process_return = create_new_process(pairing_init_state.to_string(), None, RELAY_ADDRESS.to_owned(), 1).unwrap();
let commit_msg = create_process_return.commit_to_send.unwrap();
let secrets_update = create_process_return.secrets.unwrap();
let unconfirmed_secrets = secrets_update.get_all_unconfirmed_secrets();
if !unconfirmed_secrets.is_empty() {
for secret in unconfirmed_secrets {
alice_secrets_store.add_unconfirmed_secret(secret);
}
}
let updated_confirmed_secrets = secrets_update.get_all_confirmed_secrets();
if !updated_confirmed_secrets.is_empty() {
for (address, secret) in updated_confirmed_secrets {
alice_secrets_store.confirm_secret_for_address(secret, address);
}
}
let updated_process = create_process_return.updated_process.unwrap();
alice_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
// Alice keeps track of the change she needs to validate
let create_process_diffs = updated_process.new_diffs;
let new_state_id = &create_process_diffs.get(0).unwrap().new_state_merkle_root;
alice_diff_cache.extend(create_process_diffs.iter());
// We send the commit_msg to the relay we got the address from
// now we create prd update for this new process
debug!("Alice creates an update prd to Bob");
let create_update_return = create_update_message(updated_process.commitment_tx.to_string(), new_state_id.clone()).unwrap();
let updated_process = create_update_return.updated_process.unwrap();
alice_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
debug!("Alice pairs her device");
// we can update our local device now, first with an empty txid
pair_device(OutPoint::null().to_string(), vec![helper_get_bob_address()]).unwrap();
pair_device(updated_process.commitment_tx.to_string(), vec![helper_get_bob_address()]).unwrap();
debug!("Alice sends a transaction commiting to an update prd to Bob");
let alice_pairing_return =
create_update_transaction(None, pairing_init_state.to_string(), 1).unwrap();
let (root_outpoint, alice_init_process) = alice_pairing_return.updated_process.unwrap();
alice_process_cache.insert(root_outpoint.clone(), alice_init_process.clone());
let pairing_tx_msg = alice_pairing_return.new_tx_to_send.unwrap();
// This is only for testing, the relay takes care of that in prod
let get_outputs_result = get_outputs().unwrap();
let alice_outputs: HashMap<OutPoint, OwnedOutput> = get_outputs_result.into_serde().unwrap();
let alice_pairing_tweak_data =
helper_get_tweak_data(&pairing_tx_msg.transaction, alice_outputs);
// End of the test only part
// Alice parses her own transaction
helper_parse_transaction(&pairing_tx_msg.transaction, &alice_pairing_tweak_data);
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();
let alice_processes = dump_process_cache().unwrap();
// ======================= Bob
reset_device().unwrap();
create_device_from_sp_wallet(BOB_LOGIN_WALLET.to_owned()).unwrap();
set_shared_secrets(serde_json::to_string(&bob_secrets_store).unwrap()).unwrap();
// Bob receives Alice pairing transaction
debug!("Bob parses Alice pairing transaction");
helper_parse_transaction(&pairing_tx_msg.transaction, &alice_pairing_tweak_data);
debug!("Bob receives the update prd");
let bob_parsed_return = parse_cipher(alice_to_bob_cipher.to_owned()).unwrap();
debug!("Bob receives the prd");
let mut bob_retrieved_prd: ApiReturn = ApiReturn::default();
for cipher in alice_pairing_return.ciphers_to_send.iter() {
// debug!("Parsing cipher: {:#?}", cipher);
match parse_cipher(cipher.clone()) {
Ok(res) => bob_retrieved_prd = res,
Err(e) => {
debug!("Error parsing cipher: {:#?}", e);
continue;
}
}
}
let updated_process = bob_parsed_return.updated_process.unwrap();
assert!(bob_retrieved_prd.ciphers_to_send.len() == 1);
assert!(bob_retrieved_prd.updated_process.is_some());
let parsed_prd_diffs = updated_process.new_diffs;
debug!("Bob retrieved prd: {:#?}", bob_retrieved_prd);
// debug!("Bob creates process {} with state {}", updated_process.commitment_tx, new_state_id);
bob_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
let (root_commitment, relevant_process) = bob_retrieved_prd.updated_process.unwrap();
// Bob also keeps track of changes
bob_process_cache.insert(root_commitment.clone(), relevant_process);
bob_diff_cache.extend(parsed_prd_diffs.into_iter());
let prd_confirm_cipher = bob_retrieved_prd.ciphers_to_send.iter().next().unwrap();
debug!("Bob can now fetch the data from storage using the hashes");
// We have to cheat here and let Bob access Alice process cache
let process = alice_process_cache.get(&updated_process.commitment_tx).unwrap();
debug!("Bob sends a Confirm Prd to Alice");
let state = process.get_state_for_commitments_root(&new_state_id).unwrap();
let hash2values: Map<String, Value> = bob_diff_cache.iter()
.filter(|diff| diff.new_state_merkle_root == *new_state_id)
.map(|diff| {
let encrypted_value = state.encrypted_pcd.as_object().unwrap().get(&diff.field).unwrap();
(diff.value_commitment.clone(), encrypted_value.clone())
})
.collect();
let update_process_res = update_process_state(updated_process.commitment_tx.to_string(), new_state_id.clone(), serde_json::to_string(&Value::Object(hash2values)).unwrap()).unwrap();
let updated_process = update_process_res.updated_process.unwrap();
let parsed_prd_diffs = updated_process.new_diffs;
bob_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
bob_diff_cache.extend(parsed_prd_diffs);
// We can also prune the old diffs from the cache
bob_diff_cache.retain(|diff| diff.new_value != Value::Null);
// this is only for testing, as we're playing both parts
let bob_device = dump_device().unwrap();
let bob_processes = dump_process_cache().unwrap();
// ======================= Alice
reset_device().unwrap();
restore_device(alice_device).unwrap();
set_process_cache(alice_processes).unwrap();
set_process_cache(serde_json::to_string(&alice_process_cache).unwrap()).unwrap();
set_shared_secrets(serde_json::to_string(&alice_secrets_store).unwrap()).unwrap();
debug!("Alice receives the Confirm Prd");
let alice_parsed_confirm = parse_cipher(prd_confirm_cipher.clone()).unwrap();
let commitment_outpoint = alice_process_cache.keys().next().unwrap();
debug!(
"Alice parsed Bob's Confirm Prd: {:#?}",
alice_parsed_confirm
);
debug!("Alice can validate the new state of the process");
let relevant_process = alice_process_cache.get(&commitment_outpoint).unwrap();
// Alice simply shoots back the return value in the ws
let bob_received_pcd = alice_parsed_confirm.ciphers_to_send[0].clone();
for diff in alice_diff_cache {
debug!("User validate diff: {:#?}", diff);
}
// Now that we're sure that bob got the prd udpate we also produce the prd response and shoot it
let alice_prd_update_commitment = alice_init_process
.get_impending_requests()
.get(0)
.unwrap()
.create_commitment();
let (_, alice_validated_prd) = add_validation_token_to_prd(
root_outpoint.clone(),
alice_prd_update_commitment.to_string(),
true,
)
.unwrap()
.updated_process
.unwrap();
// Alice can also sign her response and send it to Bob
let validate_state_return = validate_state(commitment_outpoint.to_string(), new_state_id.clone()).unwrap();
alice_process_cache.insert(root_outpoint.clone(), alice_validated_prd);
let updated_process = validate_state_return.updated_process.unwrap();
let alice_prd_response =
response_prd(root_outpoint, alice_prd_update_commitment.to_string(), true).unwrap();
alice_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
let bob_received_response = alice_prd_response.ciphers_to_send.get(0).unwrap().clone();
let alice_response = create_response_prd(updated_process.commitment_tx.to_string(), new_state_id.clone()).unwrap();
// ======================= Bob
reset_device().unwrap();
restore_device(bob_device).unwrap();
set_process_cache(bob_processes).unwrap();
set_process_cache(serde_json::to_string(&bob_process_cache).unwrap()).unwrap();
set_shared_secrets(serde_json::to_string(&bob_secrets_store).unwrap()).unwrap();
debug!("Bob parses Alice's pcd");
let bob_parsed_pcd_return = parse_cipher(bob_received_pcd).unwrap();
debug!("bob_parsed_pcd: {:#?}", bob_parsed_pcd_return);
// Here we would update our database
bob_process_cache.insert(
root_commitment.clone(),
bob_parsed_pcd_return.updated_process.unwrap().1,
);
// We now need Alice prd response, and update our process with it
debug!("Bob also parses alice prd response");
let bob_parsed_response = parse_cipher(bob_received_response).unwrap();
debug!("bob_parsed_response: {:#?}", bob_parsed_response);
bob_process_cache.insert(
root_commitment.clone(),
bob_parsed_response.updated_process.unwrap().1,
);
debug!("{:#?}", bob_process_cache.get(&root_commitment).unwrap());
// At this point, user must validate the pairing proposal received from Alice
// We decrypt the content of the pcd so that we can display to user what matters
let alice_proposal = get_update_proposals(root_commitment.clone()).unwrap();
debug!("Alice proposal: {:#?}", alice_proposal);
let proposal = Value::from_str(&alice_proposal.get(0).unwrap()).unwrap();
debug!("proposal: {:#?}", proposal);
// get the roles from the proposal
let roles = proposal
.get("roles")
.and_then(|v| Value::from_str(v.as_str().unwrap()).ok())
.unwrap()
.as_object()
.unwrap()
.iter()
.map(|(role_name, role_value)| {
let role_def: RoleDefinition = serde_json::from_value(role_value.clone())?;
Ok((role_name.clone(), role_def))
})
.collect::<Result<HashMap<String, RoleDefinition>, anyhow::Error>>();
let roles = roles.unwrap();
// we check that the proposal contains only one member
assert!(roles.len() == 1);
assert!(roles["owner"].members.len() == 1);
// we get all the addresses of the members of the proposal
let proposal_members = roles
.iter()
.flat_map(|(_, members)| members.members.iter().flat_map(|m| m.get_addresses()))
.collect::<Vec<String>>();
// we can automatically check that a pairing member contains local device address + the one that sent the proposal
assert!(proposal_members.contains(&alice_address));
assert!(proposal_members.contains(&bob_address));
assert!(proposal_members.len() == 2); // no free riders
// We remove the local address, but maybe that's the responsibility of the Member type
let proposal_members = proposal_members
.into_iter()
.filter(|m| m != &bob_address)
.collect::<Vec<String>>();
debug!("proposal_members: {:?}", proposal_members);
// we can now show all the addresses to the user on device to prompt confirmation
info!("Pop-up: User confirmation");
for diff in &bob_diff_cache {
if diff.need_validation {
debug!("Pop-up: User confirmation");
debug!("{:#?}", diff);
}
}
// If user is ok, we can add our own validation token
let prd_to_respond = bob_process_cache
.get(&root_commitment)
.unwrap()
.get_impending_requests()
.get(0)
.unwrap()
.to_owned();
let bob_added_validation =
add_validation_token_to_prd(root_commitment.clone(), prd_to_respond.create_commitment().to_string(), true).unwrap();
// Get the whole commitment from the process
let bob_validated_process = validate_state(updated_process.commitment_tx.to_string(), new_state_id.clone()).unwrap();
bob_process_cache.insert(
root_commitment.clone(),
bob_added_validation.updated_process.unwrap().1,
);
let updated_process = bob_validated_process.updated_process.unwrap();
// We create the commit msg for the relay that includes Alice and Bob's proofs
let commit_msg = create_commit_message(root_commitment.clone(), "tsp1qqvfm6wvd55r68ltysdhmagg7qavxrzlmm9a7tujsp8qqy6x2vr0muqajt5p2jdxfw450wyeygevypxte29sxlxzgprmh2gwnutnt09slrcqqy5h4".to_owned(), 1).unwrap().commit_to_send.unwrap();
bob_process_cache.insert(updated_process.commitment_tx, updated_process.current_process);
let tx: Transaction = deserialize(&Vec::from_hex(&commit_msg.init_tx).unwrap()).unwrap();
let bob_response = create_response_prd(updated_process.commitment_tx.to_string(), new_state_id.clone()).unwrap();
// We send the commit_msg to the relay we got the address from
// We also send
// We can just take the txid of the transaction we created for the commitment
let commitment_outpoint = OutPoint::new(tx.txid(), 0);
let ciphers = bob_response.ciphers_to_send; // We would send it to Alice to let her know we agree
debug!("Bob pairs device with Alice");
pair_device(commitment_outpoint.to_string(), proposal_members).unwrap();
let roles: HashMap<String, RoleDefinition> = serde_json::from_value(bob_diff_cache.iter().find(|diff| diff.field == "roles").unwrap().new_value.clone()).unwrap();
let owner = roles.get("owner").unwrap();
let members_to_pair: Vec<String> = owner.members.iter().flat_map(|m| m.get_addresses()).collect();
pair_device(updated_process.commitment_tx.to_string(), members_to_pair).unwrap();
// To make the pairing effective, alice and bob must now creates a new transaction where they both control one output
// login();
// We can also check alice response
let parsed_alice_response = parse_cipher(alice_response.ciphers_to_send[0].clone()).unwrap();
}

View File

@ -69,7 +69,7 @@ pub fn helper_parse_transaction(transaction: &str, tweak_data: &str) -> ApiRetur
))
.unwrap();
// debug!("new_tx_msg: {:?}", new_tx_msg);
let result = parse_new_tx(new_tx_msg, 0, 1);
let result = parse_new_tx(new_tx_msg, 0);
match result {
Ok(m) => m,
Err(e) => panic!("Unexpected error: {}", e.message),