Update to latest common

This commit is contained in:
NicolasCantu 2025-03-12 13:02:26 +01:00
parent a99f41f50e
commit f25c6f27de

View File

@ -20,7 +20,7 @@ use anyhow::Error as AnyhowError;
use anyhow::Result as AnyhowResult;
use sdk_common::aes_gcm::aead::{Aead, Payload};
use sdk_common::crypto::{
decrypt_with_key, encrypt_with_key, AeadCore, Aes256Gcm, AnkSharedSecretHash, KeyInit, AAD
decrypt_with_key, encrypt_with_key, generate_key, AeadCore, Aes256Gcm, AnkSharedSecretHash, KeyInit, AAD
};
use sdk_common::process::{check_tx_for_process_updates, lock_processes, Process, ProcessState};
use sdk_common::signature::{AnkHash, AnkMessageHash, AnkValidationNoHash, AnkValidationYesHash, Proof};
@ -63,7 +63,7 @@ use sdk_common::network::{
NewTxMessage,
};
use sdk_common::pcd::{
AnkPcdHash, AnkPcdTag, Member, Pcd, RoleDefinition, ValidationRule,
AnkPcdHash, AnkPcdTag, Member, Pcd, PcdCommitments, RoleDefinition, Roles, ValidationRule
};
use sdk_common::prd::{AnkPrdHash, Prd, PrdType};
use sdk_common::silentpayments::{create_transaction, map_outputs_to_sp_address};
@ -94,7 +94,7 @@ pub struct UserDiff {
pub state_id: String, // TODO add a merkle proof that the new_value belongs to that state
pub value_commitment: String,
pub field: String,
pub roles: BTreeMap<String, RoleDefinition>,
pub roles: Roles,
pub description: Option<String>,
pub notify_user: bool,
pub need_validation: bool,
@ -108,8 +108,8 @@ pub struct UpdatedProcess {
pub process_id: OutPoint,
pub current_process: Process,
pub diffs: Vec<UserDiff>, // All diffs should have the same state_id
pub encrypted_data: HashMap<String, String>, // hashes in pcd commitment to ciphers
pub validated_state: Option<String>, // when we add/receive validation proofs for a state
pub encrypted_data: BTreeMap<String, String>, // hashes in pcd commitment to ciphers
pub validated_state: Option<[u8; 32]>, // when we add/receive validation proofs for a state
}
#[derive(Debug, PartialEq, Tsify, Serialize, Deserialize, Default)]
@ -126,15 +126,6 @@ pub struct ApiReturn {
pub type ApiResult<T: FromWasmAbi> = Result<T, ApiError>;
#[derive(Debug, PartialEq, Tsify, Serialize, Deserialize, Default)]
#[tsify(into_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct NewKey {
pub private_key: String,
pub x_only_public_key: String,
pub key_parity: bool
}
const IS_TESTNET: bool = true;
const DEFAULT_AMOUNT: Amount = Amount::from_sat(1000);
@ -623,41 +614,41 @@ fn confirm_prd(prd: &Prd, shared_secret: &AnkSharedSecretHash) -> AnyhowResult<S
_ => (),
}
let outpoint = OutPoint::from_str(&prd.process_id)?;
let outpoint = prd.process_id;
let local_device = lock_local_device()?;
let sender = local_device.to_member();
let prd_confirm = Prd::new_confirm(outpoint, sender, prd.pcd_commitments.clone());
// debug!("Sending confirm prd: {:?}", prd_confirm);
let prd_msg = prd_confirm.to_network_msg(local_device.get_wallet())?;
Ok(encrypt_with_key(shared_secret.as_byte_array(), prd_msg.as_bytes())?.to_lower_hex_string())
}
fn create_diffs(process: &Process, new_state: &ProcessState) -> AnyhowResult<Vec<UserDiff>> {
let new_state_commitments = new_state.pcd_commitment.as_object().ok_or(AnyhowError::msg("new_state commitments is not an object"))?;
let new_state_commitments = &new_state.pcd_commitment;
let device = lock_local_device()?;
let our_id = device.to_member();
let fields_to_validate = new_state.get_fields_to_validate_for_member(&our_id)?;
let new_state_root = &new_state.state_id;
let new_state_id = &new_state.state_id;
let new_public_data = &new_state.public_data;
let process_id = process.get_process_id()?.to_string();
let mut diffs = vec![];
for (field, hash) in new_state_commitments {
let description = new_public_data.get(field).map(|d| d.to_string());
for (field, hash) in new_state_commitments.iter() {
let description_field = new_public_data.get(field);
let has_description = description_field.as_ref().is_some_and(|v| v.is_string());
let description = if has_description { description_field.unwrap().as_str().map(|s| s.to_owned()) } else { None };
let need_validation = fields_to_validate.contains(field);
diffs.push(UserDiff {
process_id: process_id.clone(),
state_id: new_state_root.to_owned(),
value_commitment: hash.as_str().unwrap().to_string(),
state_id: new_state_id.to_lower_hex_string(),
value_commitment: hash.to_lower_hex_string(),
field: field.to_owned(),
description,
notify_user: false,
@ -691,9 +682,8 @@ fn handle_prd_connect(prd: Prd, secret: AnkSharedSecretHash) -> AnyhowResult<Api
return Err(anyhow::Error::msg("Previous proof signs another message"));
}
// Now we can confirm the secret and link it to an address
let sender = serde_json::from_str::<Member>(&prd.sender)?;
let proof = prd.proof.unwrap();
let actual_sender = sender.get_address_for_key(&proof.get_key())
let actual_sender = prd.sender.get_address_for_key(&proof.get_key())
.ok_or(anyhow::Error::msg("Signer of the proof is not part of sender"))?;
shared_secrets.confirm_secret_for_address(secret, actual_sender.clone().try_into()?);
let mut secrets_return = SecretsStore::new();
@ -704,8 +694,7 @@ fn handle_prd_connect(prd: Prd, secret: AnkSharedSecretHash) -> AnyhowResult<Api
})
} else {
let proof = prd.proof.unwrap();
let sender = serde_json::from_str::<Member>(&prd.sender)?;
let actual_sender = sender.get_address_for_key(&proof.get_key())
let actual_sender = prd.sender.get_address_for_key(&proof.get_key())
.ok_or(anyhow::Error::msg("Signer of the proof is not part of sender"))?;
shared_secrets.confirm_secret_for_address(secret, actual_sender.clone().try_into()?);
@ -736,7 +725,7 @@ fn handle_prd(
return handle_prd_connect(prd, secret);
}
let outpoint = OutPoint::from_str(&prd.process_id)?;
let outpoint = prd.process_id;
let mut processes = lock_processes()?;
let relevant_process = match processes.entry(outpoint) {
@ -750,7 +739,7 @@ fn handle_prd(
match prd.prd_type {
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();
let update_merkle_root = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?;
if relevant_process.get_state_for_id(&update_merkle_root).is_ok() {
// We already know about that state
return Err(AnyhowError::msg("Received update for a state we already know"));
@ -786,7 +775,7 @@ fn handle_prd(
});
}
PrdType::Response => {
let update_state_id = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?.to_lower_hex_string();
let update_state_id = prd.pcd_commitments.create_merkle_tree()?.root().ok_or(AnyhowError::msg("Invalid merkle tree"))?;
let mut to_update = relevant_process.get_state_for_id_mut(&update_state_id)?;
let new_validations = prd.validation_tokens;
@ -819,19 +808,19 @@ fn handle_prd(
let updated_state = to_update.clone();
let validated_state = Some(to_update.state_id.clone());
let validated_state = Some(to_update.state_id);
let mut commit_msg = CommitMessage::new_update_commitment(
OutPoint::from_str(&prd.process_id)?,
prd.process_id,
updated_state.pcd_commitment,
updated_state.roles.clone().into_iter().collect(),
updated_state.public_data.clone()
updated_state.roles,
updated_state.public_data
);
commit_msg.set_validation_tokens(updated_state.validation_tokens);
// We must return an update of the process
let updated_process = UpdatedProcess {
process_id: OutPoint::from_str(&prd.process_id)?,
process_id: prd.process_id,
current_process: relevant_process.clone(),
validated_state,
..Default::default()
@ -846,7 +835,7 @@ fn handle_prd(
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)?;
let requester = prd.sender;
// diffs will trigger upload of the encrypted data on storage
let mut diffs = vec![];
@ -855,7 +844,7 @@ fn handle_prd(
let mut push_to_storage = vec![];
for state_id in states {
let state = match relevant_process.get_state_for_id(&state_id.to_lower_hex_string()) {
let state = match relevant_process.get_state_for_id(&state_id) {
Ok(state) => state,
Err(_) => {
debug!("Ignoring request for unknown state {}", state_id.to_lower_hex_string());
@ -869,7 +858,7 @@ fn handle_prd(
let mut relevant_fields: HashSet<String> = HashSet::new();
let shared_secrets = lock_shared_secrets()?;
for (name, role) in &state.roles {
for (name, role) in state.roles.iter() {
if !role.members.contains(&requester) {
// This role doesn't concern requester
continue;
@ -912,21 +901,21 @@ fn handle_prd(
ciphers.push(cipher.to_lower_hex_string());
}
let pcd_commitment: HashMap<String, String> = serde_json::from_value(state.pcd_commitment.clone())?;
for (field, hash) in pcd_commitment {
let pcd_commitment = &state.pcd_commitment;
for (field, hash) in pcd_commitment.iter() {
// We only need field that are visible by requester
if !relevant_fields.contains(&field) {
if !relevant_fields.contains(field.as_str()) {
continue;
}
let diff = UserDiff {
process_id: outpoint.to_string(),
state_id: state_id.to_lower_hex_string(),
value_commitment: hash.clone(),
field,
value_commitment: hash.to_lower_hex_string(),
field: field.to_owned(),
..Default::default()
};
diffs.push(diff);
push_to_storage.push(hash);
push_to_storage.push(hash.to_lower_hex_string());
}
}
@ -1092,15 +1081,15 @@ pub fn create_connect_transaction(addresses: Vec<String>, fee_rate: u32) -> ApiR
#[wasm_bindgen]
pub fn create_new_process(
init_state_str: String,
init_state: JsValue,
roles: JsValue,
public_data: JsValue,
relay_address: String,
fee_rate: u32,
) -> ApiResult<ApiReturn> {
let init_state: Value = <Value as Pcd>::new_from_string(&init_state_str)?;
let roles: BTreeMap<String, RoleDefinition> = serde_wasm_bindgen::from_value(roles)?;
let public_data: BTreeMap<String, String> = serde_wasm_bindgen::from_value(public_data)?;
let init_state: Pcd = serde_wasm_bindgen::from_value(init_state)?;
let roles: Roles = serde_wasm_bindgen::from_value(roles)?;
let public_data: Pcd = serde_wasm_bindgen::from_value(public_data)?;
// We create a transaction that spends to the relay address
let psbt = create_transaction_for_addresses(vec![relay_address.clone()], fee_rate)?;
@ -1122,7 +1111,7 @@ pub fn create_new_process(
let new_tx_msg = NewTxMessage::new(serialize(&transaction).to_lower_hex_string(), None);
let mut new_state = ProcessState::new(process_id, init_state.as_object().unwrap().clone(), &public_data, roles.clone())?;
let mut new_state = ProcessState::new(process_id, init_state.clone(), public_data.clone(), roles.clone())?;
let pcd_commitment = new_state.pcd_commitment.clone();
@ -1130,21 +1119,22 @@ pub fn create_new_process(
let diffs = create_diffs(&process, &new_state)?;
let all_fields: Vec<String> = init_state.as_object().unwrap().into_iter().map(|(field, _)| field.clone()).collect();
let mut fields2keys = Map::new();
let mut fields2cipher = Map::new();
init_state.encrypt_fields(&all_fields, &mut fields2keys, &mut fields2cipher);
let all_fields: Vec<String> = init_state.iter().map(|(field, _)| field.clone()).collect();
let mut fields2keys = BTreeMap::new();
let mut encrypted_data = BTreeMap::new();
let mut rng = thread_rng();
for (field, plain_value) in init_state.iter() {
let hash = pcd_commitment.get(field).ok_or(anyhow::Error::msg("Missing commitment"))?;
let key = generate_key(&mut rng);
let serialized = serde_json::to_string(plain_value)?;
let cipher = encrypt_with_key(&key, serialized.as_bytes())?;
fields2keys.insert(field.to_owned(), key);
encrypted_data.insert(hash.to_lower_hex_string(), cipher.to_lower_hex_string());
}
new_state.keys = fields2keys;
let encrypted_data: HashMap<String, String> = fields2cipher.into_iter()
.map(|(k, v)| {
let hash = new_state.pcd_commitment.get(k).unwrap().to_owned();
let cipher = v.as_str().unwrap().to_owned();
(hash.as_str().unwrap().to_owned(), cipher)
})
.collect();
process.insert_concurrent_state(new_state.clone())?;
{
@ -1159,7 +1149,7 @@ pub fn create_new_process(
let commit_msg = CommitMessage::new_update_commitment(
process_id,
pcd_commitment,
roles.into_iter().collect(),
roles,
public_data,
);
@ -1183,33 +1173,32 @@ pub fn create_new_process(
#[wasm_bindgen]
/// TODO allow modifications from user that doesn't have read access to all attributes
pub fn update_process(
process: JsValue,
mut process: Process,
new_attributes: JsValue,
roles: JsValue,
new_public_data: JsValue,
) -> ApiResult<ApiReturn> {
let mut process: Process = serde_wasm_bindgen::from_value(process)?;
let new_attributes: Value = serde_wasm_bindgen::from_value(new_attributes)?;
let roles: BTreeMap<String, RoleDefinition> = serde_wasm_bindgen::from_value(roles)?;
let new_public_data: BTreeMap<String, String> = serde_wasm_bindgen::from_value(new_public_data)?;
// debug!("{:#?}", process);
// debug!("{:#?}", new_attributes);
// debug!("{:#?}", roles);
let new_attributes: Pcd = serde_wasm_bindgen::from_value(new_attributes)?;
let roles: Roles = serde_wasm_bindgen::from_value(roles)?;
let new_public_data: Pcd = serde_wasm_bindgen::from_value(new_public_data)?;
let process_id = process.get_process_id()?;
let prev_state = process.get_latest_commited_state()
.ok_or(ApiError::new("Process must have at least one state already commited".to_owned()))?;
let public_data = if new_public_data.len() > 0 { new_public_data } else { prev_state.public_data.clone() };
let mut prev_public_data = prev_state.public_data.clone();
for (field, value) in new_public_data.into_iter() {
prev_public_data.insert(field, value);
}
// We expect the whole set of attributes for now, even if value does'nt change since previous state
// We expect the whole set of attributes for now, even if value doesn't change since previous state
// We rehash everything with the new txid, so we need the clear value
// eventually we would like to be able to create partial states even if we don't have read access to some attributes
let mut new_state = ProcessState::new(
process.get_process_tip()?,
new_attributes.to_value_object()?,
&public_data,
new_attributes.clone(),
prev_public_data,
roles.clone()
)?;
@ -1228,20 +1217,18 @@ pub fn update_process(
let diffs = create_diffs(&process, &new_state)?;
let all_fields: Vec<String> = new_attributes.as_object().unwrap().into_iter().map(|(field, _)| field.clone()).collect();
let mut fields2keys = Map::new();
let mut fields2cipher = Map::new();
new_attributes.encrypt_fields(&all_fields, &mut fields2keys, &mut fields2cipher);
let all_fields: Vec<String> = new_attributes.iter().map(|(field, _)| field.clone()).collect();
let mut encrypted_data = BTreeMap::new();
new_state.keys = fields2keys;
let encrypted_data: HashMap<String, String> = fields2cipher.into_iter()
.map(|(k, v)| {
let hash = new_state.pcd_commitment.get(k).unwrap().to_owned();
let cipher = v.as_str().unwrap().to_owned();
(hash.as_str().unwrap().to_owned(), cipher)
})
.collect();
let mut rng = thread_rng();
for (field, plain_value) in new_attributes.iter() {
let hash = new_state.pcd_commitment.get(field).ok_or(anyhow::Error::msg("Missing commitment"))?;
let key = generate_key(&mut rng);
new_state.keys.insert(field.to_owned(), key);
let serialized = serde_json::to_string(plain_value)?;
let cipher = encrypt_with_key(&key, serialized.as_bytes())?;
encrypted_data.insert(hash.to_lower_hex_string(), cipher.to_lower_hex_string());
}
// Add the new state to the process
process.insert_concurrent_state(new_state.clone())?;
@ -1264,7 +1251,7 @@ pub fn update_process(
let commit_msg = CommitMessage::new_update_commitment(
process_id,
new_state.pcd_commitment,
roles.into_iter().collect(),
roles,
new_state.public_data,
);
@ -1276,13 +1263,22 @@ pub fn update_process(
}
#[wasm_bindgen]
pub fn request_data(process_id: String, state_ids: Vec<String>, roles: JsValue) -> ApiResult<ApiReturn> {
pub fn request_data(process_id: String, state_ids_str: Vec<String>, roles: JsValue) -> ApiResult<ApiReturn> {
let process_id = OutPoint::from_str(&process_id)?;
let local_device = lock_local_device()?;
let sender = local_device.to_member();
let sp_wallet = local_device.get_wallet();
let local_address = sp_wallet.get_client().get_receiving_address();
let roles: Vec<BTreeMap<String, RoleDefinition>> = serde_wasm_bindgen::from_value(roles)?;
let roles: Vec<Roles> = serde_wasm_bindgen::from_value(roles)?;
let mut state_ids: Vec<[u8; 32]> = vec![];
for s in state_ids_str {
if (s.len() == 0 || s == String::from_utf8(Vec::from([0u8; 32])).unwrap()) { continue; }
let state_id: Result<[u8; 32], _> = Vec::from_hex(&s)?.try_into().map_err(|_| ApiError::new("Invalid state id".to_owned()));
if let Ok(state_id) = state_id {
state_ids.push(state_id);
}
}
let mut send_to: HashSet<SilentPaymentAddress> = HashSet::new();
for role in roles {
@ -1294,7 +1290,7 @@ pub fn request_data(process_id: String, state_ids: Vec<String>, roles: JsValue)
for member in members {
for address in member.get_addresses() {
if address == local_address { continue };
send_to.insert(SilentPaymentAddress::try_from(address).unwrap());
send_to.insert(SilentPaymentAddress::try_from(address)?);
}
}
}
@ -1303,7 +1299,8 @@ pub fn request_data(process_id: String, state_ids: Vec<String>, roles: JsValue)
let prd_request = Prd::new_request(
process_id,
sender,
state_ids.iter().map(|s| Vec::from_hex(s).unwrap().try_into().unwrap()).collect());
state_ids
);
let prd_msg = prd_request.to_network_msg(sp_wallet)?;
@ -1333,6 +1330,7 @@ pub fn create_update_message(
let mut processes = lock_processes()?;
let process_id = OutPoint::from_str(&process_id)?;
let state_id: [u8; 32] = Vec::from_hex(&state_id)?.try_into().map_err(|_| ApiError::new("Invalid state_id".to_owned()))?;
let process = processes.get_mut(&process_id)
.ok_or(ApiError::new("Unknown process".to_owned()))?;
@ -1346,7 +1344,7 @@ pub fn create_update_message(
let mut all_members: HashMap<Member, HashSet<String>> = HashMap::new();
let shared_secrets = lock_shared_secrets()?;
for (name, role) in &update_state.roles {
for (name, role) in update_state.roles.iter() {
let fields: Vec<String> = role
.validation_rules
.iter()
@ -1410,30 +1408,25 @@ pub fn create_update_message(
}
#[wasm_bindgen]
pub fn validate_state(process_id: String, state_id: String) -> ApiResult<ApiReturn> {
add_validation_token(process_id, state_id, true)
pub fn validate_state(process: Process, state_id: String) -> ApiResult<ApiReturn> {
add_validation_token(process, state_id, true)
}
#[wasm_bindgen]
pub fn refuse_state(process_id: String, state_id: String) -> ApiResult<ApiReturn> {
add_validation_token(process_id, state_id, false)
pub fn refuse_state(process: Process, state_id: String) -> ApiResult<ApiReturn> {
add_validation_token(process, state_id, false)
}
#[wasm_bindgen]
pub fn evaluate_state(process_id: String, previous_state: Option<String>, state: String) -> ApiResult<ApiReturn> {
let prev_state: Option<ProcessState> = if let Some(s) = previous_state { Some(serde_json::from_str(&s)?) } else { None };
let process_state: ProcessState = serde_json::from_str(&state)?;
process_state.is_valid(prev_state.as_ref())?;
let clear_pcd = process_state.decrypt_pcd()?;
pub fn evaluate_state(process_id: String, previous_state: Option<ProcessState>, process_state: ProcessState) -> ApiResult<ApiReturn> {
process_state.is_valid(previous_state.as_ref())?;
// We create a commit msg with the valid state
let outpoint: OutPoint = OutPoint::from_str(&process_id)?;
let commit_msg = CommitMessage::new_update_commitment(
outpoint,
process_state.pcd_commitment,
process_state.roles.clone().into_iter().collect(),
process_state.roles,
process_state.public_data,
);
@ -1443,27 +1436,18 @@ pub fn evaluate_state(process_id: String, previous_state: Option<String>, state:
})
}
fn add_validation_token(process_id: String, state_id: String, approval: bool) -> ApiResult<ApiReturn> {
let mut processes = lock_processes()?;
let outpoint = OutPoint::from_str(&process_id)?;
let process = processes.get_mut(&outpoint)
.ok_or(ApiError::new("Unknown process".to_owned()))?;
fn add_validation_token(mut process: Process, state_id: String, approval: bool) -> ApiResult<ApiReturn> {
let process_id = process.get_process_id()?;
let state_id: [u8; 32] = Vec::from_hex(&state_id)?.try_into().map_err(|_| ApiError::new("Invalid state_id".to_owned()))?;
if state_id == [0u8; 32] { return Err(ApiError::new("Can't validate empty state".to_owned())); }
{
let update_state: &mut ProcessState = process.get_state_for_id_mut(&state_id)?;
let merkle_root: [u8; 32] = Vec::from_hex(&state_id)?
.try_into()
.map_err(
|_| ApiError::new(format!("Failed to deserialize state_id: {}", state_id))
)?;
let message_hash = if approval {
AnkHash::ValidationYes(AnkValidationYesHash::from_merkle_root(merkle_root))
AnkHash::ValidationYes(AnkValidationYesHash::from_merkle_root(state_id))
} else {
AnkHash::ValidationNo(AnkValidationNoHash::from_merkle_root(merkle_root))
AnkHash::ValidationNo(AnkValidationNoHash::from_merkle_root(state_id))
};
let local_device = lock_local_device()?;
@ -1480,11 +1464,10 @@ fn add_validation_token(process_id: String, state_id: String, approval: bool) ->
let update_is_valid = update_state.is_valid(process.get_parent_state(&update_state.commited_in));
if update_is_valid.is_ok() {
let pcd_commitment = update_state.pcd_commitment.clone();
let mut commit_msg = CommitMessage::new_update_commitment(
process.get_process_id()?,
pcd_commitment,
update_state.roles.clone().into_iter().collect(),
update_state.pcd_commitment.clone(),
update_state.roles.clone(),
update_state.public_data.clone(),
);
commit_msg.set_validation_tokens(update_state.validation_tokens.clone());
@ -1496,13 +1479,13 @@ fn add_validation_token(process_id: String, state_id: String, approval: bool) ->
};
let updated_process = UpdatedProcess {
process_id: OutPoint::from_str(&process_id)?,
process_id,
current_process: process.clone(),
validated_state: Some(state_id.clone()),
..Default::default()
};
let ciphers_to_send = new_response_prd(OutPoint::from_str(&process_id)?, process.get_state_for_id_mut(&state_id)?)?;
let ciphers_to_send = new_response_prd(process_id, process.get_state_for_id_mut(&state_id)?)?;
Ok(ApiReturn {
updated_process: Some(updated_process),
@ -1515,6 +1498,7 @@ fn add_validation_token(process_id: String, state_id: String, approval: bool) ->
#[wasm_bindgen]
pub fn create_response_prd(process_id: String, state_id: String) -> ApiResult<ApiReturn> {
let mut processes = lock_processes()?;
let state_id: [u8; 32] = Vec::from_hex(&state_id)?.try_into().map_err(|_| ApiError::new("Invalid state_id".to_owned()))?;
let outpoint = OutPoint::from_str(&process_id)?;
@ -1538,7 +1522,7 @@ fn new_response_prd(process_id: OutPoint, update_state: &mut ProcessState) -> An
let mut all_members: HashMap<Member, HashSet<String>> = HashMap::new();
let shared_secrets = lock_shared_secrets()?;
for (name, role) in &update_state.roles {
for (name, role) in update_state.roles.iter() {
let fields: Vec<String> = role
.validation_rules
.iter()