roles/
all_role_claims_deleted_proof.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use std::collections::BTreeMap;

use hdk::prelude::*;
use roles_integrity::{AllRoleClaimsDeletedProof, EntryTypes, LinkTypes};

use crate::{
    linked_devices::get_all_my_agents,
    unassignments::get_pending_unassignment_links_for_me,
    utils::{create_relaxed, delete_link_relaxed},
};

#[hdk_extern]
pub fn create_all_role_claims_deleted_proofs_if_possible() -> ExternResult<()> {
    let pending_unassignment_links_for_me = get_pending_unassignment_links_for_me()?;

    let my_delete_links = query(ChainQueryFilter::new().action_type(ActionType::DeleteLink))?;
    let my_deleted_links_hashes = my_delete_links
        .into_iter()
        .map(|record| match record.action() {
            Action::DeleteLink(delete_link) => Ok(delete_link.link_add_address.clone()),
            _ => Err(wasm_error!(
                "DeleteLink record does not include a DeleteLink."
            )),
        })
        .collect::<ExternResult<Vec<ActionHash>>>()?;
    let undeleted_pending_unassignment_links_for_me: Vec<Link> = pending_unassignment_links_for_me
        .into_iter()
        .filter(|link| !my_deleted_links_hashes.contains(&link.create_link_hash))
        .collect();

    if undeleted_pending_unassignment_links_for_me.len() == 0 {
        // I have no pending unassignments: do nothing
        return Ok(());
    }

    let all_my_agents_activities = get_all_my_agents_role_claims_activities()?;

    for my_pending_unassignment_link in undeleted_pending_unassignment_links_for_me {
        create_all_role_claims_deleted_proof_if_possible(
            my_pending_unassignment_link,
            &all_my_agents_activities,
        )?;
    }
    Ok(())
}

pub fn get_all_my_agents_role_claims_activities(
) -> ExternResult<BTreeMap<AgentPubKey, AgentActivity>> {
    let all_my_agents = get_all_my_agents()?;
    let mut all_my_agents_activities: BTreeMap<AgentPubKey, AgentActivity> = BTreeMap::new();
    let creates_and_deletes_role_claims_query_filter = ChainQueryFilter::new()
        .action_type(ActionType::CreateLink)
        .action_type(ActionType::DeleteLink);

    for agent in all_my_agents {
        let activity = get_agent_activity(
            agent.clone(),
            creates_and_deletes_role_claims_query_filter.clone(),
            ActivityRequest::Full,
        )?;
        all_my_agents_activities.insert(agent, activity);
    }

    Ok(all_my_agents_activities)
}

/** If all my agents have deleted their role claim, create the AllRoleClaimsDeletedProof and delete the links */
pub fn create_all_role_claims_deleted_proof_if_possible(
    my_pending_unassignment_link: Link,
    all_my_agents_activity: &BTreeMap<AgentPubKey, AgentActivity>,
) -> ExternResult<()> {
    info!("Attempting to create an AllRoleClaimsDeletedProof for the existing PendingUnassignment link.");
    let Some(assign_role_create_link_hash) = my_pending_unassignment_link.target.into_action_hash()
    else {
        return Err(wasm_error!(
            "Invalid PendingUnassignment link: must have an ActionHash as its target"
        ));
    };
    let mut role_claims_deletes_hashes: BTreeMap<AgentPubKeyB64, ActionHash> = BTreeMap::new();
    for (agent, activity) in all_my_agents_activity {
        let maybe_role_claim_deletes =
            get_deleted_role_claim_for(activity, &assign_role_create_link_hash)?;
        let Some(role_claim_delete) = maybe_role_claim_deletes else {
            info!("Giving up on trying to create AllRoleClaimsDeletedProof: not all our devices have deleted their role claim.");
            return Ok(());
        };
        role_claims_deletes_hashes.insert(agent.clone().into(), role_claim_delete);
    }

    let proof = AllRoleClaimsDeletedProof {
        assign_role_create_link_hash: assign_role_create_link_hash.clone(),
        pending_unassignment_create_link_hash: my_pending_unassignment_link
            .create_link_hash
            .clone(),
        role_claims_delete_links_hashes: role_claims_deletes_hashes,
        lost_agents: vec![],
    };
    create_relaxed(EntryTypes::AllRoleClaimsDeletedProof(proof))?;
    delete_link_relaxed(assign_role_create_link_hash)?;
    delete_link_relaxed(my_pending_unassignment_link.create_link_hash)?;
    info!("Unassigning role: created an AllRoleClaimsDeletedProof and deleted the assignment and the pending unassignment links.");

    Ok(())
}

fn get_deleted_role_claim_for(
    activity: &AgentActivity,
    assign_role_create_link_hash: &ActionHash,
) -> ExternResult<Option<ActionHash>> {
    let get_inputs: Vec<GetInput> = activity
        .valid_activity
        .iter()
        .map(|(_, action_hash)| GetInput::new(action_hash.clone().into(), GetOptions::default()))
        .collect();

    let maybe_records = HDK.with(|hdk| hdk.borrow().get_details(get_inputs))?;

    let records = maybe_records
        .into_iter()
        .map(|maybe_details| {
            let details =
                maybe_details.ok_or(wasm_error!("Could not get Record for my agent activity."))?;
            let Details::Record(RecordDetails { record, .. }) = details else {
                return Err(wasm_error!("get_details returned EntryDetails."));
            };
            Ok(record)
        })
        .collect::<ExternResult<Vec<Record>>>()?;

    for role_claim_create_link_record in &records {
        let Action::CreateLink(role_claim_create_link) = role_claim_create_link_record.action()
        else {
            continue;
        };

        let Ok(Some(LinkTypes::AssigneeRoleClaim)) = LinkTypes::from_type(
            role_claim_create_link.zome_index,
            role_claim_create_link.link_type,
        ) else {
            continue;
        };

        let Some(role_claim_role_to_assignee_create_link_hash) = role_claim_create_link
            .target_address
            .clone()
            .into_action_hash()
        else {
            continue;
        };

        if role_claim_role_to_assignee_create_link_hash.ne(assign_role_create_link_hash) {
            continue;
        }

        let delete_record = records.iter().find(|delete_record| {
            let Action::DeleteLink(delete_link) = delete_record.action() else {
                return false;
            };
            delete_link
                .link_add_address
                .eq(role_claim_create_link_record.action_address())
        });
        return Ok(delete_record.map(|r| r.action_address().clone()));
    }

    Ok(None)
}