tap_msg/examples/
policy_examples.rs

1//! Examples for using policies according to TAIP-7.
2
3use crate::error::Result;
4use crate::message::{
5    policy::{Policy, RequireAuthorization, RequirePresentation, RequireProofOfControl},
6    tap_message_trait::TapMessageBody,
7    types::{Authorizable, Authorize, Participant, Transfer, UpdatePolicies},
8};
9use didcomm::Message;
10use std::collections::HashMap;
11use std::str::FromStr;
12use tap_caip::AssetId;
13
14/// This example demonstrates how to create a participant with policies
15pub fn create_participant_with_policies_example() -> Result<Participant> {
16    // Create a RequireAuthorization policy
17    let auth_policy = RequireAuthorization {
18        from: Some(vec!["did:example:alice".to_string()]),
19        from_role: None,
20        from_agent: None,
21        purpose: Some("Authorization required from Alice".to_string()),
22    };
23
24    // Create a RequirePresentation policy
25    let presentation_policy = RequirePresentation {
26        context: Some(vec![
27            "https://www.w3.org/2018/credentials/v1".to_string(),
28            "https://w3id.org/security/suites/ed25519-2020/v1".to_string(),
29        ]),
30        from: Some(vec!["did:example:bob".to_string()]),
31        from_role: None,
32        from_agent: None,
33        about_party: Some("originator".to_string()),
34        about_agent: None,
35        purpose: Some("Please provide KYC credentials".to_string()),
36        presentation_definition: Some("https://example.com/presentations/kyc".to_string()),
37        credentials: None,
38    };
39
40    // Create the participant with policies
41    let participant = Participant {
42        id: "did:example:charlie".to_string(),
43        role: Some("beneficiary".to_string()),
44        leiCode: None,
45        policies: Some(vec![
46            Policy::RequireAuthorization(auth_policy),
47            Policy::RequirePresentation(presentation_policy),
48        ]),
49    };
50
51    Ok(participant)
52}
53
54/// This example demonstrates how to update policies for a transaction
55pub fn update_policies_example(
56    transaction_id: &str,
57    creator_did: &str,
58    recipients: &[&str],
59) -> Result<Message> {
60    // Create a proof of control policy
61    let proof_policy = RequireProofOfControl {
62        from: Some(vec!["did:example:dave".to_string()]),
63        from_role: None,
64        from_agent: None,
65        address_id: "eip155:1:0x1234567890123456789012345678901234567890".to_string(),
66        purpose: Some("Please prove control of your account".to_string()),
67    };
68
69    // Create an UpdatePolicies message
70    let update = UpdatePolicies {
71        transaction_id: transaction_id.to_string(),
72        policies: vec![Policy::RequireProofOfControl(proof_policy)],
73    };
74
75    // Convert the update to a DIDComm message
76    let participants = recipients
77        .iter()
78        .filter(|&&did| did != creator_did)
79        .copied()
80        .collect::<Vec<_>>();
81    let message = update.to_didcomm_with_route(Some(creator_did), participants)?;
82
83    // Set the thread ID to link this message to the existing thread
84    let message_with_thread = Message {
85        thid: Some(transaction_id.to_string()),
86        ..message
87    };
88
89    Ok(message_with_thread)
90}
91
92/// This example demonstrates a complete workflow for adding and updating policies
93pub fn policy_workflow_example() -> Result<()> {
94    println!("=== Starting Policy Workflow Example ===");
95
96    // Define DIDs for our example
97    let originator_did = "did:example:originator";
98    let beneficiary_did = "did:example:beneficiary";
99    let sender_vaspd_did = "did:example:sender_vasp";
100    let receiver_vaspd_did = "did:example:receiver_vasp";
101
102    // Step 1: Create a beneficiary with policies
103    println!("Step 1: Creating beneficiary with policies");
104    let auth_policy = RequireAuthorization {
105        from: Some(vec![originator_did.to_string()]),
106        from_role: None,
107        from_agent: None,
108        purpose: Some("Authorization required from originator".to_string()),
109    };
110
111    let beneficiary = Participant {
112        id: beneficiary_did.to_string(),
113        role: Some("beneficiary".to_string()),
114        leiCode: None,
115        policies: Some(vec![Policy::RequireAuthorization(auth_policy)]),
116    };
117    println!("  Created beneficiary with policies: {:?}", beneficiary);
118
119    // Step 2: Create a transfer ID (this would normally be generated in practice)
120    let transfer_id = "transfer_12345";
121    println!("Step 2: Created transfer ID: {}", transfer_id);
122
123    // Step 3: Sender VASP wants to add a presentation requirement policy
124    println!("Step 3: Sender VASP adds a presentation requirement");
125    let presentation_policy = RequirePresentation {
126        context: Some(vec!["https://www.w3.org/2018/credentials/v1".to_string()]),
127        from: Some(vec![beneficiary_did.to_string()]),
128        from_role: None,
129        from_agent: None,
130        about_party: Some("beneficiary".to_string()),
131        about_agent: None,
132        purpose: Some("Please provide identity credentials".to_string()),
133        presentation_definition: None,
134        credentials: Some(HashMap::from([(
135            "type".to_string(),
136            vec!["IdentityCredential".to_string()],
137        )])),
138    };
139
140    let update_message = UpdatePolicies {
141        transaction_id: transfer_id.to_string(),
142        policies: vec![Policy::RequirePresentation(presentation_policy)],
143    };
144
145    // Convert to DIDComm message with proper routing
146    let participants = [
147        originator_did,
148        beneficiary_did,
149        sender_vaspd_did,
150        receiver_vaspd_did,
151    ];
152    let to = participants
153        .iter()
154        .filter(|&&did| did != sender_vaspd_did)
155        .copied()
156        .collect::<Vec<_>>();
157    let message = update_message.to_didcomm_with_route(Some(sender_vaspd_did), to)?;
158
159    // Link to our transfer thread
160    let message_with_thread = Message {
161        thid: Some(transfer_id.to_string()),
162        ..message
163    };
164
165    println!(
166        "  Created UpdatePolicies message: {:?}",
167        message_with_thread
168    );
169    println!("  This message will be routed to all participants");
170
171    println!("=== Policy Workflow Example Completed ===");
172    Ok(())
173}
174
175/// This example demonstrates the use of the Authorizable trait's update_policies method
176pub fn create_update_policies_using_authorizable_example(
177    original_message: &Result<Message>,
178    policies: Vec<Policy>,
179    _transaction_id: &str,
180    creator_did: &str,
181    participant_dids: &[String],
182) -> Result<Message> {
183    // 1. Extract the body from the original DIDComm message
184    let body_json = original_message
185        .as_ref()
186        .map_err(Clone::clone)?
187        .body
188        .clone();
189    // 2. Deserialize the body into a Transfer struct
190    let transfer_body: Transfer = serde_json::from_value(body_json.clone())
191        .map_err(|e| crate::error::Error::SerializationError(e.to_string()))?;
192    // 3. Call update_policies on the Transfer struct (Authorizable trait impl)
193    // Extract or generate a transaction ID
194    let transaction_id = transfer_body.transaction_id.clone();
195    let update_policies_message = transfer_body.update_policies(transaction_id, policies);
196
197    // Convert the update to a DIDComm message
198    let mut update_policies_reply = update_policies_message.to_didcomm(Some(creator_did))?;
199
200    // Set thread ID to maintain conversation
201    update_policies_reply.thid = Some(original_message.as_ref().map_err(Clone::clone)?.id.clone());
202
203    // Set recipients
204    update_policies_reply.to = Some(participant_dids.iter().map(|s| s.to_string()).collect());
205
206    Ok(update_policies_reply)
207}
208
209/// This example demonstrates a modified policy workflow using the Authorizable trait
210pub fn policy_workflow_with_authorizable_example() -> Result<()> {
211    println!("=== Starting Policy Workflow with Authorizable Example ===");
212
213    // Define DIDs for our example
214    let originator_did = "did:example:originator";
215    let beneficiary_did = "did:example:beneficiary";
216    let sender_vasp_did = "did:example:sender_vasp";
217    let receiver_vasp_did = "did:example:receiver_vasp";
218
219    // Step 1: Create a transfer message to initiate the workflow
220    let transfer = Transfer {
221        transaction_id: uuid::Uuid::new_v4().to_string(),
222        asset: AssetId::from_str("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
223            .unwrap(),
224        originator: Participant {
225            id: originator_did.to_string(),
226            role: Some("originator".to_string()),
227            leiCode: None,
228            policies: None,
229        },
230        beneficiary: Some(Participant {
231            id: beneficiary_did.to_string(),
232            role: Some("beneficiary".to_string()),
233            leiCode: None,
234            policies: None,
235        }),
236        amount: "100.00".to_string(),
237        memo: None,
238        agents: vec![
239            Participant {
240                id: sender_vasp_did.to_string(),
241                role: Some("sender_vasp".to_string()),
242                leiCode: None,
243                policies: None,
244            },
245            Participant {
246                id: receiver_vasp_did.to_string(),
247                role: Some("receiver_vasp".to_string()),
248                leiCode: None,
249                policies: None,
250            },
251        ],
252        settlement_id: None,
253        metadata: HashMap::new(),
254    };
255
256    // Convert the transfer to a DIDComm message
257    let transfer_message = transfer.to_didcomm_with_route(
258        Some(originator_did),
259        [beneficiary_did, sender_vasp_did, receiver_vasp_did]
260            .iter()
261            .copied(),
262    )?;
263
264    println!("Transfer message created: {:?}", transfer_message);
265
266    // Step 2: Create an UpdatePolicies message using the Authorizable trait
267    // This would typically be created by a VASP to enforce compliance
268    let participants = [
269        originator_did.to_string(),
270        beneficiary_did.to_string(),
271        sender_vasp_did.to_string(),
272        receiver_vasp_did.to_string(),
273    ];
274
275    let cloned_transfer_id = transfer_message.id.clone();
276    let update_policies_message = create_update_policies_using_authorizable_example(
277        &Ok(transfer_message),
278        vec![],
279        &cloned_transfer_id,
280        sender_vasp_did,
281        &participants,
282    )?;
283
284    println!(
285        "Update policies message created: {:?}",
286        update_policies_message
287    );
288
289    // Step 3: Create an authorization message in response to the updated policies
290    let authorize_body =
291        transfer.authorize(Some("Authorization with policy constraints".to_string()));
292
293    // Create a reply to the update policies message
294    let mut authorize_reply = authorize_body.to_didcomm(Some(beneficiary_did))?;
295
296    // Set thread ID to maintain conversation
297    authorize_reply.thid = Some(update_policies_message.id.clone());
298
299    // Set recipients
300    authorize_reply.to = Some(participants.iter().map(|s| s.to_string()).collect());
301
302    println!("Authorization message created: {:?}", authorize_reply);
303
304    Ok(())
305}
306
307/// This example demonstrates a modified policy workflow using the Authorizable trait
308pub fn create_authorize_example() -> Result<()> {
309    // Create an example Authorize message body
310    let authorize_message = Authorize {
311        transaction_id: "transfer_12345".to_string(),
312        note: Some("Authorized with policy constraints".to_string()),
313    };
314
315    println!("Authorize message: {:#?}", authorize_message);
316
317    Ok(())
318}