<jupyter_start><jupyter_text>PETs/TETs – Hyperledger Aries – Manufacturer 1 (Holder) 🚗<jupyter_code>%%javascript
document.title ='🚗 Manufacturer1'<jupyter_output><empty_output><jupyter_text>PART 2: Issue a VC to the Manufacturer Agents**What:** Receive and store verifiable credential (VC) from the authority agent (i.e., the issuing authority)**Why:** To prove that the agent is a manufacturer to relying parties (e.g., the city agent)**How:** 1. [Initiate Manufacturer's AgentCommunicationManager (ACM)](1)2. [Connect with the Authority agent](2)3. [Request VC](3)**Accompanying Agents and Notebooks:** * Authority 🏛️: `02_issue_VC_manufacturers.ipynb` --- 0 - Setup 0.1 - Imports<jupyter_code>import os

from aries_cloudcontroller import AriesAgentController

import libs.helpers as helpers
from libs.agent_connection_manager import CredentialHolder<jupyter_output><empty_output><jupyter_text>0.2 – Variables<jupyter_code># Get relevant details from .env file
api_key = os.getenv("ACAPY_ADMIN_API_KEY")
admin_url = os.getenv("ADMIN_URL")
webhook_port = int(os.getenv("WEBHOOK_PORT"))
webhook_host = "0.0.0.0"<jupyter_output><empty_output><jupyter_text>---  1 – Initiate Manufacturer1 Agent 1.1 – Init ACA-PY agent controller<jupyter_code># Setup
agent_controller = AriesAgentController(admin_url,api_key)
print(f"Initialising a controller with admin api at {admin_url} and an api key of {api_key}")<jupyter_output>Initialising a controller with admin api at http://manufacturer1-agent:3021 and an api key of adminApiKey<jupyter_text>1.2 – Start Webhook Server to enable communication with other agents@todo: is communication with other agents, or with other docker containers?<jupyter_code># Listen on webhook server
await agent_controller.init_webhook_server(webhook_host, webhook_port)
print(f"Listening for webhooks from agent at http://{webhook_host}:{webhook_port}")<jupyter_output>Listening for webhooks from agent at http://0.0.0.0:3010<jupyter_text>1.3 – Init ACM issuing authority<jupyter_code># The CredentialHolder registers relevant webhook servers and event listeners
manufacturer1_agent = CredentialHolder(agent_controller)

# Verify if Manufacturer already has a VC 
# (if there are manufacturer credentials, there is no need to execute the notebook)
manufacturer1_agent.get_credentials()<jupyter_output>[1m[32mSuccessfully initiated AgentConnectionManager for a(n) Holder ACA-PY agent[0m<jupyter_text>--- 2 – Establish a connection with the Authority agentA connection with the credential issuer (i.e., the authority agent) must be established before a VC can be received. In this scenario, the manufacturer1 requests a connection with the Authority to be certified as an official city agency. Thus, the manufacturer1 agent sends an invitation to the Authority. In real life, the invitation can be shared via video call, phone call, or E-Mail. In this PoC, this is represented by copy and pasting the invitation into the manufacturers' notebooks. 2.1 Create invitation to Authority agentSeveral state changes of the connection between the Manufacturer agent, the inviter (A), and the authority agent, the invitee (B),  are required before successfully establishing a connection: | Step | State | Agent | Description | Function/Prompt/Variable || --- | --- | --- | --- | --- || 1 | invitation-sent | A | A sent an invitation to B | `create_connection_invitation()`| 2 | invitation-received | B | B receives the invitation of A | Prompt: Paste invitation from A || 3 | request-sent | B | B sends B connection request | Prompt: Accept invitation OR `auto_accept=True` || 4 | request-received | A | A receives the connection request from B | Prompt: Accept invitation request response OR `auto_accept=True` || 5 | response-sent | A | A sends B response to B | - || 6 | response-received | B | B receives the response from A | - || 7 | active (completed) | A | B pings A to finalize connection | Prompt: Trust ping OR `auto_ping=True` |<jupyter_code># Setup for connection with Authority agent
alias = None
auto_accept = True # Accept response of Authority agent right away
auto_ping = True
public = False # Do not use public DID
multi_use = False # Invitation is only for one invitee

connection_id = manufacturer1_agent.create_connection_invitation(alias=alias, auto_accept=auto_accept, public=public, multi_use=multi_use, auto_ping=auto_ping)<jupyter_output>[1m[35m
Copy & paste invitation and share with external agent(s):[0m
{
    '@id': 'd3680083-53c5-42da-8e18-0d31b805f470',
    '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation',
    'label': 'AnonymousAgent1',
    'recipientKeys': ['<KEY>'],
    'serviceEndpoint': 'https://ed79-80-134-219-30.ngrok.io',
}

---------------------------------------------------------------------
[1mConnection Webhook Event Received: Connections Handler[0m
Connection ID :  448fcc6b-0d32-45d1-a963-fdaf40db5b9b
State :  [34minvitation (invitation-sent)[0m
Routing State : none
Their Role :  invitee
---------------------------------------------------------------------

---------------------------------------------------------------------
[1mConnection Webhook Event Received: Connections Handler[0m
Connection ID :  448fcc6b-0d32-45d1-a963-fdaf40db5b9b
State :  [34mrequest (request-received)[0m
Routing State : none
Connection with :  Governan[...]<jupyter_text>Break Point 1🚗 ➡️ 🏛Please switch to the Authority agent 🏛: open 02_issue_VC_manufacturers.ipynb, and run all cells until Break Point 2. Please paste the invitation in Step 2.1 when prompted. If auto_accept is set to False, enter "yes" when prompted before proceeding.--- 3 – Request VC from `Authority` agent 3.1 – Message `Authority` with request<jupyter_code># Contact authority agent
basic_message = "Hello Authority. I would like to be certified as a manufacturer."
manufacturer1_agent.send_message(connection_id, basic_message)

# Send authority agent the relevant information
basic_message = '{"manufacturerName": "Manufacturer1", "manufacturerCountry": "DE", "manufacturerCity": "Berlin"}'
manufacturer1_agent.send_message(connection_id, basic_message)<jupyter_output>Sent message via Connection ID 448fcc6b-0d32-45d1-a963-fdaf40db5b9b
Sent message via Connection ID 448fcc6b-0d32-45d1-a963-fdaf40db5b9b<jupyter_text>Break Point 3🚗 ➡️ 🏛Please switch to the Authority agent 🏛: Proceed with Step 3 3.2 – Respond to Authority's VC offerThe following `request_vc()` function wraps multiple functionalities. First, the offer of the Authority agent must be identified using a credential_exchange_id generated for it. Then, Manufacturer1 can respond to the offer. The detailed steps and state changes are presented in the table below, whereby I is the Issuer (i.e., the Authority agent) and H is the Holder (i.e., the Manufacturer agent)| Step | State | Role | Description | Function/Prompt/Variable || --- | --- | --- | --- | --- || 1 | offer_sent | I | I sends I VC offer with personalized information to H| `offer_vc()` || 2 | offer_received | H | H receives offer made by I | - || 3 | request_sent | H | Request VC offer | `request_vc()` AND (Prompt: request VC OR `auto_request=True`) || 4 | request_received | I | M1's request to get VC was received | - || 5 | credential_issued | I | Automatic response to issue credential | - || 6 | credential_received | H| H receives VC and is asked to store it | Prompt: Store VC OR `auto_store=True`| 7 | credential_acked | I / H | Credential was issued and stored | - |<jupyter_code># Get schema_id of manufacturer_schema to find the correct credential_exchange_id
identifiers = helpers.get_identifiers()
schema_id = identifiers["manufacturer_schema_identifiers"]["schema_id"]

# Variables
auto_request = False # Authomatically requests VC when offer is found
auto_store = False # Automatically stores VC if issued by Authority

# Request VC
manufacturer1_agent.request_vc(connection_id, schema_id, auto_request, auto_store)<jupyter_output>[34mGet stored identifiers dictionary to access schema information 💾[0m
[1m[35m
Request VC from offer[0m<jupyter_text>3.3 – Verify that VC was stored in wallet<jupyter_code>manufacturer1_agent.get_credentials()<jupyter_output><empty_output><jupyter_text>---  4 – Terminate ControllerWhenever you have finished with this notebook, be sure to terminate the controller. This is especially important if your business logic runs across multiple notebooks.<jupyter_code>await agent_controller.terminate()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11572490267783414, 'fraction_numerical': 0.025716645039518697, 'mean_word_length': 4.846896551724138, 'pattern_counts': {'":': 3, '<': 40, '<?xml version=': 0, '>': 40, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '634167', 'license': 'Apache-2.0', 'n_tokens_mistral': 2685, 'n_tokens_neox': 2482, 'n_words': 881, 'path': 'agents/manufacturer1/notebooks/02_get_manufacturer1_VC.ipynb', 'repo_name': 'kajaschmidt/aries-jupyter-playground', 'size': '8516'}