diff --git a/configuration/aries-args-multitenant.yaml b/configuration/aries-args-multitenant.yaml new file mode 100644 index 00000000..08428dd2 --- /dev/null +++ b/configuration/aries-args-multitenant.yaml @@ -0,0 +1,60 @@ +# see: https://pypi.org/project/ConfigArgParse/ for file format overview +# before running aca-py, run the following (the commands are embedded below, next to the related parameters): +# - run a local postgres database +# - run a local instance of von-network +# - register your did (seed) on the network +# run aca-py as: +# ACAPY_WALLET_SEED=my_seed_000000000000000000000000 ACAPY_WALLET_KEY=key ./bin/aca-py start --arg-file ./demo/local-indy-args.yaml + +log-level: info +genesis-url: !ENV ${GENESIS_URL} +# Admin insecure mode and api key will come from the following two variables +# which is parsed and used automatically by ACA-Py +# ACAPY_ADMIN_API_KEY +# ACAPY_ADMIN_INSECURE_MODE +admin: [0.0.0.0, !ENV '${ADMIN_PORT}'] +label: !ENV ${AGENT_NAME} +# the following is the callback url for your controller +webhook-url: !ENV ${WEBHOOK_URL} +# assumes you are running a local von-network, like: +# cd von-network +# ./manage start +inbound-transport: + - [http, 0.0.0.0, !ENV '${HTTP_PORT}'] +# - [ws, 0.0.0.0, !ENV '${HTTP_PORT}'] +outbound-transport: http +# the following is the public endpoint advertised by the agent +endpoint: !ENV ${AGENT_ENDPOINT} +auto-ping-connection: true +# register your did using (this example is for von-network): +# curl -d '{"seed":"my_seed_000000000000000000000000", "role":"TRUST_ANCHOR", "alias":"My Agent"}' -X POST http://localhost:9000/register +# note that the env var name is configured in argparse.py +# seed = comes from ACAPY_WALLET_SEED +wallet-type: !ENV ${WALLET_TYPE} +wallet-name: !ENV ${WALLET_NAME} +wallet-key: !ENV ${WALLET_KEY} +seed: !ENV ${WALLET_SEED} + +## run a local postgres (docker) like: +## docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres:10 +#wallet-storage-type: postgres_storage +## could be sent using env var ACAPY_WALLET_STORAGE_CONFIG +#wallet-storage-config: '{"url":"localhost:5432","max_connections":5}' +## could be sent using env var ACAPY_WALLET_STORAGE_CREDS +#wallet-storage-creds: '{"account":"postgres","password":"mysecretpassword","admin_account":"postgres","admin_password":"mysecretpassword"}' + + +auto-accept-requests: true +auto-provision: true +auto-respond-credential-proposal: false +auto-respond-credential-offer: false +auto-respond-credential-request: false +auto-store-credential: false +auto-respond-presentation-proposal: true +auto-respond-presentation-request: true +preserve-exchange-records: true +debug-connections: true +debug-credentials: true +debug-presentations: true +enable-undelivered-queue: true +open-mediation: true diff --git a/dockerfiles/controllers/Dockerfile.attachmentcontroller b/dockerfiles/controllers/Dockerfile.attachmentcontroller index 1b674751..017dd6aa 100644 --- a/dockerfiles/controllers/Dockerfile.attachmentcontroller +++ b/dockerfiles/controllers/Dockerfile.attachmentcontroller @@ -6,8 +6,8 @@ ARG jupyter_port ENV env_jupyter_port=jupyter_port # Setup workspace environment RUN apt-get update && apt-get install -y gcc -# RUN conda install jupyter notebook=5.7.8 -RUN conda install jupyter notebook=6.1.5 +# RUN conda install -c conda-forge jupyterlab==3.0.11 +RUN conda install -c conda-forge jupyterlab==3.0.11 ADD libs/attachment-controller attachment-controller diff --git a/dockerfiles/controllers/Dockerfile.basiccontroller b/dockerfiles/controllers/Dockerfile.basiccontroller index e41831ee..a2d0a529 100644 --- a/dockerfiles/controllers/Dockerfile.basiccontroller +++ b/dockerfiles/controllers/Dockerfile.basiccontroller @@ -7,8 +7,8 @@ ENV env_jupyter_port=jupyter_port # Setup workspace environment RUN apt-get update && apt-get install -y gcc -# RUN conda install jupyter notebook=5.7.8 -RUN conda install jupyter notebook=6.1.5 +# RUN conda install -c conda-forge jupyterlab==3.0.11 +RUN conda install -c conda-forge jupyterlab==3.0.11 ADD libs/aries-basic-controller . # diff --git a/libs/acapy-protocol-example/entrypoint.sh b/libs/acapy-protocol-example/entrypoint.sh index bd3aaa61..59279d48 100644 --- a/libs/acapy-protocol-example/entrypoint.sh +++ b/libs/acapy-protocol-example/entrypoint.sh @@ -10,4 +10,4 @@ fi cd $WORKSPACE echo "HELLO THIS IS PORT $1" -jupyter notebook --ip=0.0.0.0 --port="$1" --allow-root +jupyter lab --ip=0.0.0.0 --port="$1" --allow-root diff --git a/libs/aries-basic-controller/aries_basic_controller/aries_controller.py b/libs/aries-basic-controller/aries_basic_controller/aries_controller.py index 1210cef5..e568560c 100644 --- a/libs/aries-basic-controller/aries_basic_controller/aries_controller.py +++ b/libs/aries-basic-controller/aries_basic_controller/aries_controller.py @@ -7,6 +7,7 @@ from .controllers.connections import ConnectionsController from .controllers.messaging import MessagingController +from .controllers.mediation import MediationController from .controllers.schema import SchemaController from .controllers.wallet import WalletController from .controllers.definitions import DefinitionsController @@ -14,6 +15,7 @@ from .controllers.proof import ProofController from .controllers.ledger import LedgerController from .controllers.credential import CredentialController +from .controllers.multitenant import MultitenancyController from .controllers.server import ServerController from .controllers.oob import OOBController from .controllers.action_menu import ActionMenuController @@ -35,10 +37,13 @@ def __init__( webhook_base: str = "", connections: bool = True, messaging: bool = True, + multitenant: bool = False, + mediation: bool = False, issuer: bool = True, action_menu: bool = True, revocations: bool = True, api_key: str = None, + tennant_jwt: str = None, ): self.webhook_site = None @@ -51,11 +56,16 @@ def __init__( self.webhook_port = webhook_port self.connections_controller = None + headers = {} + if api_key: - headers = {"X-API-Key": api_key} - self.client_session: ClientSession = ClientSession(headers=headers) - else: - self.client_session: ClientSession = ClientSession() + headers.update({"X-API-Key": api_key}) + + if tennant_jwt: + headers.update({'Authorization': 'Bearer ' + tennant_jwt, 'content-type': "application/json"}) + + self.client_session: ClientSession = ClientSession(headers=headers) + if connections: self.connections = ConnectionsController(self.admin_url, self.client_session) @@ -68,6 +78,12 @@ def __init__( self.server = ServerController(self.admin_url, self.client_session) self.oob = OOBController(self.admin_url, self.client_session) + if multitenant: + self.multitenant = MultitenancyController(self.admin_url, self.client_session) + + if mediation: + self.mediation = MediationController(self.admin_url, self.client_session) + if issuer: self.schema = SchemaController(self.admin_url, self.client_session) self.wallet = WalletController(self.admin_url, self.client_session) @@ -77,12 +93,55 @@ def __init__( if action_menu: self.action_menu = ActionMenuController(self.admin_url, self.client_session) + if revocations: self.revocations = RevocationController( self.admin_url, self.client_session ) + # TODO: Determine whether we really want to essentially create a whole new ClientSession object as done below + # Ideally we'd just update the existing session along the lines of self.client_session(headers) + # That does not work, though because it is not callable and updating cannot be achieved reliably + # because headers can be of different type + # from https://docs.aiohttp.org/en/stable/client_reference.html : + # "May be either iterable of key-value pairs or Mapping (e.g. dict, CIMultiDict)." + # So for now let's create a new ClientSession and use all the instances current attributes + # to update every attr using ClientSession + def update_tennant_jwt(self, tennant_jwt): + self.tennant_jwt = tennant_jwt + headers = {'Authorization': 'Bearer ' + tennant_jwt, 'content-type': "application/json"} + self.client_session: ClientSession = ClientSession(headers=headers) + + if self.connections: + self.connections = ConnectionsController(self.admin_url, self.client_session) + + if self.messaging: + self.messaging = MessagingController(self.admin_url, self.client_session) + + if self.multitenant: + self.multitenant = MultitenancyController(self.admin_url, self.client_session) + + if self.mediation: + self.mediation = MediationController(self.admin_url, self.client_session) + + if self.issuer: + self.schema = SchemaController(self.admin_url, self.client_session) + self.wallet = WalletController(self.admin_url, self.client_session) + self.definitions = DefinitionsController(self.admin_url, self.client_session) + self.issuer = IssuerController(self.admin_url, self.client_session, self.connections, + self.wallet, self.definitions) + + if self.action_menu: + self.action_menu = ActionMenuController(self.admin_url, self.client_session) + + if self.revocations: + self.revocations = RevocationController( + self.admin_url, + self.client_session + ) + + def register_listeners(self, listeners, defaults=True): if defaults: if self.connections: diff --git a/libs/aries-basic-controller/aries_basic_controller/controllers/mediation.py b/libs/aries-basic-controller/aries_basic_controller/controllers/mediation.py index eca1ca17..1815e7ef 100644 --- a/libs/aries-basic-controller/aries_basic_controller/controllers/mediation.py +++ b/libs/aries-basic-controller/aries_basic_controller/controllers/mediation.py @@ -20,7 +20,7 @@ def default_handler(self, payload): # Get default mediator async def get_default_mediator(self): - return await self.admin_POST(f"{self.base_url}/default-mediator") + return await self.admin_GET(f"{self.base_url}/default-mediator") # Clear default mediator @@ -57,12 +57,12 @@ async def send_keylist_update(self, request, mediation_id: str): # Request mediation from connection - async def request_mediation(self, request, conn_id: str): + async def request_mediation(self, conn_id: str, request: {} = {}): return await self.admin_POST(f"{self.base_url}/request/{conn_id}", json_data=request) # Query mediation requests, returns list of all mediation records - async def get_mediation_records(self, mediator_terms: [str], recipient_terms: [str], state: str = None, conn_id: str = None): + async def get_mediation_records(self, mediator_terms: [str] = None, recipient_terms: [str] = None, state: str = None, conn_id: str = None): params = {} if conn_id: params["conn_id"] = conn_id @@ -96,6 +96,6 @@ async def grant_mediation_request_by_id(self, mediation_id: str): return await self.admin_POST(f"{self.base_url}/requests/{mediation_id}/grant") - # Grant received mediation request + # Set default mediator async def set_default_mediator(self, mediation_id: str): - return await self.admin_PUT(f"{self.base_url}/requests/{mediation_id}/default-mediator") + return await self.admin_PUT(f"{self.base_url}/{mediation_id}/default-mediator") diff --git a/libs/aries-basic-controller/aries_basic_controller/controllers/multitenant.py b/libs/aries-basic-controller/aries_basic_controller/controllers/multitenant.py index 9301e2e5..32b95f99 100644 --- a/libs/aries-basic-controller/aries_basic_controller/controllers/multitenant.py +++ b/libs/aries-basic-controller/aries_basic_controller/controllers/multitenant.py @@ -34,12 +34,12 @@ async def update_subwallet_by_id(self, request, wallet_id: str): # Remove a subwallet - async def remove_subwallet_by_id(self, request, wallet_id: str): + async def remove_subwallet_by_id(self, wallet_id: str, request=None): return await self.admin_POST(f"{self.base_url}/wallet/{wallet_id}/remove", json_data=request) # Get auth token for a subwallet - async def get_subwallet_authtoken_by_id(self, request, wallet_id: str): + async def get_subwallet_authtoken_by_id(self, wallet_id: str, request=None): return await self.admin_POST(f"{self.base_url}/wallet/{wallet_id}/token", json_data=request) diff --git a/libs/aries-basic-controller/requirements.txt b/libs/aries-basic-controller/requirements.txt index 9d2ca32c..8183e393 100644 --- a/libs/aries-basic-controller/requirements.txt +++ b/libs/aries-basic-controller/requirements.txt @@ -1,4 +1,4 @@ -aiohttp~=3.6.2 +aiohttp~=3.7.3 asyncio prompt_toolkit pygments diff --git a/projects/aries-fl/Dockerfile.hospitalcontroller b/projects/aries-fl/Dockerfile.hospitalcontroller index e06bf083..dee3f8f3 100644 --- a/projects/aries-fl/Dockerfile.hospitalcontroller +++ b/projects/aries-fl/Dockerfile.hospitalcontroller @@ -7,8 +7,8 @@ ENV env_jupyter_port=jupyter_port # Setup workspace environment RUN apt-get update && apt-get install -y gcc -# RUN conda install jupyter notebook=5.7.8 -RUN conda install jupyter notebook=6.1.5 +# RUN conda install -c conda-forge jupyterlab==3.0.11 +RUN conda install -c conda-forge jupyterlab==3.0.11 ADD projects/aries-fl/requirements.txt . diff --git a/projects/aries-fl/Dockerfile.ppmlcontroller b/projects/aries-fl/Dockerfile.ppmlcontroller index e06bf083..dee3f8f3 100644 --- a/projects/aries-fl/Dockerfile.ppmlcontroller +++ b/projects/aries-fl/Dockerfile.ppmlcontroller @@ -7,8 +7,8 @@ ENV env_jupyter_port=jupyter_port # Setup workspace environment RUN apt-get update && apt-get install -y gcc -# RUN conda install jupyter notebook=5.7.8 -RUN conda install jupyter notebook=6.1.5 +# RUN conda install -c conda-forge jupyterlab==3.0.11 +RUN conda install -c conda-forge jupyterlab==3.0.11 ADD projects/aries-fl/requirements.txt . diff --git a/projects/aries-fl/notebooks/researcher/Researcher.ipynb b/projects/aries-fl/notebooks/researcher/Researcher.ipynb index bcd447e5..3ddfdd10 100644 --- a/projects/aries-fl/notebooks/researcher/Researcher.ipynb +++ b/projects/aries-fl/notebooks/researcher/Researcher.ipynb @@ -103,7 +103,7 @@ "source": [ "## Request Authorised Research Credential\n", "\n", - "Copy invite from the [Health Research Authority Notebook](http://localhost:8889/notebooks/Health%20Research%20Regulator.ipynb)" + "Copy invite from the [Health Research Authority Notebook](http://localhost:8889/lab/tree/Health%20Research%20Regulator.ipynb)" ] }, { diff --git a/projects/doctors-in-training/notebooks/hee/Part 4.1 - Initialising the HEE Agent.ipynb b/projects/doctors-in-training/notebooks/hee/Part 4.1 - Initialising the HEE Agent.ipynb index a93f7d89..514c78dc 100644 --- a/projects/doctors-in-training/notebooks/hee/Part 4.1 - Initialising the HEE Agent.ipynb +++ b/projects/doctors-in-training/notebooks/hee/Part 4.1 - Initialising the HEE Agent.ipynb @@ -6,7 +6,7 @@ "source": [ "# Initialising the Health Education England Agent\n", "\n", - "## If you wish to start at the beginning of the Doctor's in Training flow go [here](http://localhost:8889/notebooks/Part%201%20-%20Getting%20Started.ipynb).\n", + "## If you wish to start at the beginning of the Doctor's in Training flow go [here](http://localhost:8889/lab/tree/Part%201%20-%20Getting%20Started.ipynb).\n", "\n", "In this notebook you will register the DID of the HEE agent on the Sovrin StagingNet and write the credential definitions that Health Education England is responsible for issuing.\n", "\n", diff --git a/projects/doctors-in-training/notebooks/hee/Part 4.2 - Interact with Doctor in Training.ipynb b/projects/doctors-in-training/notebooks/hee/Part 4.2 - Interact with Doctor in Training.ipynb index 725faa82..d97cf54d 100644 --- a/projects/doctors-in-training/notebooks/hee/Part 4.2 - Interact with Doctor in Training.ipynb +++ b/projects/doctors-in-training/notebooks/hee/Part 4.2 - Interact with Doctor in Training.ipynb @@ -11,7 +11,7 @@ "\n", "## Before running through this notebook you should run through the following notebook - [Part 4.1](http://localhost:8891/notebooks/Part%204.1%20-%20Initialising%20the%20HEE%20Agent.ipynb).\n", "\n", - "## Or alternatively you can start at the beginning of the Doctors in Training flow [here](http://localhost:8889/notebooks/Part%201%20-%20Getting%20Started.ipynb).\n", + "## Or alternatively you can start at the beginning of the Doctors in Training flow [here](http://localhost:8889/lab/tree/Part%201%20-%20Getting%20Started.ipynb).\n", "\n" ] }, diff --git a/projects/doctors-in-training/notebooks/lead-employer/Part 5.1 - Initialising the Lead Employer Agent.ipynb b/projects/doctors-in-training/notebooks/lead-employer/Part 5.1 - Initialising the Lead Employer Agent.ipynb index 1e1e1474..4c2bb656 100644 --- a/projects/doctors-in-training/notebooks/lead-employer/Part 5.1 - Initialising the Lead Employer Agent.ipynb +++ b/projects/doctors-in-training/notebooks/lead-employer/Part 5.1 - Initialising the Lead Employer Agent.ipynb @@ -6,7 +6,7 @@ "source": [ "# Initialising the Lead Employer Agent\n", "\n", - "## If you wish to start at the beginning of the Doctor's in Training flow go [here](http://localhost:8889/notebooks/Part%201%20-%20Getting%20Started.ipynb).\n", + "## If you wish to start at the beginning of the Doctor's in Training flow go [here](http://localhost:8889/lab/tree/Part%201%20-%20Getting%20Started.ipynb).\n", "\n", "In this notebook you will register the DID of the Lead Employer agent on the Sovrin StagingNet and write the credential definitions for the schema the Lead Employer is responsible for issuing.\n", "\n", diff --git a/projects/doctors-in-training/notebooks/lead-employer/Part 5.2 - Onboard a Doctor in Training.ipynb b/projects/doctors-in-training/notebooks/lead-employer/Part 5.2 - Onboard a Doctor in Training.ipynb index 5a241790..c00ccfc9 100644 --- a/projects/doctors-in-training/notebooks/lead-employer/Part 5.2 - Onboard a Doctor in Training.ipynb +++ b/projects/doctors-in-training/notebooks/lead-employer/Part 5.2 - Onboard a Doctor in Training.ipynb @@ -13,7 +13,7 @@ "\n", "## Before running through this notebook you should run through the following notebook - [Part 5.1](http://localhost:8892/notebooks/Part%205.1%20-%20Initialising%20the%20Lead%20Employer%20Agent.ipynb).\n", "\n", - "## Or alternatively you can start at the beginning of the Doctors in Training flow [here](http://localhost:8889/notebooks/Part%201%20-%20Getting%20Started.ipynb).\n", + "## Or alternatively you can start at the beginning of the Doctors in Training flow [here](http://localhost:8889/lab/tree/Part%201%20-%20Getting%20Started.ipynb).\n", "\n" ] }, diff --git a/scripts/jupyter-entrypoint.sh b/scripts/jupyter-entrypoint.sh index bd3aaa61..59279d48 100644 --- a/scripts/jupyter-entrypoint.sh +++ b/scripts/jupyter-entrypoint.sh @@ -10,4 +10,4 @@ fi cd $WORKSPACE echo "HELLO THIS IS PORT $1" -jupyter notebook --ip=0.0.0.0 --port="$1" --allow-root +jupyter lab --ip=0.0.0.0 --port="$1" --allow-root diff --git a/scripts/startup.sh b/scripts/startup.sh index d600ab73..1aa3c195 100644 --- a/scripts/startup.sh +++ b/scripts/startup.sh @@ -1 +1,6 @@ -python3 ./scripts/parse_yml_env_variables.py -c ./configuration/aries-args-advanced.yaml;aca-py start --arg-file /tmp/agent_conf.yml \ No newline at end of file +if [[ -z "${MULTITENANT_TUTORIAL}" ]]; then + echo $MULTITENANT_TUTORIAL + python3 ./scripts/parse_yml_env_variables.py -c ./configuration/aries-args-multitenant.yaml;aca-py start --arg-file /tmp/agent_conf.yml +else + python3 ./scripts/parse_yml_env_variables.py -c ./configuration/aries-args-advanced.yaml;aca-py start --arg-file /tmp/agent_conf.yml +fi \ No newline at end of file diff --git a/tutorials/aries-basic-controller/README.md b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/README.md similarity index 100% rename from tutorials/aries-basic-controller/README.md rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/README.md diff --git a/tutorials/aries-basic-controller/docker-compose.yml b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/docker-compose.yml similarity index 100% rename from tutorials/aries-basic-controller/docker-compose.yml rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/docker-compose.yml diff --git a/tutorials/aries-basic-controller/manage b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/manage similarity index 100% rename from tutorials/aries-basic-controller/manage rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/manage diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 1 - Self-Sovereign Identity.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 1 - Self-Sovereign Identity.ipynb similarity index 97% rename from tutorials/aries-basic-controller/notebooks/alice/Part 1 - Self-Sovereign Identity.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 1 - Self-Sovereign Identity.ipynb index 28dfd92a..3a2af91b 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 1 - Self-Sovereign Identity.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 1 - Self-Sovereign Identity.ipynb @@ -19,7 +19,7 @@ "By the end of this tutorial we hope you will have a strong grounding in the capabilities of SSI and the ability to use them in your own applications by leveraging the library we have developed. \n", "\n", "If you want to dive straight into the code start here:\n", - "* [Part 2 - Aries Basic Controller](http://localhost:8888/notebooks/Part%202%20-%20Aries%20Basic%20Controller.ipynb)" + "* [Part 2 - Aries Basic Controller](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%202%20-%20Aries%20Basic%20Controller.ipynb)" ] }, { @@ -147,9 +147,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 2 - Aries Basic Controller.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 2 - Aries Basic Controller.ipynb similarity index 90% rename from tutorials/aries-basic-controller/notebooks/alice/Part 2 - Aries Basic Controller.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 2 - Aries Basic Controller.ipynb index 493ff91e..a727c7ef 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 2 - Aries Basic Controller.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 2 - Aries Basic Controller.ipynb @@ -136,7 +136,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -240,21 +240,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'agent_controller' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 10\u001b[0m }\n\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0magent_controller\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_listener\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msome_listener2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'agent_controller' is not defined" - ] - } - ], + "outputs": [], "source": [ "def some_handler2(payload):\n", "\n", @@ -334,11 +322,11 @@ "\n", "These tutorials are largely designed to be stand alone, but if this is your first time exploring this tutorial it is recommended that you follow the order defined.\n", "\n", - "* Part 3: Establishing a Connection: [Alice](http://localhost:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb), [Bob](http://localhost:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", - "* Part 4: Credential Schema and Definitions: [Alice only](http://localhost:8888/notebooks/Part%204%20-%20Credential%20Schema%20and%20Definitions.ipynb)\n", - "* Part 5: Issue Credential: [Alice](http://localhost:8888/notebooks/Part%205%20-%20Issue%20Credential.ipynb), [Bob](http://localhost:8889/notebooks/Part%205%20-%20Issue%20Credential.ipynb)\n", - "* Part 6: Present Proof: [Alice](http://localhost:8888/notebooks/Part%206%20-%20Present%20Proof.ipynb), [Bob](http://localhost:8889/notebooks/Part%206%20-%20Present%20Proof.ipynb) (must have completed Part 5)\n", - "* Part 7: Basic Messaging: [Alice](http://localhost:8888/notebooks/Part%207%20-%20Basic%20Message.ipynb), [Bob](http://localhost:8889/notebooks/Part%207%20-%20Basic%20Message.ipynb)\n" + "* Part 3: Establishing a Connection: [Alice](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb), [Bob](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", + "* Part 4: Credential Schema and Definitions: [Alice only](http://localhost:8888/lab/tree/2%20Credentials/Part%201%20-%20Credential%20Schema%20and%20Definitions.ipynb)\n", + "* Part 5: Issue Credential: [Alice](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb), [Bob](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb)\n", + "* Part 6: Present Proof: [Alice](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb), [Bob](http://localhost:8889/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) (must have completed Part 5)\n", + "* Part 7: Basic Messaging: [Alice](http://localhost:8888/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb), [Bob](http://localhost:8889/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb)\n" ] } ], @@ -358,7 +346,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 3 - Establishing a Connection.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb similarity index 78% rename from tutorials/aries-basic-controller/notebooks/alice/Part 3 - Establishing a Connection.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb index 10fee98f..ad70ad77 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 3 - Establishing a Connection.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb @@ -23,7 +23,7 @@ "4. Use the controller to create an invitation from our agent\n", "5. Copy the invitation output from 4 and move over to Bob's notebook\n", "\n", - "Carry on in [Bob's notebook](http://localhost:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", + "Carry on in [Bob's notebook](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", " \n", "11. Accept Request for Connection\n", "12. Send Trust Ping to Bob\n", @@ -45,17 +45,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "IPython autoawait is `on`, and set to use `asyncio`\n" - ] - } - ], + "outputs": [], "source": [ "%autoawait\n", "import time\n", @@ -77,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -112,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -149,48 +141,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connection ID 0b42a8c8-d25f-4a2e-9ba1-d7aa74ebb9d4\n", - "Invitation\n", - "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '0f6250e8-f996-4a33-b798-9d5218cfd264', 'label': 'Alice', 'recipientKeys': ['5oBk3KC87hLHjbU14oetYAm2CRrxZTtiHituhyCCvEcs'], 'serviceEndpoint': 'http://172.17.0.1:8020'}\n", - "Connection Handler Called\n", - "Connection 0b42a8c8-d25f-4a2e-9ba1-d7aa74ebb9d4 in State invitation\n", - "Connection Handler 2 Called\n", - "Connection 0b42a8c8-d25f-4a2e-9ba1-d7aa74ebb9d4 in State invitation\n" - ] - } - ], - "source": [ - "# Create Invitation\n", - "invite = await agent_controller.connections.create_invitation()\n", - "connection_id = invite[\"connection_id\"]\n", - "invite_message = invite['invitation']\n", - "print(\"Connection ID\", connection_id)\n", - "print(\"Invitation\")\n", - "print(invite_message)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connection ID b2bed5c0-09e5-42e8-afaf-93304b84c5a6\n", - "Invitation\n", - "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '66d5629d-5ea6-4a46-8f19-a5f0b1758a58', 'label': 'Alice', 'recipientKeys': ['31qfD7BQ3i2sAQzNR3BZocynrc7vgDwWYAQ7xXcYyKtk'], 'serviceEndpoint': 'http://172.17.0.1:8020'}\n" - ] - } - ], + "outputs": [], "source": [ "# Create Invitation\n", "invite = await agent_controller.connections.create_invitation()\n", @@ -209,7 +162,7 @@ } }, "source": [ - "### 5. Copy the invitation output from 4 and move over to the [Bob notebook](http://localhost:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb) " + "### 5. Copy the invitation output from 4 and move over to the [Bob notebook](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb) " ] }, { @@ -345,7 +298,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" }, "pycharm": { "stem_cell": { diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 4 - Credential Schema and Definitions.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 1 - Credential Schema and Definitions.ipynb similarity index 93% rename from tutorials/aries-basic-controller/notebooks/alice/Part 4 - Credential Schema and Definitions.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 1 - Credential Schema and Definitions.ipynb index 1b20e880..63d98319 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 4 - Credential Schema and Definitions.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 1 - Credential Schema and Definitions.ipynb @@ -2,9 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "# Credential Schema and Definitions\n", "\n", @@ -28,9 +26,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], "source": [ "%autoawait\n", "import time\n", @@ -62,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -85,9 +91,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PQRXDxdGqQGSZ8z69p4xZP:2:alice_test_schema:0.0.1\n" + ] + } + ], "source": [ "response = await agent_controller.schema.write_schema(schema_name, attributes, schema_version)\n", "schema_id = response[\"schema_id\"]\n", @@ -100,7 +114,7 @@ "source": [ "## 4. Write a credential definition for this schema\n", "\n", - "It is only possible to write a single credential definition per schema per public DID to the ledger. Howtryever, [Bob](http://localhost:8889) is also able to write a credential definition for the schema if he wants. Feel free to copy the schema_id across and give it a try in a new notebook.\n", + "It is only possible to write a single credential definition per schema per public DID to the ledger. However, [Bob](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credentials.ipynb) is also able to write a credential definition for the schema if he wants. Feel free to copy the schema_id across and give it a try in a new notebook.\n", "\n", "#### Arguments\n", "* schema_id - you need to pass in one of these and it must exist on the ledger\n", @@ -336,7 +350,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" }, "pycharm": { "stem_cell": { @@ -349,5 +363,5 @@ } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 5 - Issue Credential.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 2 - Issue Credential.ipynb similarity index 94% rename from tutorials/aries-basic-controller/notebooks/alice/Part 5 - Issue Credential.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 2 - Issue Credential.ipynb index dfdc3255..40c71591 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 5 - Issue Credential.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 2 - Issue Credential.ipynb @@ -9,7 +9,7 @@ "# Issue Credential - Alice\n", "## Role: Issuer\n", "\n", - "### This notebook walks through how to issue a credential across a previously established connection. It should be run in parallel with the [Holder notebook](http://127.0.0.1:8889/notebooks/Part%205%20-%20Issue%20Credential.ipynb) controlling the agent receiving the credential.\n", + "### This notebook walks through how to issue a credential across a previously established connection. It should be run in parallel with the [Holder notebook](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) controlling the agent receiving the credential.\n", "\n", "If unfamiliar with the protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0036-issue-credential)\n" ] @@ -89,8 +89,8 @@ "\n", "**Note: An active connection is required, this should have been established on start up through the python script create_connection.py in the setup folder. If not it is possible to run through the did-exchange tutorial to create one between Alice and Bob**\n", "\n", - "* [Alice](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", - "* [Bob](http://127.0.0.1:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)" + "* [Alice](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", + "* [Bob](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)" ] }, { @@ -146,7 +146,7 @@ "source": [ "## 5. Write a Credential Definition to the Ledger\n", "\n", - "More details in the [definitions notebook](http://localhost:8888/notebooks/definitions_api.ipynb)\n", + "More details in the [definitions notebook](http://localhost:8888/lab/tree/2%20Credentials/Part%201%20-%20Credential%20Schema%20and%20Definitions.ipynb)\n", "\n", "**Note: Again this can only be done once per issuer, per schema_id.**" ] @@ -199,7 +199,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Continue to the [Holder Notebook](http://localhost:8889/notebooks/holder.ipynb)\n", + "## 7. Continue to the [Holder Notebook](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb)\n", "\n", "It works best if this notebook is already initialised and has listeners registered.\n" ] @@ -372,7 +372,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 6 - Present Proof.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 3 - Present Proof.ipynb similarity index 93% rename from tutorials/aries-basic-controller/notebooks/alice/Part 6 - Present Proof.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 3 - Present Proof.ipynb index 21972fcf..19333962 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 6 - Present Proof.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/2 Credentials/Part 3 - Present Proof.ipynb @@ -9,7 +9,7 @@ "\n", "## This notebook works through the present proof protocol from the Verifier's perspective, it should be run alongside the prover notebook from Bob's perspective. \n", "\n", - "## Before running through these two notebooks you should have run through Part 5 - Issue Credential. This can be found in these two notebooks: [Alice](http://localhost:8888/notebooks/Part%205%20-%20Issue%20Credential.ipynb), [Bob](http://localhost:8889/notebooks/Part%205%20-%20Issue%20Credential.ipynb)\n", + "## Before running through these two notebooks you should have run through Part 2 of the Credentials tutorial - Issue Credential. This can be found in these two notebooks: [Alice](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Issue%20Credential.ipynb), [Bob](http://localhost:8889/lab/tree/2%20Credentials/Part%203%20-%20Issue%20Credential.ipynb)\n", "\n", "\n", "If unfamiliar with the present-proof protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof)\n" @@ -102,8 +102,8 @@ "\n", "**Note: An active connection is required, this should have been established on start up through the python script create_connection.py in the setup folder. If not it is possible to run through the did-exchange tutorial to create one between Alice and Bob**\n", "\n", - "* [Alice](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", - "* [Bob](http://127.0.0.1:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)" + "* [Alice](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", + "* [Bob](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)" ] }, { @@ -139,7 +139,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Continue in [prover](http://127.0.0.1:8889/notebooks/Part%206%20-%20Present%20Proof.ipynb) Notebook" + "## 4. Continue in [prover](http://localhost:8889/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) Notebook" ] }, { @@ -389,9 +389,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 7 - Basic Message.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/3 Messages/Part 1 - Basic Message.ipynb similarity index 94% rename from tutorials/aries-basic-controller/notebooks/alice/Part 7 - Basic Message.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/3 Messages/Part 1 - Basic Message.ipynb index 19747568..aedef4c2 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 7 - Basic Message.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/3 Messages/Part 1 - Basic Message.ipynb @@ -6,7 +6,7 @@ "source": [ "# Basic Message - Alice\n", "\n", - "**Should be run alongside [Bob](http://localhost:8889/notebooks/Part%207%20-%20Basic%20Message.ipynb)**" + "**Should be run alongside [Bob](http://localhost:8889/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb)**" ] }, { @@ -132,7 +132,7 @@ "\n", "See [aries-rfc](https://github.com/hyperledger/aries-rfcs/tree/master/features/0095-basic-message)\n", "\n", - "You can send as messages as you want, if you are running the [basic-message tutorial on Bob's notebook](http://localhost:8889/notebooks/Part%207%20-%20Basic%20Message.ipynb) these will be received and printed by the message handler. You may have to run a code block to see the output." + "You can send as messages as you want, if you are running the [basic-message tutorial on Bob's notebook](http://localhost:8889/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb) these will be received and printed by the message handler. You may have to run a code block to see the output." ] }, { @@ -190,7 +190,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 9 - Action Menu.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 1 - Action Menu.ipynb similarity index 94% rename from tutorials/aries-basic-controller/notebooks/alice/Part 9 - Action Menu.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 1 - Action Menu.ipynb index 7c88e8da..0225b74d 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 9 - Action Menu.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 1 - Action Menu.ipynb @@ -10,7 +10,7 @@ "\n", "In this notebook we'll be going through the Action Menu Protocol. For details on the protocol, please refer to the [RFC](https://github.com/hyperledger/aries-rfcs/tree/master/features/0509-action-menu).\n", "\n", - "A requirement for this protocol to work is an active DIDComm communication channel between Alice and Bob. To achieve that, you should have completed the [Establishing a Connection](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial." + "A requirement for this protocol to work is an active DIDComm communication channel between Alice and Bob. To achieve that, you should have completed the [Establishing a Connection](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial." ] }, { @@ -96,7 +96,7 @@ "\n", "An active connection between Alice and Bob is required. You can either:\n", "\n", - "- complete the [Establishing a Connection](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial, or\n", + "- complete the [Establishing a Connection](http://127.0.0.1:8888/notebooks/1%20Basic%20%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial, or\n", "- running the python script `create_connection.py` in the setup folder" ] }, @@ -141,7 +141,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 5. Continue with step 6 of [Bob's notebook](http://127.0.0.1:8889/notebooks/Part%209%20-%20Action%20Menu.ipynb)" + "## 5. Continue with step 6 of [Bob's notebook](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%201%20-%20Action%20Menu.ipynb)" ] }, { @@ -271,7 +271,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 10 - Revocation.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 2 - Revocation.ipynb similarity index 93% rename from tutorials/aries-basic-controller/notebooks/alice/Part 10 - Revocation.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 2 - Revocation.ipynb index 1f5f04a2..92d427a5 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 10 - Revocation.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 2 - Revocation.ipynb @@ -10,7 +10,7 @@ "\n", "There will be two parts to this notebook:\n", "\n", - "- Part 1: Alice issues a revocable credential to [Bob](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb). Bob will store this credential and present proof of it back to Alice, where she will verify the presentation.\n", + "- Part 1: Alice issues a revocable credential to [Bob](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb). Bob will store this credential and present proof of it back to Alice, where she will verify the presentation.\n", "- Part 2: Alice revokes the credential issued to Bob in Part 1 and requests another proof from Bob. This second presentation from Bob will fail to verify.\n", "\n", "For details on how revocation works on Hyperledger Indy, you can read more [here](https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation)." @@ -114,8 +114,8 @@ "\n", "**Note: An active connection is required, this should have been established on start up through the python script `create_connection.py` in the setup folder. If not it is possible to run through the did-exchange tutorial to create one between Alice and Bob**\n", "\n", - "* [Alice](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", - "* [Bob](http://127.0.0.1:8889/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb)" + "* [Alice](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", + "* [Bob](http://localhost:8889/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)" ] }, { @@ -218,7 +218,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Continue in [Bob's Notebook](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 7. Continue in [Bob's Notebook](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "You need to initialise the controller and listen to webhooks so you can track the messages Bob's agent receives." ] @@ -249,7 +249,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 10. Continue in [Bob's Notebook ](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 10. Continue in [Bob's Notebook ](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Here you will request, store and then present the credential." ] @@ -323,7 +323,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 15. Continue in [Bob's Notebook ](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 15. Continue in [Bob's Notebook ](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Bob will respond to the presentation request" ] @@ -391,7 +391,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 19. Continue in [Bob's Notebook ](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 19. Continue in [Bob's Notebook ](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Bob will check his credential to find that it is now revoked." ] @@ -463,7 +463,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 21. Continue in [Bob's Notebook ](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 21. Continue in [Bob's Notebook ](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Bob will respond to the presentation request" ] @@ -549,7 +549,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/alice/Part 8 - Out of Band Protocol.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb similarity index 97% rename from tutorials/aries-basic-controller/notebooks/alice/Part 8 - Out of Band Protocol.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb index 1b81fe18..f9a9bac9 100644 --- a/tutorials/aries-basic-controller/notebooks/alice/Part 8 - Out of Band Protocol.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb @@ -101,7 +101,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Copy Invitation Across to Bob's [Notebook](http://localhost:8889/notebooks/Part%208%20-%20Out%20of%20Band%20Protocol.ipynb)" + "## 4. Copy Invitation Across to Bob's [Notebook](http://localhost:8889/lab/tree/4%20Advanced%20Concepts/Part%203%20-%20Out%20of%20Band%20Protocol.ipynb)" ] }, { diff --git a/tutorials/aries-basic-controller/notebooks/alice/misc/connections_api.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/connections_api.ipynb similarity index 100% rename from tutorials/aries-basic-controller/notebooks/alice/misc/connections_api.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/connections_api.ipynb diff --git a/tutorials/aries-basic-controller/notebooks/alice/misc/ledger-api.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/ledger-api.ipynb similarity index 100% rename from tutorials/aries-basic-controller/notebooks/alice/misc/ledger-api.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/ledger-api.ipynb diff --git a/tutorials/aries-basic-controller/notebooks/alice/misc/server-api.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/server-api.ipynb similarity index 100% rename from tutorials/aries-basic-controller/notebooks/alice/misc/server-api.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/server-api.ipynb diff --git a/tutorials/aries-basic-controller/notebooks/alice/misc/wallet_api.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/wallet_api.ipynb similarity index 100% rename from tutorials/aries-basic-controller/notebooks/alice/misc/wallet_api.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/wallet_api.ipynb diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 3 - Establishing a Connection.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb similarity index 91% rename from tutorials/aries-basic-controller/notebooks/bob/Part 3 - Establishing a Connection.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb index 460fba63..ce6bf935 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 3 - Establishing a Connection.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/1 Basic Concepts and 1st Connection/Part 3 - Establishing a Connection.ipynb @@ -11,11 +11,11 @@ "# Establishing a Connection - Bob\n", "## Role: Invitee\n", "\n", - "If you want to start at Part 1 of this tutorial series go [here](http://localhost:8888/notebooks/Part%201%20-%20Self-Sovereign%20Identity.ipynb).\n", + "If you want to start at Part 1 of this tutorial series go [here](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%201%20-%20Self-Sovereign%20Identity.ipynb).\n", "\n", "In this notebook we'll be responding to an invitation from Alice through our Agents. This notebook has the following phases:\n", "\n", - "Begin with the Alice notebook ([localhost:8888](\"http://localhost:8888\"))\n", + "Begin with the [Alice notebook](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb)\n", "\n", "\n", "6. Instatiate the controller for our Agent\n", @@ -110,7 +110,7 @@ "outputs": [], "source": [ "#Paste in invitation from Alice agent\n", - "invitation = {'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '67f3c865-995e-436e-b458-ea8234c714ce', 'recipientKeys': ['9RzVRHXZoEkyz3MLQ3pn4gCM7eHi7xhm7wigbTh6SRMz'], 'label': 'Alice', 'serviceEndpoint': 'http://172.17.0.1:8020'}\n" + "invitation = {'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': 'e755dc99-9f61-4ab8-a59c-3f449a0287c3', 'label': 'Alice', 'recipientKeys': ['7rh9AtsmZg2QXVgePCUgsGUSn4VVAcxeBeadSMVmwkrb'], 'serviceEndpoint': 'http://172.17.0.1:8020'}" ] }, { @@ -239,7 +239,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" }, "pycharm": { "stem_cell": { diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 5 - Issue Credential.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 2 - Issue Credential.ipynb similarity index 96% rename from tutorials/aries-basic-controller/notebooks/bob/Part 5 - Issue Credential.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 2 - Issue Credential.ipynb index 784c002b..0f1320cd 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 5 - Issue Credential.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 2 - Issue Credential.ipynb @@ -7,7 +7,7 @@ "# Issue Credential - Bob\n", "## Role: Holder\n", "\n", - "### This tutorial runs through the issuer-api from the perspective of a holder. It should be run alongside the [issuer notebook](http://localhost:8888/notebooks/Part%205%20-%20Issue%20Credential.ipynb) from Alice's perspective. \n", + "### This tutorial runs through the issuer-api from the perspective of a holder. It should be run alongside the [issuer notebook](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) from Alice's perspective. \n", "\n", "\n", "If unfamiliar with the protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0036-issue-credential)" @@ -85,7 +85,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 9. Continue in the [Issuer](http://localhost:8888/notebooks/issuer.ipynb) Notebook\n", + "## 9. Continue in the [Issuer](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) Notebook\n", "\n", "This sends the credential to the holder Bob and should be recognisable through the print statements from the above handler." ] diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 6 - Present Proof.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 3 - Present Proof.ipynb similarity index 89% rename from tutorials/aries-basic-controller/notebooks/bob/Part 6 - Present Proof.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 3 - Present Proof.ipynb index c8ab1b3a..f233746b 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 6 - Present Proof.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/2 Credentials/Part 3 - Present Proof.ipynb @@ -7,9 +7,9 @@ "# Present Proof\n", "## Role: Prover\n", "\n", - "## This notebook works through the present proof protocol from the provers's perspective, it should be run alongside the [verifier](http://127.0.0.1:8888/notebooks/Part%206%20-%20Present%20Proof.ipynb) notebook from Alices's perspective. \n", + "## This notebook works through the present proof protocol from the provers's perspective, it should be run alongside the [verifier](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) notebook from Alices's perspective. \n", "\n", - "## Before running through these two notebooks you should run through the Issuer/Holder flow found in these two notebooks - [issuer](http://127.0.0.1:8888/notebooks/Part%205%20-%20Issue%20Credential.ipynb) [holder](http://127.0.0.1:8889/notebooks/Part%205%20-%20Issue%20Credential.ipynb).\n", + "## Before running through these two notebooks you should run through the Issuer/Holder flow found in these two notebooks - [issuer](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) [holder](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb).\n", "\n", "If unfamiliar with the present-proof protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof)" ] @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Complete steps 1-3 in the [verifier](http://127.0.0.1:8888/notebooks/Part%206%20-%20Present%20Proof.ipynb) notebook first." + "## Complete steps 1-3 in the [verifier](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) notebook first." ] }, { @@ -55,8 +55,8 @@ "source": [ "## 5. Verify that Bob has at least one credential in his wallet\n", "\n", - "This uses the credential api, for more details see this [notebook](http://localhost:8889/notebooks/credential-api.ipynb). \n", - "A credential with the specified credential_id is issued and saved in the issue-credential flow that can be run through by following the [issuer](http://localhost:8888/notebooks/issuer.ipynb) and [holder](http://localhost:8889/notebooks/holder.ipynb) notebooks. (See step 12. store the credential, in the holder notebook for the credential id)\n", + "This uses the credential api, for more details see this [notebook](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb). \n", + "A credential with the specified credential_id is issued and saved in the issue-credential flow that can be run through by following the [issuer](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) and [holder](http://localhost:8889/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) notebooks. (See step 12. store the credential, in the holder notebook for the credential id)\n", "\n", "**This step will error out otherwise**" ] @@ -111,7 +111,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Continue in the [verifier](http://127.0.0.1:8888/notebooks/Part%206%20-%20Present%20Proof.ipynb) notebook.\n", + "## 7. Continue in the [verifier](http://localhost:8888/lab/tree/2%20Credentials/Part%203%20-%20Present%20Proof.ipynb) notebook.\n", "\n", "This sends a proof request to Bob" ] @@ -291,7 +291,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 7 - Basic Message.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/3 Messages/Part 1 - Basic Message.ipynb similarity index 97% rename from tutorials/aries-basic-controller/notebooks/bob/Part 7 - Basic Message.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/3 Messages/Part 1 - Basic Message.ipynb index 4e453a7a..6ddd0736 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 7 - Basic Message.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/3 Messages/Part 1 - Basic Message.ipynb @@ -108,7 +108,7 @@ "\n", "See [aries-rfc](https://github.com/hyperledger/aries-rfcs/tree/master/features/0095-basic-message)\n", "\n", - "You can send as many messages as you want, if you are running the [basic-message tutorial on Alice's notebook](http://localhost:8888/notebooks/Part%207%20-%20Basic%20Message.ipynb) these will be received and printed by the message handler." + "You can send as many messages as you want, if you are running the [basic-message tutorial on Alice's notebook](http://localhost:8888/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb) these will be received and printed by the message handler." ] }, { diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 9 - Action Menu.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 1 - Action Menu.ipynb similarity index 93% rename from tutorials/aries-basic-controller/notebooks/bob/Part 9 - Action Menu.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 1 - Action Menu.ipynb index 1cf9eb6e..2a9ad236 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 9 - Action Menu.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 1 - Action Menu.ipynb @@ -10,7 +10,7 @@ "\n", "In this notebook we'll be going through the Action Menu Protocol. For details on the protocol, please refer to the [RFC](https://github.com/hyperledger/aries-rfcs/tree/master/features/0509-action-menu). Specifically, we'll demonstrate how Bob acts as a Responder to respond to Action Menu related requests from Alice.\n", "\n", - "A requirement for this protocol to work is an active DIDComm communication channel between Alice and Bob. To achieve that, you should have completed the [Establishing a Connection](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial." + "A requirement for this protocol to work is an active DIDComm communication channel between Alice and Bob. To achieve that, you should have completed the [Establishing a Connection](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial." ] }, { @@ -99,7 +99,7 @@ "\n", "An active connection between Alice and Bob is required. You can either:\n", "\n", - "- complete the [Establishing a Connection](http://127.0.0.1:8888/notebooks/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial, or\n", + "- complete the [Establishing a Connection](http://localhost:8888/lab/tree/1%20Basic%20Concepts%20and%201st%20Connection/Part%203%20-%20Establishing%20a%20Connection.ipynb) tutorial, or\n", "- running the python script `create_connection.py` in the setup folder" ] }, @@ -176,7 +176,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Continue with step 8 of [Alice's notebook](http://127.0.0.1:8889/notebooks/Part%209%20-%20Action%20Menu.ipynb)" + "## 7. Continue with step 8 of [Alice's notebook](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%201%20-%20Action%20Menu.ipynb)" ] }, { @@ -222,7 +222,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 10 - Revocation.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 2 - Revocation.ipynb similarity index 94% rename from tutorials/aries-basic-controller/notebooks/bob/Part 10 - Revocation.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 2 - Revocation.ipynb index 978fb40c..48843fa8 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 10 - Revocation.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 2 - Revocation.ipynb @@ -10,7 +10,7 @@ "\n", "There will be two parts to this notebook:\n", "\n", - "- Part 1: Alice issues a revocable credential to [Bob](http://127.0.0.1:8889/notebooks/Part%2010%20-%20Revocation.ipynb). Bob will store this credential and present proof of it back to Alice while it is not revoked, and Alice will verify the presentation.\n", + "- Part 1: Alice issues a revocable credential to [Bob](http://localhost:8889/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb). Bob will store this credential and present proof of it back to Alice while it is not revoked, and Alice will verify the presentation.\n", "- Part 2: Alice then revokes the credential issued to Bob in Part 1 and requests another proof from Bob. This second presentation from Bob will fail to verify.\n", "\n", "For details on how revocation works on Hyperledger Indy, you can read more [here](https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation)." @@ -22,7 +22,7 @@ "source": [ "# Part 1 - Issuing & Proving Non-Revoked Credential\n", "\n", - "## Complete steps 1-6 in [Alice's revocation notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)" + "## Complete steps 1-6 in [Alice's revocation notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)" ] }, { @@ -114,7 +114,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 9. Continue to [Alice's Notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 9. Continue to [Alice's Notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Alice will issue Bob a revocable credential." ] @@ -228,7 +228,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 14. Continue in [Alice's Notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 14. Continue in [Alice's Notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Send a presentation request for a non revoked credential proof.\n" ] @@ -346,7 +346,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 17. Continue in [Alice's Notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 17. Continue in [Alice's Notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Alice will verify the non-revoked credential to be OK." ] @@ -381,7 +381,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 20. Continue in [Alice's Notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 20. Continue in [Alice's Notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Alice will request for a credential proof once again." ] @@ -498,7 +498,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 23. Continue in [Alice's Notebook](http://127.0.0.1:8888/notebooks/Part%2010%20-%20Revocation.ipynb)\n", + "## 23. Continue in [Alice's Notebook](http://localhost:8888/lab/tree/5%20Advanced%20Concepts/Part%202%20-%20Revocation.ipynb)\n", "\n", "Alice will verify the now revoked credential to be NOT OK." ] @@ -546,7 +546,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-basic-controller/notebooks/bob/Part 8 - Out of Band Protocol.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb similarity index 96% rename from tutorials/aries-basic-controller/notebooks/bob/Part 8 - Out of Band Protocol.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb index 30bccdfc..e6a06232 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/Part 8 - Out of Band Protocol.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb @@ -10,7 +10,7 @@ "\n", "The RFC is described [here](https://github.com/hyperledger/aries-rfcs/tree/master/features/0434-outofband)\n", "\n", - "Begin in the [sender notebook](http://localhost:8888/notebooks/Part%208%20-%20Out%20of%20Band%20Protocol.ipynb)." + "Begin in the [sender notebook](http://localhost:8889/lab/tree/4%20Advanced%20Concepts/Part%203%20-%20Out%20of%20Band%20Protocol.ipynb)." ] }, { diff --git a/tutorials/aries-basic-controller/notebooks/bob/credential-api.ipynb b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/APIs/credential-api.ipynb similarity index 90% rename from tutorials/aries-basic-controller/notebooks/bob/credential-api.ipynb rename to tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/APIs/credential-api.ipynb index 48894c18..a5f665cf 100644 --- a/tutorials/aries-basic-controller/notebooks/bob/credential-api.ipynb +++ b/tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/APIs/credential-api.ipynb @@ -6,7 +6,7 @@ "source": [ "# Aries Basic Controller - Credential API\n", "\n", - "## First run through credential issuance flow in the [issuer](http://localhost:8888/notebooks/issuer.ipynb) and [holder](http://localhost:8889/notebooks/holder.ipynb) notebooks so that Bob's agent has a credential stored in it's wallet.\n", + "## First run through credential issuance flow in the [issuer](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) and [holder](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) notebooks so that Bob's agent has a credential stored in it's wallet.\n", "\n", "The credential api enables operations to be performed on the credentials currently stored within an agents wallet." ] @@ -72,7 +72,7 @@ "source": [ "## Get By ID\n", "\n", - "Fetch a credential by it's ID. This is a string value that you gave to the credential when initially storing it. See [holder notbook](http://localhost:8889/notebooks/holder.ipynb) step 12." + "Fetch a credential by it's ID. This is a string value that you gave to the credential when initially storing it. See [holder notbook](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) step 12." ] }, { @@ -159,7 +159,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-stagingnet/README.md b/tutorials/2. Public Ledgers and Mobile Wallets/README.md similarity index 100% rename from tutorials/aries-stagingnet/README.md rename to tutorials/2. Public Ledgers and Mobile Wallets/README.md diff --git a/tutorials/aries-stagingnet/docker-compose.yml b/tutorials/2. Public Ledgers and Mobile Wallets/docker-compose.yml similarity index 100% rename from tutorials/aries-stagingnet/docker-compose.yml rename to tutorials/2. Public Ledgers and Mobile Wallets/docker-compose.yml diff --git a/tutorials/aries-stagingnet/manage b/tutorials/2. Public Ledgers and Mobile Wallets/manage similarity index 100% rename from tutorials/aries-stagingnet/manage rename to tutorials/2. Public Ledgers and Mobile Wallets/manage diff --git a/tutorials/aries-stagingnet/notebooks/issuer/Part 1 - Getting Started.ipynb b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 1 - Getting Started.ipynb similarity index 97% rename from tutorials/aries-stagingnet/notebooks/issuer/Part 1 - Getting Started.ipynb rename to tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 1 - Getting Started.ipynb index a012b1fd..532d1a5a 100644 --- a/tutorials/aries-stagingnet/notebooks/issuer/Part 1 - Getting Started.ipynb +++ b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 1 - Getting Started.ipynb @@ -86,7 +86,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Continue to [Part 2 - Writing a Public DID to the StagingNet](http://localhost:8888/notebooks/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb)" + "## Continue to [Part 2 - Writing a Public DID to the StagingNet](http://localhost:8888/lab/tree/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb)" ] } ], @@ -106,7 +106,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb new file mode 100644 index 00000000..0e95acc6 --- /dev/null +++ b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb @@ -0,0 +1,391 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Writing a Public DID to the Sovrin StagingNet\n", + "\n", + "### This notebook walks through how to connect an Aca-Py agent to a live Indy network. In our example we will be connecting to the Sovrin Stagingnet. To view details about Sovrin Stagingnet transactions please visit [Indyscan](https://indyscan.io/home/SOVRIN_STAGINGNET)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Initialise a controller for Issuer Agent" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], + "source": [ + "%autoawait\n", + "import time\n", + "import asyncio\n", + "from aries_basic_controller.aries_controller import AriesAgentController\n", + " \n", + "WEBHOOK_HOST = \"0.0.0.0\"\n", + "WEBHOOK_BASE = \"\"\n", + "\n", + "WEBHOOK_PORT = 8022\n", + "ADMIN_URL = \"http://issuer-agent:8021\"\n", + "\n", + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Get current public DID\n", + "\n", + "Before being able to write to any indy based ledger, your agent must have a public DID written on the ledger giving it the authority to write to it. As the cell below shows, this agent does not currently have a public DID. So any writes to the ledger will be rejected." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'result': {'did': 'MjFQDoitt2us2JNpLBGnSY', 'verkey': 'CJGSRQvPb9defKY6w8YW8rprDhKiTqkmexp1P8ioUznY', 'posture': 'public'}}\n" + ] + } + ], + "source": [ + "response = await agent_controller.wallet.get_public_did()\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### THIS WILL FAIL" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "ClientResponseError", + "evalue": "400, message='Ledger rejected transaction request: client request invalid: could not authenticate, verkey for MjFQDoitt2us2JNpLBGnSY cannot be found', url=URL('http://issuer-agent:8021/credential-definitions')", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mClientResponseError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mschema_id\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'MGgoJXWbeupKsaHDa7s4fW:2:testabc:0.0.1'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0magent_controller\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefinitions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrite_cred_def\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mschema_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/aries_basic_controller/controllers/definitions.py\u001b[0m in \u001b[0;36mwrite_cred_def\u001b[0;34m(self, schema_id, tag, support_revocation)\u001b[0m\n\u001b[1;32m 22\u001b[0m }\n\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_POST\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{self.base_url}\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m async def search_created(self, schema_id = None, schema_issuer_did=None, schema_name=None,\n", + "\u001b[0;32m/aries_basic_controller/controllers/base.py\u001b[0m in \u001b[0;36madmin_POST\u001b[0;34m(self, path, json_data, text, params, data)\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\" with data: \\n{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepr_json\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjson_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mjson_data\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m )\n\u001b[0;32m---> 95\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"POST\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 96\u001b[0m EVENT_LOGGER.debug(\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\"Response from POST %s received: \\n%s\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/aries_basic_controller/controllers/base.py\u001b[0m in \u001b[0;36madmin_request\u001b[0;34m(self, method, path, json_data, text, params, data)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_url\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjson_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m ) as resp:\n\u001b[0;32m---> 63\u001b[0;31m \u001b[0mresp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_for_status\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 64\u001b[0m \u001b[0mresp_text\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mresp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mresp_text\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/lib/python3.8/site-packages/aiohttp/client_reqrep.py\u001b[0m in \u001b[0;36mraise_for_status\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 998\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreason\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 999\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrelease\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1000\u001b[0;31m raise ClientResponseError(\n\u001b[0m\u001b[1;32m 1001\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest_info\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1002\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mClientResponseError\u001b[0m: 400, message='Ledger rejected transaction request: client request invalid: could not authenticate, verkey for MjFQDoitt2us2JNpLBGnSY cannot be found', url=URL('http://issuer-agent:8021/credential-definitions')" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Warning: Output is not a terminal (fd=None).\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0m\u001b[?7h\u001b[0;34mError during POST /credential-definitions: 400, message='Ledger rejected transaction request: client request invalid: could not authenticate, verkey for MjFQDoitt2us2JNpLBGnSY cannot be found', url=URL('http://issuer-agent:8021/credential-definitions')\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "schema_id = 'MGgoJXWbeupKsaHDa7s4fW:2:testabc:0.0.1'\n", + "response = await agent_controller.definitions.write_cred_def(schema_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Generate a new DID\n", + "\n", + "Before being able to write a DID to the ledger, you must create one using the wallet api. This api returns the identifier (the DID), and the verification key for that DID. A representation of it's public key. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "New DID {'did': 'Chd378GJNMskEvivsowWtL', 'verkey': '7NsZLoUwjas2Jyk5aCGwc6x9eVxaNNjVT1g1LUbin4ui', 'posture': 'wallet_only'}\n" + ] + } + ], + "source": [ + "# generate new DID\n", + "response = await agent_controller.wallet.create_did()\n", + "\n", + "did_object = response['result']\n", + "print(\"New DID\", did_object)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Write DID to Sovrin Stagingnet\n", + "\n", + "Anoyone can write a DID to the Sovrin StagingNet, it is a permissionless ledger. \n", + "\n", + "Visit [Sovrin Selfserve Portal](https://selfserve.sovrin.org) for more information. We have provided an automated process to write DIDs to Stagingnet in the step below." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'statusCode': 200, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': '{\"statusCode\": 200, \"Chd378GJNMskEvivsowWtL\": {\"status\": \"Success\", \"statusCode\": 200, \"reason\": \"Successfully wrote NYM identified by Chd378GJNMskEvivsowWtL to the ledger with role ENDORSER\"}}'}\n", + "200\n" + ] + } + ], + "source": [ + "# write new DID to Sovrin Stagingnet\n", + "import requests\n", + "import json \n", + "\n", + "url = 'https://selfserve.sovrin.org/nym'\n", + "\n", + "payload = {\"network\":\"stagingnet\",\"did\": did_object[\"did\"],\"verkey\":did_object[\"verkey\"],\"paymentaddr\":\"\"}\n", + "\n", + "# Adding empty header as parameters are being sent in payload\n", + "headers = {}\n", + "\n", + "r = requests.post(url, data=json.dumps(payload), headers=headers)\n", + "print(r.json())\n", + "print(r.status_code)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Accepting the Transaction Author Agreement (TAA)\n", + "\n", + "Although the Sovrin StagingNet is permissionless, before DID's have the authority to write to the ledger they must accept something called a transaction author agreement by signing it using the DID they have on the ledger.\n", + "\n", + "As a global public ledger, the Sovrin Ledger and all its participants are subject to privacy and data protection regulations such as the EU General Data Protection Regulation (GDPR). These regulations require that the participants be explicit about responsibilities for Personal Data.\n", + "\n", + "To clarify these responsibilities and provide protection for all parties, the Sovrin Governance Framework Working Group developed an agreement between Transaction Authors and the Sovrin Foundation. The TAA can be found at Sovrin.org. It ensures that users are aware of and consent to the fact that all data written to the Sovrin Ledger cannot be removed, even if the original author of the transaction requests its removal.\n", + "\n", + "The TAA outlines the policies that users must follow when interacting with the Sovrin Ledger. When a user’s client software is preparing a transaction for submission to the network, it must include a demonstration that the user had the opportunity to review the current TAA and accept it. This is done by including some additional fields in the ledger write transaction: \n", + "\n", + "* A hash of the agreement\n", + "* A date when the agreement was accepted, and\n", + "* A string indicating the user interaction that was followed to obtain the acceptance.\n", + "\n", + "The Indy client API used by Sovrin has been extended to allow users to review current and past agreements and to indicate acceptance through an approved user interaction pattern. - source: https://sovrin.org/preparing-for-the-sovrin-transaction-author-agreement/\n", + "\n", + "For more details on TAA please read more at the following links:\n", + "* [Preparing for the Sovrin Transaction Author Agreement](https://sovrin.org/preparing-for-the-sovrin-transaction-author-agreement/)\n", + "* [How the recent approval of the Sovrin Governance Framework v2 affects Transaction Authors\n", + "](https://sovrin.org/how-the-recent-approval-of-the-sovrin-governance-framework-v2-affects-transaction-authors/)\n", + "* [TAA v2](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md)\n", + "* [TAA Acceptance Mechanism List (AML)](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/AML.md)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This will not work until you have accepted the TAA\n", + "response = await agent_controller.wallet.assign_public_did(did_object[\"did\"])\n", + "print(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.ledger.get_taa()\n", + "TAA = response['result']['taa_record']\n", + "TAA['mechanism'] = \"service_agreement\"\n", + "print(TAA)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.ledger.accept_taa(TAA)\n", + "## Will return {} if successful\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Set public DID\n", + "\n", + "Now you are able to assign the DID written to the ledger as public." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.wallet.assign_public_did(did_object[\"did\"])\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Get public DID" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.wallet.get_public_did()\n", + "print(response)\n", + "issuer_nym = response['result']['did']\n", + "print('\\nIssuer public DID:',issuer_nym)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Fetch verkey for public DID\n", + "\n", + "Additionally, we can verify that this DID does actually resolve to the public key material on the ledger." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issuer_verkey = await agent_controller.ledger.get_did_verkey(issuer_nym)\n", + "print(issuer_verkey)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Get public DID endpoint\n", + "\n", + "As well as providing a publically accessible endpoint to contact the DID controller through the agent framework." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issuer_endpoint = await agent_controller.ledger.get_did_endpoint(issuer_nym)\n", + "print(issuer_endpoint)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. End of Tutorial\n", + "\n", + "Be sure to terminate the controller so you can run another tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.terminate()\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Proceed to Part 3 on [Issuer Notebook](http://localhost:8888/lab/tree/Part%203%20-%20Issue%20Credential.ipynb)\n", + "\n", + "Here you will be issued with a credential into your mobile SSI wallet that will be verified in part 4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tutorials/aries-stagingnet/notebooks/issuer/Part 3 - Issue Credential.ipynb b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 3 - Issue Credential.ipynb similarity index 85% rename from tutorials/aries-stagingnet/notebooks/issuer/Part 3 - Issue Credential.ipynb rename to tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 3 - Issue Credential.ipynb index a42e1ad6..cb8963c3 100644 --- a/tutorials/aries-stagingnet/notebooks/issuer/Part 3 - Issue Credential.ipynb +++ b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/Part 3 - Issue Credential.ipynb @@ -10,7 +10,7 @@ "\n", "## This notebook walks through how to issue a credential across a newly established connection with a mobile SSI agent. \n", "\n", - "## Before running through this notebook you should run through the following notebook - [Part 2](http://localhost:8888/notebooks/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb).\n", + "## Before running through this notebook you should run through the following notebook - [Part 2](http://localhost:8888/lab/tree/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb).\n", "\n", "If unfamiliar with the protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0036-issue-credential)\n" ] @@ -24,17 +24,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "IPython autoawait is `on`, and set to use `asyncio`\n" - ] - } - ], + "outputs": [], "source": [ "%autoawait\n", "import time\n", @@ -65,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -113,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -126,7 +118,7 @@ "source": [ "## 3b. (OPTIONAL) Write a Schema to the Ledger\n", "\n", - "For more details see the [schema-api notebook](http://localhost:8888/notebooks/schema_api.ipynb)\n", + "For more details see the [schema-api notebook](http://localhost:8888/lab/tree/schema_api.ipynb)\n", "\n", "**Note: You will only be able to do this once unless you change the schema_name or version. Once you have a schema written to the ledger it can be reused by multiple entities**" ] @@ -160,17 +152,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Jqi9JNk9Z637azKekx51V4:3:CL:142409:default\n" - ] - } - ], + "outputs": [], "source": [ "response = await agent_controller.definitions.write_cred_def(schema_id)\n", "\n", @@ -204,19 +188,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Please enter your name and surname: Joe\n", - "Please enter your age: 28\n", - "[{'name': 'fullname', 'value': 'Joe'}, {'name': 'skill', 'value': 'PyDentity SSI Ninja'}, {'name': 'age', 'value': '28'}]\n" - ] - } - ], + "outputs": [], "source": [ "name=input(\"Please enter your name and surname: \")\n", "age=input(\"Please enter your age: \")\n", @@ -256,17 +230,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "EXISTING CONNECTIONS\n" - ] - } - ], + "outputs": [], "source": [ "# Check for existing connections\n", "connection = await agent_controller.connections.get_connections()\n", @@ -285,20 +251,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connection message {'invitation_mode': 'once', 'created_at': '2021-01-20 08:38:21.097819Z', 'state': 'invitation', 'invitation_key': '432bz3xmHA7QT4zZBs1ZYLbxTv4G12d1NygHUhnnNx9m', 'updated_at': '2021-01-20 08:38:21.097819Z', 'connection_id': 'adfc5ffc-1560-4075-99cd-45f238b8346b', 'accept': 'auto', 'initiator': 'self', 'routing_state': 'none'} adfc5ffc-1560-4075-99cd-45f238b8346b\n", - "Connection ID adfc5ffc-1560-4075-99cd-45f238b8346b\n", - "Invitation\n", - "https://1ae2ec7766b1.ngrok.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiNmNjZGIwMWQtYThjNi00NDNlLThmMWEtOThiMzIwMWFiOThmIiwgInJlY2lwaWVudEtleXMiOiBbIjQzMmJ6M3htSEE3UVQ0elpCczFaWUxieFR2NEcxMmQxTnlnSFVobm5OeDltIl0sICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cHM6Ly8xYWUyZWM3NzY2YjEubmdyb2suaW8iLCAibGFiZWwiOiAiSVNTVUVSIn0=\n" - ] - } - ], + "outputs": [], "source": [ "# Create Invitation\n", "invite = await agent_controller.connections.create_invitation()\n", @@ -431,7 +386,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Proceed to Part 4 on [Verifier Notebook](http://127.0.0.1:8889/notebooks/Part%204%20-%20Verifier.ipynb)\n", + "# Proceed to Part 4 on [Verifier Notebook](http://localhost:8889/lab/tree/Part%204%20-%20Verify%20a%20Presentation.ipynb)\n", "\n", "Here you will present the attributes issued to you within this tutorial to a verifying entity." ] @@ -460,9 +415,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/tutorials/aries-stagingnet/notebooks/issuer/issuer_agent_invite_QRcode.png b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/issuer_agent_invite_QRcode.png similarity index 100% rename from tutorials/aries-stagingnet/notebooks/issuer/issuer_agent_invite_QRcode.png rename to tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/issuer_agent_invite_QRcode.png diff --git a/tutorials/aries-stagingnet/notebooks/verifier/Part 4 - Verify a Presentation.ipynb b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/Part 4 - Verify a Presentation.ipynb similarity index 97% rename from tutorials/aries-stagingnet/notebooks/verifier/Part 4 - Verify a Presentation.ipynb rename to tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/Part 4 - Verify a Presentation.ipynb index 0020a9c6..64e9205f 100644 --- a/tutorials/aries-stagingnet/notebooks/verifier/Part 4 - Verify a Presentation.ipynb +++ b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/Part 4 - Verify a Presentation.ipynb @@ -6,7 +6,7 @@ "source": [ "# Verify a Presentation\n", "\n", - "## Before running this notebook you should have a mobile wallet contained the credential you issued yourself in Part 3. If not run through [Part 2](http://127.0.0.1:8888/notebooks/Part%202%20-%20Connecting%20Issuer%20Agent%20to%20Sovrin%20Stagingnet.ipynb) and [Part 3](http://localhost:8888/notebooks/Part%203%20-%20Issue%20Credential.ipynb) of this tutorial.\n", + "## Before running this notebook you should have a mobile wallet contained the credential you issued yourself in Part 3. If not run through [Part 2](http://localhost:8888/lab/tree/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb) and [Part 3](http://localhost:8888/lab/tree/Part%203%20-%20Issue%20Credential.ipynb) of this tutorial.\n", "\n", "If unfamiliar with the present-proof protocol it is worth reading through the [aries-rfs](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof)\n" ] @@ -110,7 +110,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 3. Make a connection with the Mobile SSI Wallet you used in [Part 2](http://127.0.0.1:8888/notebooks/Part%202%20-%20Connecting%20Issuer%20Agent%20to%20Sovrin%20Stagingnet.ipynb)\n", + "## 3. Make a connection with the Mobile SSI Wallet you used in [Part 2](http://localhost:8888/lab/tree/Part%202%20-%20Writing%20a%20Public%20DID%20to%20the%20Sovrin%20StagingNet.ipynb)\n", "\n", "**You should have a credential stored in here from the previous notebook!**\n", "\n", @@ -494,7 +494,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/tutorials/aries-stagingnet/notebooks/verifier/verifier_agent_invite_QRcode.png b/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/verifier_agent_invite_QRcode.png similarity index 100% rename from tutorials/aries-stagingnet/notebooks/verifier/verifier_agent_invite_QRcode.png rename to tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/verifier_agent_invite_QRcode.png diff --git a/tutorials/attachments/README.md b/tutorials/3. Attachments/README.md similarity index 100% rename from tutorials/attachments/README.md rename to tutorials/3. Attachments/README.md diff --git a/tutorials/attachments/docker-compose.yml b/tutorials/3. Attachments/docker-compose.yml similarity index 100% rename from tutorials/attachments/docker-compose.yml rename to tutorials/3. Attachments/docker-compose.yml diff --git a/tutorials/attachments/manage b/tutorials/3. Attachments/manage similarity index 100% rename from tutorials/attachments/manage rename to tutorials/3. Attachments/manage diff --git a/tutorials/attachments/notebooks/alice/attachment.ipynb b/tutorials/3. Attachments/notebooks/alice/attachment.ipynb similarity index 95% rename from tutorials/attachments/notebooks/alice/attachment.ipynb rename to tutorials/3. Attachments/notebooks/alice/attachment.ipynb index 43749029..7ffaecd1 100644 --- a/tutorials/attachments/notebooks/alice/attachment.ipynb +++ b/tutorials/3. Attachments/notebooks/alice/attachment.ipynb @@ -9,7 +9,7 @@ "## Role: \n", "\n", "This notebook tests the capablity to extend the basic controller to control a custom protocol developed in an aries agent. This protocol allows you to attach a file to a message.\n", - "Run this along side [Attachment Protocol Bob](http://localhost:8889/notebooks/attachment.ipynb)" + "Run this along side [Attachment Protocol Bob](http://localhost:8889/lab/tree/attachment.ipynb)" ] }, { @@ -92,7 +92,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Make sure you have initialised the handler on [Bob's notebook](http://localhost:8889/notebooks/attachment.ipynb)" + "## Make sure you have initialised the handler on [Bob's notebook](http://localhost:8889/lab/tree/attachment.ipynb)" ] }, { @@ -106,7 +106,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this instance a text file is sent, this will be received and saved by the handler in the [Bob notebook](http://localhost:8889/notebooks/attachment.ipynb)." + "In this instance a text file is sent, this will be received and saved by the handler in the [Bob's notebook](http://localhost:8889/lab/tree/attachment.ipynb)." ] }, { @@ -130,7 +130,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here an image file is sent and is received by the handler in [Bob notebook](http://localhost:8889/notebooks/attachment.ipynb)." + "Here an image file is sent and is received by the handler in [Bob's notebook](http://localhost:8889/lab/tree/attachment.ipynb)." ] }, { diff --git a/tutorials/attachments/notebooks/alice/openmined.jpg b/tutorials/3. Attachments/notebooks/alice/openmined.jpg similarity index 100% rename from tutorials/attachments/notebooks/alice/openmined.jpg rename to tutorials/3. Attachments/notebooks/alice/openmined.jpg diff --git a/tutorials/attachments/notebooks/alice/test_file.txt b/tutorials/3. Attachments/notebooks/alice/test_file.txt similarity index 100% rename from tutorials/attachments/notebooks/alice/test_file.txt rename to tutorials/3. Attachments/notebooks/alice/test_file.txt diff --git a/tutorials/attachments/notebooks/bob/attachment.ipynb b/tutorials/3. Attachments/notebooks/bob/attachment.ipynb similarity index 98% rename from tutorials/attachments/notebooks/bob/attachment.ipynb rename to tutorials/3. Attachments/notebooks/bob/attachment.ipynb index 029d80e1..636c9f4b 100644 --- a/tutorials/attachments/notebooks/bob/attachment.ipynb +++ b/tutorials/3. Attachments/notebooks/bob/attachment.ipynb @@ -7,7 +7,7 @@ "# Attachment Protocol - Bob\n", "## Role: Receiver\n", "\n", - "## This notebook should be run alongside [Alice](http://localhost:8888/notebooks/attachment.ipynb), who will send Bob two attachments.\n", + "## This notebook should be run alongside [Alice](http://localhost:8888/lab/tree/attachment.ipynb), who will send Bob two attachments.\n", "\n", "In this notebook Bob instatiates his controller and sets up a handler for attachment protocol messages, all files he receives he will save in the [received_files folder](http://localhost:8889/tree/received_files). It should currently be empty.\n" ] diff --git a/tutorials/4. Multitenancy/docker-compose.yml b/tutorials/4. Multitenancy/docker-compose.yml new file mode 100644 index 00000000..d7aaa67d --- /dev/null +++ b/tutorials/4. Multitenancy/docker-compose.yml @@ -0,0 +1,137 @@ +version: "3" +services: + ngrok-external: + image: wernight/ngrok + command: ngrok http external-agent:${EXTERNAL_HTTP_PORT} --log stdout + networks: + - indy_demo + external-agent: + build: + context: ../../ + dockerfile: dockerfiles/agents/Dockerfile.ngrok + environment: + - NGROK_NAME=ngrok-external + - ADMIN_URL=http://external-agent:${EXTERNAL_ADMIN_PORT} + - AGENT_NAME=${EXTERNAL_AGENT_NAME} + - ADMIN_PORT=${EXTERNAL_ADMIN_PORT} + - HTTP_PORT=${EXTERNAL_HTTP_PORT} + - WEBHOOK_URL=${EXTERNAL_WEBHOOK_URL} + - AGENT_ENDPOINT=${EXTERNAL_AGENT_ENDPOINT} + - WALLET_SEED=${EXTERNAL_WALLET_SEED} + - WALLET_NAME=${EXTERNAL_WALLET_NAME} + - WALLET_KEY=${EXTERNAL_WALLET_KEY} + - WALLET_TYPE=${WALLET_TYPE} + - ACAPY_ADMIN_INSECURE_MODE=${EXTERNAL_ADMIN_SECURE} + - GENESIS_URL=${GENESIS_URL} + ports: + - ${EXTERNAL_HTTP_PORT}:${EXTERNAL_HTTP_PORT} + - ${EXTERNAL_ADMIN_PORT}:${EXTERNAL_ADMIN_PORT} + networks: + - indy_demo + ngrok-mediator: + image: wernight/ngrok + command: ngrok http mediator-agent:${MEDIATOR_HTTP_PORT} --log stdout + networks: + - indy_demo + mediator-agent: + build: + context: ../../ + dockerfile: dockerfiles/agents/Dockerfile.ngrok + environment: + - NGROK_NAME=ngrok-mediator + - ADMIN_URL=http://mediator-agent:${MEDIATOR_ADMIN_PORT} + - AGENT_NAME=${MEDIATOR_AGENT_NAME} + - ADMIN_PORT=${MEDIATOR_ADMIN_PORT} + - HTTP_PORT=${MEDIATOR_HTTP_PORT} + - WEBHOOK_URL=${MEDIATOR_WEBHOOK_URL} + - AGENT_ENDPOINT=${MEDIATOR_AGENT_ENDPOINT} + - WALLET_SEED=${MEDIATOR_WALLET_SEED} + - WALLET_NAME=${MEDIATOR_WALLET_NAME} + - WALLET_KEY=${MEDIATOR_WALLET_KEY} + - WALLET_TYPE=${WALLET_TYPE} + - ACAPY_ADMIN_INSECURE_MODE=${MEDIATOR_ADMIN_SECURE} + - GENESIS_URL=${GENESIS_URL} + ports: + - ${MEDIATOR_HTTP_PORT}:${MEDIATOR_HTTP_PORT} + - ${MEDIATOR_ADMIN_PORT}:${MEDIATOR_ADMIN_PORT} + networks: + - indy_demo + mediator-notebook: + build: + context: ../../ + dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller + args: + - jupyter_port=${MEDIATOR_JUPYTER_PORT} + depends_on: + - mediator-agent + networks: + - indy_demo + volumes: + - ${PWD}/notebooks/mediator:/workspace + ports: + - ${MEDIATOR_JUPYTER_PORT}:8888 + - ${MEDIATOR_WEBHOOK_PORT}:${MEDIATOR_WEBHOOK_PORT} + ngrok-basewallet: + image: wernight/ngrok + command: ngrok http basewallet-agent:${BASEWALLET_HTTP_PORT} --log stdout + networks: + - indy_demo + basewallet-agent: + build: + context: ../../ + dockerfile: dockerfiles/agents/Dockerfile.ngrok + environment: + - NGROK_NAME=ngrok-basewallet + - ADMIN_URL=http://basewallet-agent:${BASEWALLET_ADMIN_PORT} + - AGENT_NAME=${BASEWALLET_AGENT_NAME} + - ADMIN_PORT=${BASEWALLET_ADMIN_PORT} + - HTTP_PORT=${BASEWALLET_HTTP_PORT} + - WEBHOOK_URL=${BASEWALLET_WEBHOOK_URL} + - AGENT_ENDPOINT=${BASEWALLET_AGENT_ENDPOINT} + - WALLET_SEED=${BASEWALLET_WALLET_SEED} + - WALLET_NAME=${BASEWALLET_WALLET_NAME} + - WALLET_KEY=${BASEWALLET_WALLET_KEY} + - WALLET_TYPE=${WALLET_TYPE} + - ACAPY_ADMIN_INSECURE_MODE=${BASEWALLET_ADMIN_SECURE} + - GENESIS_URL=${GENESIS_URL} + - ACAPY_MULTITENANT=true + - ACAPY_MULTITENANT_JWT_SECRET="password" + - ACAPY_MULTITENANT_ADMIN=true + ports: + - ${BASEWALLET_HTTP_PORT}:${BASEWALLET_HTTP_PORT} + - ${BASEWALLET_ADMIN_PORT}:${BASEWALLET_ADMIN_PORT} + networks: + - indy_demo + basewallet-notebook: + build: + context: ../../ + dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller + args: + - jupyter_port=${BASEWALLET_JUPYTER_PORT} + depends_on: + - basewallet-agent + networks: + - indy_demo + volumes: + - ./notebooks/basewallet:/workspace + ports: + - "8888:8888" + - ${BASEWALLET_WEBHOOK_PORT}:${BASEWALLET_WEBHOOK_PORT} + external-notebook: + build: + context: ../../ + dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller + args: + - jupyter_port=${EXTERNAL_JUPYTER_PORT} + depends_on: + - external-agent + networks: + - indy_demo + volumes: + - ${PWD}/notebooks/external:/workspace + ports: + - "8889:8888" + - ${EXTERNAL_WEBHOOK_PORT}:${EXTERNAL_WEBHOOK_PORT} + +networks: + indy_demo: diff --git a/tutorials/4. Multitenancy/manage b/tutorials/4. Multitenancy/manage new file mode 100755 index 00000000..9df8228b --- /dev/null +++ b/tutorials/4. Multitenancy/manage @@ -0,0 +1,307 @@ +#!/bin/bash +export MSYS_NO_PATHCONV=1 +export DOCKERHOST=${APPLICATION_URL-$(docker run --rm --net=host codenvy/che-ip)} +set -e + +S2I_EXE=s2i +if [ -z $(type -P "$S2I_EXE") ]; then + echo -e "The ${S2I_EXE} executable is needed and not on your path." + echo -e "It can be downloaded from here: https://github.com/openshift/source-to-image/releases" + echo -e "Make sure you extract the binary and place it in a directory on your path." + exit 1 +fi + +SCRIPT_HOME="$(cd "$(dirname "$0")" && pwd)" + +# ================================================================================================================= +# Usage: +# ----------------------------------------------------------------------------------------------------------------- +usage() { + cat <<-EOF + + Usage: $0 [command] [options] + + Commands: + + up - Builds the images, creates the application containers + and starts the services based on the docker-compose.yml file. + + You can pass in a list of containers to start. + By default all containers will be started. + + The API_URL used by tob-web can also be redirected. + + Examples: + $0 start + $0 start EXTERNAL-agent + + start - Same as up + + restart - Re-starts the application containers, + useful when updating one of the container images during development. + + You can pass in a list of containers to be restarted. + By default all containers will be restarted. + + Examples: + $0 start + $0 start faber-agent + + logs - Display the logs from the docker compose run (ctrl-c to exit). + + stop - Stops the services. This is a non-destructive process. The volumes and containers + are not deleted so they will be reused the next time you run start. + + down - Brings down the services and removes the volumes (storage) and containers. + rm - Same as down + + +EOF + exit 1 +} +# ----------------------------------------------------------------------------------------------------------------- +# Default Settings: +# ----------------------------------------------------------------------------------------------------------------- +DEFAULT_CONTAINERS="ngrok-external external-agent ngrok-basewallet basewallet-agent basewallet-notebook external-notebook ngrok-mediator mediator-agent mediator-notebook " + +# ----------------------------------------------------------------------------------------------------------------- +# Functions: +# ----------------------------------------------------------------------------------------------------------------- +function echoRed() { + _msg=${1} + _red='\e[31m' + _nc='\e[0m' # No Color + echo -e "${_red}${_msg}${_nc}" +} + +function echoYellow() { + _msg=${1} + _yellow='\e[33m' + _nc='\e[0m' # No Color + echo -e "${_yellow}${_msg}${_nc}" +} + +configureEnvironment() { + + if [ -f .env ]; then + while read line; do + if [[ ! "$line" =~ ^\# ]] && [[ "$line" =~ .*= ]]; then + export ${line//[$'\r\n']/} + fi + done <.env + fi + + for arg in "$@"; do + # Remove recognized arguments from the list after processing. + shift + + # echo "arg: ${arg}" + # echo "Remaining: ${@}" + + case "$arg" in + *=*) + # echo "Exporting ..." + export "${arg}" + ;; + *) + # echo "Saving for later ..." + # If not recognized, save it for later procesing ... + set -- "$@" "$arg" + ;; + esac + done + + # global + export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:controller}" + export GENESIS_URL="https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis" + export WALLET_TYPE="indy" + + # EXTERNAL-agent + export EXTERNAL_AGENT_NAME="EXTERNAL" + export EXTERNAL_ADMIN_PORT=8051 + export EXTERNAL_HTTP_PORT=8050 + export EXTERNAL_WEBHOOK_PORT=8052 + export EXTERNAL_WEBHOOK_URL=${EXTERNAL_WEBHOOK_URL:-http://$DOCKERHOST:$EXTERNAL_WEBHOOK_PORT} + export EXTERNAL_AGENT_ENDPOINT=${EXTERNAL_AGENT_ENDPOINT:-http://$DOCKERHOST:$EXTERNAL_HTTP_PORT} + export EXTERNAL_WALLET_SEED="VAGENT_seed_00000000000000000000" + export EXTERNAL_WALLET_KEY="EXTERNAL_key_00000000000000000000000" + export EXTERNAL_WALLET_NAME="EXTERNAL Wallet" + export EXTERNAL_JUPYTER_PORT="8889" + export EXTERNAL_ADMIN_SECURE="true" + + # BASEWALLET-agent + export BASEWALLET_AGENT_NAME="BASEWALLET" + export BASEWALLET_ADMIN_PORT=8021 + export BASEWALLET_HTTP_PORT=8020 + export BASEWALLET_WEBHOOK_PORT=8022 + export BASEWALLET_WEBHOOK_URL=${BASEWALLET_WEBHOOK_URL:-http://$DOCKERHOST:$BASEWALLET_WEBHOOK_PORT} + export BASEWALLET_AGENT_ENDPOINT=${BASEWALLET_AGENT_ENDPOINT:-http://$DOCKERHOST:$BASEWALLET_HTTP_PORT} + export BASEWALLET_WALLET_SEED="IAGENT_seed_00000000000000000000" + export BASEWALLET_WALLET_KEY="BASEWALLET_key_00000000000000000000000" + export BASEWALLET_WALLET_NAME="BASEWALLET Wallet" + export BASEWALLET_JUPYTER_PORT="8888" + export BASEWALLET_ADMIN_SECURE="true" + + # MEDIATOR-agent + export MEDIATOR_AGENT_NAME="MEDIATOR" + export MEDIATOR_ADMIN_PORT=8041 + export MEDIATOR_HTTP_PORT=8040 + export MEDIATOR_WEBHOOK_PORT=8042 + export MEDIATOR_WEBHOOK_URL=${MEDIATOR_WEBHOOK_URL:-http://$DOCKERHOST:$MEDIATOR_WEBHOOK_PORT} + export MEDIATOR_AGENT_ENDPOINT=${MEDIATOR_AGENT_ENDPOINT:-http://$DOCKERHOST:$MEDIATOR_HTTP_PORT} + export MEDIATOR_WALLET_SEED="VAGENT_seed_00000000000000000000" + export MEDIATOR_WALLET_KEY="MEDIATOR_key_00000000000000000000000" + export MEDIATOR_WALLET_NAME="MEDIATOR Wallet" + export MEDIATOR_JUPYTER_PORT="8890" + export MEDIATOR_ADMIN_SECURE="true" + + + +} + +getInputParams() { + ARGS="" + + for arg in $@; do + case "$arg" in + *=*) + # Skip it + ;; + *) + ARGS+=" $arg" + ;; + esac + done + + echo ${ARGS} +} + +getStartupParams() { + CONTAINERS="" + ARGS="" + + for arg in $@; do + case "$arg" in + *=*) + # Skip it + ;; + -*) + ARGS+=" $arg" + ;; + *) + CONTAINERS+=" $arg" + ;; + esac + done + + if [ -z "$CONTAINERS" ]; then + CONTAINERS="$DEFAULT_CONTAINERS" + fi + + echo ${ARGS} ${CONTAINERS} +} + +deleteVolumes() { + _projectName=${COMPOSE_PROJECT_NAME:-docker} + + echo "Stopping and removing any running containers ..." + docker-compose down -v + + _pattern="^${_projectName}_\|^docker_" + _volumes=$(docker volume ls -q | grep ${_pattern}) + + if [ ! -z "${_volumes}" ]; then + echo "Removing project volumes ..." + echo ${_volumes} | xargs docker volume rm + else + echo "No project volumes exist." + fi + + echo "Removing build cache ..." + rm -Rf ../client/tob-web/.cache +} + + +getSeedJson() { + _seed=${1} + if [ -z "${_seed}" ]; then + echo -e \\n"getSeedJson; Missing parameter!"\\n + exit 1 + fi + + echo "{\"seed\": \"${_seed}\"}" +} + +generateSeeds() { + echo ${INDY_WALLET_SEED} +} + + + +toLower() { + echo $(echo ${@} | tr '[:upper:]' '[:lower:]') +} + +echoError() { + _msg=${1} + _red='\033[0;31m' + _nc='\033[0m' # No Color + echo -e "${_red}${_msg}${_nc}" >&2 +} + +functionExists() { + ( + if [ ! -z ${1} ] && type ${1} &>/dev/null; then + return 0 + else + return 1 + fi + ) +} +# ================================================================================================================= + +pushd "${SCRIPT_HOME}" >/dev/null +COMMAND=$(toLower ${1}) +shift || COMMAND=usage + +case "${COMMAND}" in +start | up) + export MULTITENANT_TUTORIAL="MULTITENANT_TUTORIAL" + echo $MULTITENANT_TUTORIAL + echoYellow "Starting up... This can take a couple of minutes." + _startupParams=$(getStartupParams $@) + configureEnvironment "$@" + docker-compose\ + --log-level ERROR up \ + --build --remove-orphans \ + -d ${_startupParams} + docker-compose \ + --log-level ERROR logs \ + -f + ;; +restart) + _startupParams=$(getStartupParams $@) + configureEnvironment "$@" + docker-compose stop ${_startupParams} + docker-compose up -d --build --remove-orphans ${_startupParams} + ;; +logs) + configureEnvironment "$@" + docker-compose logs -f + ;; +stop) + unset MULTITENANT_TUTORIAL + configureEnvironment + docker-compose stop + ;; +rm | down) + unset MULTITENANT_TUTORIAL + configureEnvironment + docker-compose \ + --log-level ERROR down \ + -v + usage + ;; +esac + +popd >/dev/null diff --git a/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 2 - Mediation of communication - Alice.ipynb b/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 2 - Mediation of communication - Alice.ipynb new file mode 100644 index 00000000..7f540185 --- /dev/null +++ b/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 2 - Mediation of communication - Alice.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "major-resort", + "metadata": {}, + "source": [ + "# Part 2 - Setting up a mediation for Alice via a mediator" + ] + }, + { + "cell_type": "markdown", + "id": "sound-protection", + "metadata": {}, + "source": [ + "### Initialise the basewallet controller" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "known-brave", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], + "source": [ + "%autoawait\n", + "import time\n", + "import asyncio\n", + "import pprint\n", + "\n", + "from aries_basic_controller.aries_controller import AriesAgentController\n", + "\n", + "# Create a small utility to print json formatted outout more human-readable \n", + "pp = pprint.PrettyPrinter(indent=4)\n", + "\n", + "WEBHOOK_HOST = \"0.0.0.0\"\n", + "WEBHOOK_BASE = \"\"\n", + "\n", + "WEBHOOK_PORT = 8022\n", + "ADMIN_URL = \"http://basewallet-agent:8021\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "wrong-original", + "metadata": {}, + "outputs": [], + "source": [ + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL, multitenant=True, mediation=True)\n" + ] + }, + { + "cell_type": "markdown", + "id": "norwegian-heart", + "metadata": {}, + "source": [ + "### Updating JWT of the agent controller" + ] + }, + { + "cell_type": "markdown", + "id": "medieval-custom", + "metadata": {}, + "source": [ + "Retrieve Alice's token we have stored previously" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "proud-plastic", + "metadata": {}, + "outputs": [], + "source": [ + "%store -r alice_jwt" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "blessed-diabetes", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM\n" + ] + } + ], + "source": [ + "print(alice_jwt)" + ] + }, + { + "cell_type": "markdown", + "id": "academic-junior", + "metadata": {}, + "source": [ + "Now we can update the agent controller with the JWT Token" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "suited-kenya", + "metadata": {}, + "outputs": [], + "source": [ + "agent_controller.update_tennant_jwt(alice_jwt)" + ] + }, + { + "cell_type": "markdown", + "id": "varying-stress", + "metadata": {}, + "source": [ + "Let's check it's really there" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "combined-adelaide", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM\n" + ] + } + ], + "source": [ + "print(agent_controller.tennant_jwt)" + ] + }, + { + "cell_type": "markdown", + "id": "delayed-strain", + "metadata": {}, + "source": [ + "### Let's create an invitation" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "advised-cosmetic", + "metadata": {}, + "outputs": [], + "source": [ + "# Create Invitation\n", + "unmediated_invite = await agent_controller.connections.create_invitation()" + ] + }, + { + "cell_type": "markdown", + "id": "organizational-appliance", + "metadata": {}, + "source": [ + "We have created an invitation now. Don't worry about this until the end of the notebook. Then you should worry. This will be used to demonstrate something relevant later on." + ] + }, + { + "cell_type": "markdown", + "id": "opening-particle", + "metadata": {}, + "source": [ + "### Go to the [mediation agent](http://localhost:8890/notebooks/Configure%20Mediator.ipynb) before you continue to generate and fetch the invitation\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "homeless-mercury", + "metadata": {}, + "source": [ + "### Accept Invite From Mediator\n", + "\n", + "Replace the invitation object below with the one you have generated in the mediator notebook" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "processed-arnold", + "metadata": {}, + "outputs": [], + "source": [ + "mediator_invitation = {'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '4d68d1f0-83d4-49b9-bb96-9ac35283878d', 'serviceEndpoint': 'https://6512c3c6a284.ngrok.io', 'label': 'MEDIATOR', 'recipientKeys': ['8kPjcAdzMdf8giMrsWackJRDHEUeG6uoDu7ECVm1yGrY']}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "blank-venture", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'accept': 'manual',\n", + " 'connection_id': '0cd18e94-c147-4a41-9130-1c0981390fec',\n", + " 'created_at': '2021-03-19 18:26:31.346476Z',\n", + " 'invitation_key': '8kPjcAdzMdf8giMrsWackJRDHEUeG6uoDu7ECVm1yGrY',\n", + " 'invitation_mode': 'once',\n", + " 'my_did': 'Pa3NQnMqJ5fqwG9Zga4Kpn',\n", + " 'request_id': '64c584ef-865b-4018-995b-c0fbcce34bd7',\n", + " 'rfc23_state': 'request-sent',\n", + " 'routing_state': 'none',\n", + " 'state': 'request',\n", + " 'their_label': 'MEDIATOR',\n", + " 'their_role': 'inviter',\n", + " 'updated_at': '2021-03-19 18:26:31.366369Z'}\n" + ] + } + ], + "source": [ + "response = await agent_controller.connections.accept_connection(mediator_invitation)\n", + "pp.pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "separate-recipient", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0cd18e94-c147-4a41-9130-1c0981390fec\n" + ] + } + ], + "source": [ + "connection_id = response[\"connection_id\"]\n", + "print(connection_id)" + ] + }, + { + "cell_type": "markdown", + "id": "fatty-pencil", + "metadata": {}, + "source": [ + "### Request mediation\n", + "\n", + "Now that we have successfully established a connection between Alice and the mediator agent we can proceed to request mediation from the mediator." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "numerical-outside", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "active\n" + ] + } + ], + "source": [ + "### check state of connection\n", + "connection = await agent_controller.connections.get_connection(connection_id)\n", + "print(connection['state'])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "harmful-producer", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'recipient_terms': [], 'created_at': '2021-03-19 18:27:01.659420Z', 'routing_keys': [], 'connection_id': '0cd18e94-c147-4a41-9130-1c0981390fec', 'updated_at': '2021-03-19 18:27:01.659420Z', 'state': 'request', 'mediation_id': '689b3744-8234-4784-821d-336ada06b1cd', 'role': 'client', 'mediator_terms': []}\n" + ] + } + ], + "source": [ + "# Let's check for the state\n", + "if connection['state'] != 'active':\n", + " print(\"No active connection. \\n Please go back and ensure you have established an active connection between the mediator agent and Alice's subwallet agent\") \n", + "else:\n", + " ## request mediation\n", + " mediation_req = await agent_controller.mediation.request_mediation(connection_id)\n", + " print(mediation_req)\n" + ] + }, + { + "cell_type": "markdown", + "id": "indie-primary", + "metadata": {}, + "source": [ + "### Let's have a look at the mediation records and we should see our mediation in there" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "tropical-barbados", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'recipient_terms': [], 'created_at': '2021-03-19 18:27:01.659420Z', 'routing_keys': ['6xw4tkNaziTmJ7wpGRq1fa3Y6CagGHcsGFXdM19T9Har'], 'endpoint': 'https://6512c3c6a284.ngrok.io', 'connection_id': '0cd18e94-c147-4a41-9130-1c0981390fec', 'updated_at': '2021-03-19 18:27:02.469528Z', 'state': 'granted', 'mediation_id': '689b3744-8234-4784-821d-336ada06b1cd', 'role': 'client', 'mediator_terms': []}]\n" + ] + } + ], + "source": [ + "response = await agent_controller.mediation.get_mediation_records()\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "id": "british-indonesian", + "metadata": {}, + "source": [ + "### Set a default mediator\n", + "\n", + "By setting a default mediator, all DIDComm connections we establish will be routed through this mediator. To do this we use the appropriate API endpoint via the agent controller and pass it the mediation ID of our mediated connection." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "protecting-opera", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'connection_id': '0cd18e94-c147-4a41-9130-1c0981390fec',\n", + " 'created_at': '2021-03-19 18:27:01.659420Z',\n", + " 'endpoint': 'https://6512c3c6a284.ngrok.io',\n", + " 'mediation_id': '689b3744-8234-4784-821d-336ada06b1cd',\n", + " 'mediator_terms': [],\n", + " 'recipient_terms': [],\n", + " 'role': 'client',\n", + " 'routing_keys': ['6xw4tkNaziTmJ7wpGRq1fa3Y6CagGHcsGFXdM19T9Har'],\n", + " 'state': 'granted',\n", + " 'updated_at': '2021-03-19 18:27:02.469528Z'}\n" + ] + } + ], + "source": [ + "default_mediation_res = await agent_controller.mediation.set_default_mediator(response[0]['mediation_id'])\n", + "pp.pprint(default_mediation_res)" + ] + }, + { + "cell_type": "markdown", + "id": "funky-portfolio", + "metadata": {}, + "source": [ + "### Check whether our default mediator is really there" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "comprehensive-circular", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'connection_id': '0cd18e94-c147-4a41-9130-1c0981390fec',\n", + " 'created_at': '2021-03-19 18:27:01.659420Z',\n", + " 'endpoint': 'https://6512c3c6a284.ngrok.io',\n", + " 'mediation_id': '689b3744-8234-4784-821d-336ada06b1cd',\n", + " 'mediator_terms': [],\n", + " 'recipient_terms': [],\n", + " 'role': 'client',\n", + " 'routing_keys': ['6xw4tkNaziTmJ7wpGRq1fa3Y6CagGHcsGFXdM19T9Har'],\n", + " 'state': 'granted',\n", + " 'updated_at': '2021-03-19 18:27:02.469528Z'}\n", + "\n", + "\n", + " Hooray! We have succesfully set a default mediator.\n" + ] + } + ], + "source": [ + "default_mediator = await agent_controller.mediation.get_default_mediator()\n", + "pp.pprint(default_mediator)\n", + "\n", + "if default_mediator['connection_id'] != connection_id:\n", + " print(\"Oooops! Something went wrong setting the default mediator. Please, check above and try again\")\n", + "else:\n", + " print(\"\\n\\n Hooray! We have succesfully set a default mediator.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "turkish-enclosure", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connection ID 9ef6b434-0baf-4f6a-bb40-4813d0f7e19b\n", + "Invitation\n", + "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': 'fd50beee-b7bd-40df-8e7c-9a37cc7fab5b', 'label': 'Alice', 'imageUrl': 'https://aries.ca/images/sample.png', 'routingKeys': ['6xw4tkNaziTmJ7wpGRq1fa3Y6CagGHcsGFXdM19T9Har'], 'serviceEndpoint': 'https://6512c3c6a284.ngrok.io', 'recipientKeys': ['n8Wm6AT6acif1zuyNmHSe2GfrCMsjwKkvwcXtdE77QC']}\n", + "'\\n6xw4tkNaziTmJ7wpGRq1fa3Y6CagGHcsGFXdM19T9Har'\n" + ] + } + ], + "source": [ + "# Create Invitation\n", + "invite = await agent_controller.connections.create_invitation()\n", + "connection_id = invite[\"connection_id\"]\n", + "invite_message = invite['invitation']\n", + "print(\"Connection ID\", connection_id)\n", + "print(\"Invitation\")\n", + "print(invite_message)\n", + "pp.pprint(\"\\n\" + invite_message['routingKeys'][0])" + ] + }, + { + "cell_type": "markdown", + "id": "sustained-audit", + "metadata": {}, + "source": [ + "### Checking routing keys" + ] + }, + { + "cell_type": "markdown", + "id": "civic-dictionary", + "metadata": {}, + "source": [ + "This routing key should be used from now on to encrypt all messages/comminucation. Below you'll see that going back our initially created invitation we don't have such key." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "civic-behalf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ '@id': '1b49bbd3-8b93-4adc-9ca9-a7b769f94800',\n", + " '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation',\n", + " 'imageUrl': 'https://aries.ca/images/sample.png',\n", + " 'label': 'Alice',\n", + " 'recipientKeys': ['D838wLAMUhLVznB8MUAbXxRLt3ZCHM2pqjHwUXbTMcJk'],\n", + " 'serviceEndpoint': 'https://997f04d484e6.ngrok.io'}\n" + ] + } + ], + "source": [ + "unmediated_invite_message = unmediated_invite['invitation']\n", + "pp.pprint(unmediated_invite_message)" + ] + }, + { + "cell_type": "markdown", + "id": "unexpected-suspension", + "metadata": {}, + "source": [ + "As you can see, there is no routing key in there" + ] + }, + { + "cell_type": "markdown", + "id": "raised-nicholas", + "metadata": {}, + "source": [ + "### Comparing endpoints" + ] + }, + { + "cell_type": "markdown", + "id": "colonial-providence", + "metadata": {}, + "source": [ + "Let's check and see that the service endpoint of the mediated connection is now actually the one we got from the invitation from the mediator agent.\n", + "\n", + "We'll also see that this is not the endpoint for the unmediated invitation." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "confirmed-cement", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unmediated endpoint: https://997f04d484e6.ngrok.io\n", + "\n", + "\n", + "Mediated endpoint: https://6512c3c6a284.ngrok.io\n", + "Mediator Invitation Ednpoint: https://6512c3c6a284.ngrok.io\n" + ] + } + ], + "source": [ + "print(\"Unmediated endpoint: \" + unmediated_invite_message['serviceEndpoint'] + \"\\n\\n\")\n", + "print(\"Mediated endpoint: \" + invite_message['serviceEndpoint'])\n", + "print(\"Mediator Invitation Ednpoint: \" + mediator_invitation['serviceEndpoint'])" + ] + }, + { + "cell_type": "markdown", + "id": "opened-listing", + "metadata": {}, + "source": [ + "### Great. You're done with this tutorial. Please move on part 3.." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "united-announcement", + "metadata": {}, + "outputs": [], + "source": [ + "await agent_controller.terminate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "minute-october", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 3 - Communicating with an external agent.ipynb b/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 3 - Communicating with an external agent.ipynb new file mode 100644 index 00000000..1f0aea54 --- /dev/null +++ b/tutorials/4. Multitenancy/notebooks/basewallet/Alice/Part 3 - Communicating with an external agent.ipynb @@ -0,0 +1,535 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bacterial-indication", + "metadata": {}, + "source": [ + "# Part 3 - Communicating with an external agent" + ] + }, + { + "cell_type": "markdown", + "id": "unknown-contemporary", + "metadata": {}, + "source": [ + "**!!! You should start this part in the [external agent notebook](http://localhost:8889/lab/workspaces/auto-w/tree/Configure%20External%20Agent.ipynb)**" + ] + }, + { + "cell_type": "markdown", + "id": "perfect-blowing", + "metadata": {}, + "source": [ + "### Initialise the basewallet controller" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "treated-respondent", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], + "source": [ + "%autoawait\n", + "import time\n", + "import asyncio\n", + "import pprint\n", + "from aiohttp import ClientResponseError\n", + "\n", + "from aries_basic_controller.aries_controller import AriesAgentController\n", + "\n", + "# Create a small utility to print json formatted outout more human-readable \n", + "pp = pprint.PrettyPrinter(indent=4)\n", + "\n", + "WEBHOOK_HOST = \"0.0.0.0\"\n", + "WEBHOOK_BASE = \"\"\n", + "\n", + "WEBHOOK_PORT = 8022\n", + "ADMIN_URL = \"http://basewallet-agent:8021\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "geographic-people", + "metadata": {}, + "outputs": [], + "source": [ + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL, multitenant=True, mediation=True)\n" + ] + }, + { + "cell_type": "markdown", + "id": "sporting-prairie", + "metadata": {}, + "source": [ + "### Updating JWT of the agent controller" + ] + }, + { + "cell_type": "markdown", + "id": "hazardous-craps", + "metadata": {}, + "source": [ + "Retrieve Alice's token we have stored previously" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "subsequent-bumper", + "metadata": {}, + "outputs": [], + "source": [ + "%store -r alice_jwt" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "opposite-rings", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM\n" + ] + } + ], + "source": [ + "print(alice_jwt)" + ] + }, + { + "cell_type": "markdown", + "id": "cloudy-pepper", + "metadata": {}, + "source": [ + "Now we can update the agent controller with the JWT Token" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "unnecessary-alberta", + "metadata": {}, + "outputs": [], + "source": [ + "agent_controller.update_tennant_jwt(alice_jwt)" + ] + }, + { + "cell_type": "markdown", + "id": "velvet-nothing", + "metadata": {}, + "source": [ + "Let's check it's really there" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "decreased-fiber", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM\n" + ] + } + ], + "source": [ + "print(agent_controller.tennant_jwt)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cloudy-speech", + "metadata": {}, + "outputs": [], + "source": [ + "loop = asyncio.get_event_loop()\n", + "loop.create_task(agent_controller.listen_webhooks())\n", + "\n", + "def cred_handler(payload):\n", + " print(\"Handle Credentials\")\n", + " exchange_id = payload['credential_exchange_id']\n", + " state = payload['state']\n", + " role = payload['role']\n", + " attributes = payload['credential_proposal_dict']['credential_proposal']['attributes']\n", + " print(f\"Credential exchange {exchange_id}, role: {role}, state: {state}\")\n", + " print(f\"Offering: {attributes}\")\n", + " \n", + "cred_listener = {\n", + " \"topic\": \"issue_credential\",\n", + " \"handler\": cred_handler\n", + "}\n", + "\n", + "def connections_handler(payload):\n", + " global STATE\n", + " connection_id = payload[\"connection_id\"]\n", + " print(\"Connection message\", payload, connection_id)\n", + " STATE = payload['state']\n", + " if STATE == 'active':\n", + "# print('Connection {0} changed state to active'.format(connection_id))\n", + " print(colored(\"Connection {0} changed state to active\".format(connection_id), \"red\", attrs=[\"bold\"]))\n", + "\n", + "\n", + "connection_listener = {\n", + " \"handler\": connections_handler,\n", + " \"topic\": \"connections\"\n", + "}\n", + "\n", + "agent_controller.register_listeners([cred_listener,connection_listener], defaults=True)" + ] + }, + { + "cell_type": "markdown", + "id": "breathing-invalid", + "metadata": {}, + "source": [ + "### Go to the [external agent](http://localhost:8889/lab/workspaces/auto-w/tree/Configure%20External%20Agent.ipynb) before you continue, if you haven't already generated an invitation\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "fitted-somalia", + "metadata": {}, + "source": [ + "### Accept Invite From external agent\n", + "\n", + "Replace the invitation object below with the one you have generated in the mediator notebook" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "great-officer", + "metadata": {}, + "outputs": [], + "source": [ + "external_invitation ={'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '557a1e44-8a85-4fd2-8abb-579ebf2a6d39', 'label': 'EXTERNAL', 'recipientKeys': ['923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2'], 'serviceEndpoint': 'https://8298dfdb2447.ngrok.io'}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "opposite-message", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'accept': 'manual',\n", + " 'connection_id': '9f766f69-5f61-48da-9267-21793aed923e',\n", + " 'created_at': '2021-03-19 18:46:41.027967Z',\n", + " 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2',\n", + " 'invitation_mode': 'once',\n", + " 'my_did': '8TLHBjUXHCkkD8aCpYKA2C',\n", + " 'request_id': 'e2ed47b1-9a3d-4ae7-9f8b-6007a38a368e',\n", + " 'rfc23_state': 'request-sent',\n", + " 'routing_state': 'none',\n", + " 'state': 'request',\n", + " 'their_label': 'EXTERNAL',\n", + " 'their_role': 'inviter',\n", + " 'updated_at': '2021-03-19 18:46:41.049804Z'}\n" + ] + } + ], + "source": [ + "response = await agent_controller.connections.accept_connection(external_invitation)\n", + "pp.pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "empirical-dublin", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9f766f69-5f61-48da-9267-21793aed923e\n" + ] + } + ], + "source": [ + "connection_id = response[\"connection_id\"]\n", + "print(connection_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "violent-census", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'invitation_mode': 'once', 'rfc23_state': 'completed', 'routing_state': 'none', 'created_at': '2021-03-19 18:46:41.027967Z', 'my_did': '8TLHBjUXHCkkD8aCpYKA2C', 'connection_id': '9f766f69-5f61-48da-9267-21793aed923e', 'their_role': 'inviter', 'updated_at': '2021-03-19 18:46:42.962588Z', 'state': 'active', 'accept': 'manual', 'their_label': 'EXTERNAL', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'their_did': 'Dd7zXynP78FEiLEivNjW5F', 'request_id': 'e2ed47b1-9a3d-4ae7-9f8b-6007a38a368e'}\n" + ] + } + ], + "source": [ + "### get the connection\n", + "connection = await agent_controller.connections.get_connection(connection_id)\n", + "print(connection)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "decreased-cinema", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Active connection established\n", + "\n", + "State: active\n", + "\n", + "{ 'accept': 'manual',\n", + " 'connection_id': '9f766f69-5f61-48da-9267-21793aed923e',\n", + " 'created_at': '2021-03-19 18:46:41.027967Z',\n", + " 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2',\n", + " 'invitation_mode': 'once',\n", + " 'my_did': '8TLHBjUXHCkkD8aCpYKA2C',\n", + " 'request_id': 'e2ed47b1-9a3d-4ae7-9f8b-6007a38a368e',\n", + " 'rfc23_state': 'completed',\n", + " 'routing_state': 'none',\n", + " 'state': 'active',\n", + " 'their_did': 'Dd7zXynP78FEiLEivNjW5F',\n", + " 'their_label': 'EXTERNAL',\n", + " 'their_role': 'inviter',\n", + " 'updated_at': '2021-03-19 18:46:42.962588Z'}\n" + ] + } + ], + "source": [ + "# Let's check for the state\n", + "def check_connection(connection):\n", + " if connection['state'] != 'active':\n", + " print(\"No active connection. \\n\\nPlease go back and ensure you have established an active connection between the mediator agent and Alice's subwallet agent\\n\") \n", + " print(\"State: \" + connection['state']+ \"\\n\") \n", + " else:\n", + " print(\"Active connection established\\n\")\n", + " print(\"State: \" + connection['state']+ \"\\n\") \n", + " pp.pprint(connection)\n", + "\n", + "check_connection(connection)" + ] + }, + { + "cell_type": "markdown", + "id": "qualified-airplane", + "metadata": {}, + "source": [ + "### Great! Well done, if you made it here. Head back to the notebook of the [external agent](http://localhost:8889/lab/workspaces/auto-w/tree/Configure%20External%20Agent.ipynb) and issue the credential." + ] + }, + { + "cell_type": "markdown", + "id": "increasing-union", + "metadata": {}, + "source": [ + "### Let's have a look at the records\n", + "\n", + "This should give us some results and our submitted record with the credentials for whomever you created in the external notebook should be in there. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "reduced-couple", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Credential exchange 6c15bc24-2afe-4cb1-a41d-8449ab739c7b, role: holder, state: offer_received\n", + "Being offered: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n" + ] + } + ], + "source": [ + "response = await agent_controller.issuer.get_records()\n", + "results = response[\"results\"]\n", + "if len(results) == 0:\n", + " print(\"You need to first send a credential from the issuer notebook (external)\")\n", + "else:\n", + " cred_record = results[0]\n", + " cred_ex_id = cred_record['credential_exchange_id']\n", + " state = cred_record['state']\n", + " role = cred_record['role']\n", + " attributes = results[0]['credential_proposal_dict']['credential_proposal']['attributes']\n", + " print(f\"Credential exchange {cred_ex_id}, role: {role}, state: {state}\")\n", + " print(f\"Being offered: {attributes}\")" + ] + }, + { + "cell_type": "markdown", + "id": "sustainable-retailer", + "metadata": {}, + "source": [ + "### Request Credential from Issuer\n", + "If happy with the attributes being offered in the credential, then the holder requests the credential from the issuer to proceed with the issuance.\n", + "\n", + "It is only possible to request a credential from an exchange when it is in the offer_received state" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "sonic-embassy", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Credential exchange 6c15bc24-2afe-4cb1-a41d-8449ab739c7b, role: holder, state: request_sent\n" + ] + } + ], + "source": [ + "try:\n", + " record = await agent_controller.issuer.send_request_for_record(cred_ex_id)\n", + " state = record['state']\n", + " role = record['role']\n", + " print(f\"Credential exchange {cred_ex_id}, role: {role}, state: {state}\")\n", + "except ClientResponseError as err:\n", + " print(err)" + ] + }, + { + "cell_type": "markdown", + "id": "optimum-sequence", + "metadata": {}, + "source": [ + "### Store the credential\n", + "Once the issuer has responded to a request by sending the credential, the holder needs to store it to save the credential for later.\n", + "\n", + "First check that the credential record is in the credential_received state" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "departmental-portal", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Credential exchange 6c15bc24-2afe-4cb1-a41d-8449ab739c7b, role: holder, state: credential_received\n" + ] + } + ], + "source": [ + "record = await agent_controller.issuer.get_record_by_id(cred_ex_id)\n", + "state = record['state']\n", + "role = record['role']\n", + "print(f\"Credential exchange {cred_ex_id}, role: {role}, state: {state}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "daily-perth", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Credential exchange 6c15bc24-2afe-4cb1-a41d-8449ab739c7b, role: holder, state: credential_acked\n" + ] + } + ], + "source": [ + "try:\n", + " response = await agent_controller.issuer.store_credential(cred_ex_id, \"My OM Credential\")\n", + " state = response['state']\n", + " role = response['role']\n", + " print(f\"Credential exchange {cred_ex_id}, role: {role}, state: {state}\")\n", + "except ClientResponseError as err:\n", + " print(err)" + ] + }, + { + "cell_type": "markdown", + "id": "three-ratio", + "metadata": {}, + "source": [ + "### Great. You're done with this tutorial. \n", + "\n", + "Almost - just terminate the controller below." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "removed-battery", + "metadata": {}, + "outputs": [], + "source": [ + "await agent_controller.terminate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "assisted-breakfast", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/4. Multitenancy/notebooks/basewallet/Part 1 - Subwallet Administration.ipynb b/tutorials/4. Multitenancy/notebooks/basewallet/Part 1 - Subwallet Administration.ipynb new file mode 100644 index 00000000..af4c67b4 --- /dev/null +++ b/tutorials/4. Multitenancy/notebooks/basewallet/Part 1 - Subwallet Administration.ipynb @@ -0,0 +1,515 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "soviet-evaluation", + "metadata": {}, + "source": [ + "# Part 1 - Exploring Sub Wallet Management\n", + "\n", + "This agent has been initialised using the multitennant flag. This means a single ACA-Py instance can be used to manage multiple subwallets. Each tennant gets their own encrypted data storage for managing their own connections, credentials and interactions etc. \n", + "\n", + "A mutli-tennant ACA-Py instance contains a base wallet that is only capable of managing the creation and deletion of subwallets. Subwallets then must authenticate to the ACA-Py agent using a JWT Token generated when the subwallet is created. A tennant agent can do all funcationality of a standard ACA-Py instance.\n", + "\n", + "### Useful links\n", + "\n", + "* [What is mult-tennancy](https://whatis.techtarget.com/definition/multi-tenancy)\n", + "* [ACA-Py mult-tennant documentation](https://github.com/hyperledger/aries-cloudagent-python/blob/main/Multitenancy.md)\n", + "\n", + "\n", + "\n", + "### Tutorial Structure\n", + "\n", + "1. Create a subwallet for Alice (this notebook)\n", + "2. Authenticate as Alice using the tennant_jwt and configure a mediator\n", + "3. Issue Alice a Credential from an External Agent\n", + "4. Alice Issues a Credential to the External Agent" + ] + }, + { + "cell_type": "markdown", + "id": "metallic-barrier", + "metadata": {}, + "source": [ + "### Initialise the basewallet controller" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "actual-morocco", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], + "source": [ + "%autoawait\n", + "import time\n", + "import asyncio\n", + "import pprint\n", + "\n", + "from aries_basic_controller.aries_controller import AriesAgentController\n", + " \n", + "# Create a small utility to print json formatted outout more human-readable \n", + "pp = pprint.PrettyPrinter(indent=4)\n", + " \n", + "WEBHOOK_HOST = \"0.0.0.0\"\n", + "WEBHOOK_BASE = \"\"\n", + "\n", + "WEBHOOK_PORT = 8022\n", + "ADMIN_URL = \"http://basewallet-agent:8021\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "needed-shock", + "metadata": {}, + "outputs": [], + "source": [ + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL, multitenant=True, api_key=\"password\")" + ] + }, + { + "cell_type": "markdown", + "id": "smooth-equation", + "metadata": {}, + "source": [ + "### Check for subwallets on the agent\n", + "\n", + "This should yield an empty result, but not error. That means we successfully asked the basewallet holder multitenant agent about subwallets. ACA-Py instances not in the multitennant configuration will not have access to this API." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "peaceful-yeast", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'results': []}\n" + ] + } + ], + "source": [ + "response = await agent_controller.multitenant.query_subwallets()\n", + "pp.pprint(response)\n" + ] + }, + { + "cell_type": "markdown", + "id": "tired-queens", + "metadata": {}, + "source": [ + "### Let's create a subwallet for Alice\n", + "\n", + "Below is an example payload to achieve this. These properties should be fairly familiar to you if you have been through the other tutorials and looked in the manage file before.\n", + "\n", + "A key different is the `key_management_mode` which is this case is set to `managed`. The base wallet, is managing the keys of Alice's agent. Alice is dependent on them to do this trustworthily" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "substantial-shaft", + "metadata": {}, + "outputs": [], + "source": [ + "## First let's create the payload\n", + "\n", + "payload = {\n", + " \"image_url\": \"https://aries.ca/images/sample.png\",\n", + " \"key_management_mode\": \"managed\",\n", + " \"label\": \"Alice\",\n", + " \"wallet_dispatch_type\": \"default\",\n", + " \"wallet_key\": \"MySecretKey123\",\n", + " \"wallet_name\": \"AlicesWallet\",\n", + " \"wallet_type\": \"indy\",\n", + " \"wallet_webhook_urls\": [\n", + " \"http://localhost:8022/webhooks\"\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "swiss-agency", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'created_at': '2021-03-19 18:14:29.502814Z',\n", + " 'key_management_mode': 'managed',\n", + " 'settings': { 'default_label': 'Alice',\n", + " 'image_url': 'https://aries.ca/images/sample.png',\n", + " 'wallet.dispatch_type': 'default',\n", + " 'wallet.id': '747a13be-d2f3-4746-b8ec-4cef19462a6e',\n", + " 'wallet.name': 'AlicesWallet',\n", + " 'wallet.type': 'indy',\n", + " 'wallet.webhook_urls': ['http://localhost:8022/webhooks']},\n", + " 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM',\n", + " 'updated_at': '2021-03-19 18:14:29.502814Z',\n", + " 'wallet_id': '747a13be-d2f3-4746-b8ec-4cef19462a6e'}\n" + ] + } + ], + "source": [ + "## Now, we create the wallet on the agent \n", + "response_alice = await agent_controller.multitenant.create_subwallet(payload)\n", + "pp.pprint(response_alice)" + ] + }, + { + "cell_type": "markdown", + "id": "palestinian-monster", + "metadata": {}, + "source": [ + "### Let's create another wallet for Joe\n", + "\n", + "Note, that here we have changed the `label` and the `wallet_name` values. The `wallet_name` has to be unique. If you were to try and create another subwallet with the same wallet name, you would receive an error, because wallet names are unique identifiers." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "pacific-jefferson", + "metadata": {}, + "outputs": [], + "source": [ + "## First let's create the payload\n", + "\n", + "payload = {\n", + " \"image_url\": \"https://aries.ca/images/sample.png\",\n", + " \"key_management_mode\": \"managed\",\n", + " \"label\": \"Joe\",\n", + " \"wallet_dispatch_type\": \"default\",\n", + " \"wallet_key\": \"MySecretKey123\",\n", + " \"wallet_name\": \"JoesWallet\",\n", + " \"wallet_type\": \"indy\",\n", + " \"wallet_webhook_urls\": [\n", + " \"http://localhost:8022/webhooks/joe\"\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "moderate-springfield", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'created_at': '2021-03-19 18:14:56.064663Z',\n", + " 'key_management_mode': 'managed',\n", + " 'settings': { 'default_label': 'Joe',\n", + " 'image_url': 'https://aries.ca/images/sample.png',\n", + " 'wallet.dispatch_type': 'default',\n", + " 'wallet.id': '87d68fb0-90db-43a5-abbf-1e7cd09ca29e',\n", + " 'wallet.name': 'JoesWallet',\n", + " 'wallet.type': 'indy',\n", + " 'wallet.webhook_urls': [ 'http://localhost:8022/webhooks/joe']},\n", + " 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI4N2Q2OGZiMC05MGRiLTQzYTUtYWJiZi0xZTdjZDA5Y2EyOWUifQ.mf8wlG_HGi3ZHiF9t1sEoeVw-DsIqEpNHjopYhR3tlw',\n", + " 'updated_at': '2021-03-19 18:14:56.064663Z',\n", + " 'wallet_id': '87d68fb0-90db-43a5-abbf-1e7cd09ca29e'}\n" + ] + } + ], + "source": [ + "## Now, we create the wallet on the agent \n", + "\n", + "response_joe = await agent_controller.multitenant.create_subwallet(payload)\n", + "pp.pprint(response_joe)" + ] + }, + { + "cell_type": "markdown", + "id": "respective-sight", + "metadata": {}, + "source": [ + "### Extract the wallet ID\n", + "\n", + "The wallet id is a unique identifier created by the ACA-Py instance to identify a particular tennant wallet instance. The base wallet controller can use this when interacting with subwallets through the multitennant API." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "turkish-lesson", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Alice's ID: 747a13be-d2f3-4746-b8ec-4cef19462a6e\n", + "Joe's ID: 87d68fb0-90db-43a5-abbf-1e7cd09ca29e\n" + ] + } + ], + "source": [ + "wallet_id_alice = response_alice['wallet_id']\n", + "wallet_id_joe = response_joe['wallet_id']\n", + "\n", + "print(\"Alice's ID: \" + wallet_id_alice)\n", + "print(\"Joe's ID: \" + wallet_id_joe)" + ] + }, + { + "cell_type": "markdown", + "id": "searching-substitute", + "metadata": {}, + "source": [ + "\n", + "### Update a single subwallet\n", + "\n", + "Let's update Joe's wallet label to Joeseph via the controller" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "leading-conflict", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'created_at': '2021-03-19 18:14:56.064663Z',\n", + " 'key_management_mode': 'managed',\n", + " 'settings': { 'default_label': 'Joeseph',\n", + " 'image_url': 'https://aries.ca/images/sample.png',\n", + " 'wallet.dispatch_type': 'default',\n", + " 'wallet.id': '87d68fb0-90db-43a5-abbf-1e7cd09ca29e',\n", + " 'wallet.name': 'JoesWallet',\n", + " 'wallet.type': 'indy',\n", + " 'wallet.webhook_urls': ['http://localhost:8022/webhooks']},\n", + " 'updated_at': '2021-03-19 18:16:13.270256Z',\n", + " 'wallet_id': '87d68fb0-90db-43a5-abbf-1e7cd09ca29e'}\n" + ] + } + ], + "source": [ + "request_body = {\n", + " \"image_url\": \"https://aries.ca/images/sample.png\",\n", + " \"label\": \"Joeseph\",\n", + " \"wallet_dispatch_type\": \"default\",\n", + " \"wallet_webhook_urls\": [\n", + " \"http://localhost:8022/webhooks\"\n", + " ]\n", + "}\n", + "\n", + "response = await agent_controller.multitenant.update_subwallet_by_id(request_body, wallet_id_joe)\n", + "pp.pprint(response)" + ] + }, + { + "cell_type": "markdown", + "id": "blessed-louisville", + "metadata": {}, + "source": [ + "### Remove the subwallet from the agent \n", + "\n", + "We can easily use the controller to remove the subwallets. Note that Joe has no immediate control over the decision of the base wallet controller to remove their wallet. Again Joe trusts the BaseWallet controller to manage his wallet in good faith, or in line with some contractual agreement etc.\n", + "\n", + "Let's go ahead and remove Joe's wallets from the base wallet." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "elegant-wagner", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n" + ] + } + ], + "source": [ + "response_joe = await agent_controller.multitenant.remove_subwallet_by_id(wallet_id_joe)\n", + "\n", + "pp.pprint(response_joe)" + ] + }, + { + "cell_type": "markdown", + "id": "oriented-sympathy", + "metadata": {}, + "source": [ + "### Check Joe's wallet has been removed\n", + "\n", + "This should now give a result only containing Alice's wallet" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "imported-spanish", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'results': [ { 'created_at': '2021-03-19 18:14:29.502814Z',\n", + " 'key_management_mode': 'managed',\n", + " 'settings': { 'default_label': 'Alice',\n", + " 'image_url': 'https://aries.ca/images/sample.png',\n", + " 'wallet.dispatch_type': 'default',\n", + " 'wallet.id': '747a13be-d2f3-4746-b8ec-4cef19462a6e',\n", + " 'wallet.name': 'AlicesWallet',\n", + " 'wallet.type': 'indy',\n", + " 'wallet.webhook_urls': [ 'http://localhost:8022/webhooks']},\n", + " 'updated_at': '2021-03-19 18:14:29.502814Z',\n", + " 'wallet_id': '747a13be-d2f3-4746-b8ec-4cef19462a6e'}]}\n" + ] + } + ], + "source": [ + "response_all_wallets = await agent_controller.multitenant.query_subwallets()\n", + "# response_single_wallet = await agent_controller.multitenant.get_single_subwallet_by_id(wallet_id)\n", + "\n", + "# print(response_single_wallet)\n", + "pp.pprint(response_all_wallets)" + ] + }, + { + "cell_type": "markdown", + "id": "technical-guitar", + "metadata": {}, + "source": [ + "### Get the auth token for a subwallet\n", + "\n", + "Subwallets have unique authentication tokens that can be obtained via the controller" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "scheduled-sustainability", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiI3NDdhMTNiZS1kMmYzLTQ3NDYtYjhlYy00Y2VmMTk0NjJhNmUifQ.sODjftZYiFqNV7b51j1yFeyE43CP6FxJ-DDWBINfTkM'}\n" + ] + } + ], + "source": [ + "response_alice = await agent_controller.multitenant.get_subwallet_authtoken_by_id(wallet_id_alice)\n", + "\n", + "pp.pprint(response_alice)" + ] + }, + { + "cell_type": "markdown", + "id": "equivalent-villa", + "metadata": {}, + "source": [ + "## Store Alice's JWT for use in later tutorials\n", + "\n", + "The % is a magic method to store variables from a notebook to the jupyter runtime" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "partial-north", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Stored 'alice_jwt' (str)\n" + ] + } + ], + "source": [ + "alice_jwt = response_alice[\"token\"]\n", + "%store alice_jwt" + ] + }, + { + "cell_type": "markdown", + "id": "quiet-encyclopedia", + "metadata": {}, + "source": [ + "### Terminate the controller\n", + "\n", + "Let's alos terminate the controller." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "controlling-professional", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "response = await agent_controller.terminate()\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "id": "common-collection", + "metadata": {}, + "source": [ + "### Continue to [Part 2](http://localhost:8888/lab/tree/Alice/Part%202%20-%20Mediation%20of%20communication%20-%20Alice.ipynb) where you will learn how to interact with the ACA-Py instance as Alice" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/aries-stagingnet/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb b/tutorials/4. Multitenancy/notebooks/external/Configure External Agent.ipynb similarity index 66% rename from tutorials/aries-stagingnet/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb rename to tutorials/4. Multitenancy/notebooks/external/Configure External Agent.ipynb index 77fb9b1a..f2cd5f17 100644 --- a/tutorials/aries-stagingnet/notebooks/issuer/Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb +++ b/tutorials/4. Multitenancy/notebooks/external/Configure External Agent.ipynb @@ -4,16 +4,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Writing a Public DID to the Sovrin StagingNet\n", + "# Configure External Agent as Issuer\n", "\n", - "### This notebook walks through how to connect an Aca-Py agent to a live Indy network. In our example we will be connecting to the Sovrin Stagingnet. To view details about Sovrin Stagingnet transactions please visit [Indyscan](https://indyscan.io/home/SOVRIN_STAGINGNET)." + "In this notebook we configure the external agent as an issuer of PyDentity Multi-Tennant Course certificates by register a public DID onto the Sovrin StagingNet and writing a Credential Definition for the PyDentity Multi-Tennant Tutorial credential schema which is already on the ledger.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Initialise a controller for Issuer Agent" + "## 1. Initialise a controller for Issuer" ] }, { @@ -33,82 +33,53 @@ "%autoawait\n", "import time\n", "import asyncio\n", + "import pprint\n", + "import sys\n", + "from termcolor import colored,cprint\n", + "from aiohttp import ClientConnectorError, ClientResponseError\n", + "from asyncio import CancelledError\n", + "\n", "from aries_basic_controller.aries_controller import AriesAgentController\n", - " \n", + " \n", + "# Create a small utility to print json formatted outout more human-readable \n", + "pp = pprint.PrettyPrinter(indent=4)\n", + "\n", "WEBHOOK_HOST = \"0.0.0.0\"\n", "WEBHOOK_BASE = \"\"\n", "\n", - "WEBHOOK_PORT = 8022\n", - "ADMIN_URL = \"http://issuer-agent:8021\"\n", - "\n", - "# Based on the aca-py agent you wish to control\n", - "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", - " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Get current public DID\n", - "\n", - "Before being able to write to any indy based ledger, your agent must have a public DID written on the ledger giving it the authority to write to it. As the cell below shows, this agent does not currently have a public DID. So any writes to the ledger will be rejected." + "WEBHOOK_PORT = 8052\n", + "ADMIN_URL = \"http://external-agent:8051\"\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'result': {'did': 'MjFQDoitt2us2JNpLBGnSY', 'verkey': 'CJGSRQvPb9defKY6w8YW8rprDhKiTqkmexp1P8ioUznY', 'posture': 'public'}}\n" - ] - } - ], - "source": [ - "response = await agent_controller.wallet.get_public_did()\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### THIS WILL FAIL" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "schema_id = 'MGgoJXWbeupKsaHDa7s4fW:2:testabc:0.0.1'\n", - "response = await agent_controller.definitions.write_cred_def(schema_id)" + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL)\n", + " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 3. Generate a new DID\n", - "\n", - "Before being able to write a DID to the ledger, you must create one using the wallet api. This api returns the identifier (the DID), and the verification key for that DID. A representation of it's public key. " + "## 2. Generate DID and Write to Sovrin StagingNet" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "New DID {'did': 'Jqi9JNk9Z637azKekx51V4', 'verkey': 'AixCmDcPfWvZsuMpV2swhvukSYn2e9JbQjXMq4C2GD1H', 'posture': 'wallet_only'}\n" + "New DID {'did': '7DfSpFJMUThna1uQoR1mtY', 'verkey': '4PaGHfTzvxRDS2k38NGGf7pRrMmEGGSiXZZgwz87acGf', 'posture': 'wallet_only'}\n" ] } ], @@ -116,31 +87,25 @@ "# generate new DID\n", "response = await agent_controller.wallet.create_did()\n", "\n", - "did_object = response['result']\n", - "print(\"New DID\", did_object)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Write DID to Sovrin Stagingnet\n", - "\n", - "Anoyone can write a DID to the Sovrin StagingNet, it is a permissionless ledger. \n", - "\n", - "Visit [Sovrin Selfserve Portal](https://selfserve.sovrin.org) for more information. We have provided an automated process to write DIDs to Stagingnet in the step below." + "try:\n", + " did_object = response['result']\n", + " print(\"New DID\", did_object)\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + " # If you wish to get the error stack trace uncomment the line below and run it again\n", + " # raise" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'statusCode': 200, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': '{\"statusCode\": 200, \"Jqi9JNk9Z637azKekx51V4\": {\"status\": \"Success\", \"statusCode\": 200, \"reason\": \"Successfully wrote NYM identified by Jqi9JNk9Z637azKekx51V4 to the ledger with role ENDORSER\"}}'}\n", + "{'statusCode': 200, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': '{\"statusCode\": 200, \"7DfSpFJMUThna1uQoR1mtY\": {\"status\": \"Success\", \"statusCode\": 200, \"reason\": \"Successfully wrote NYM identified by 7DfSpFJMUThna1uQoR1mtY to the ledger with role ENDORSER\"}}'}\n", "200\n" ] } @@ -157,18 +122,21 @@ "# Adding empty header as parameters are being sent in payload\n", "headers = {}\n", "\n", - "r = requests.post(url, data=json.dumps(payload), headers=headers)\n", - "print(r.json())\n", - "print(r.status_code)" + "try:\n", + " r = requests.post(url, data=json.dumps(payload), headers=headers)\n", + " print(r.json())\n", + " print(r.status_code)\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 5. Accepting the Transaction Author Agreement (TAA)\n", + "## 3. Accepting the Transaction Author Agreement (TAA)\n", "\n", - "Although the Sovrin StagingNet is permissionless, before DID's have the authority to write to the ledger they must accept something called a transaction author agreement by signing it using the DID they have on the ledger.\n", + "Although the Sovrin StagingNet is permissionless, before DIDs have the authority to write to the ledger they must accept something called a transaction author agreement by signing it using the DID they have on the ledger.\n", "\n", "As a global public ledger, the Sovrin Ledger and all its participants are subject to privacy and data protection regulations such as the EU General Data Protection Regulation (GDPR). These regulations require that the participants be explicit about responsibilities for Personal Data.\n", "\n", @@ -190,46 +158,74 @@ "* [TAA Acceptance Mechanism List (AML)](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/AML.md)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetch The TAA" + ] + }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [ { - "ename": "ClientResponseError", - "evalue": "400, message=\"Ledger rejected transaction request: client request invalid: InvalidClientTaaAcceptanceError('Txn Author Agreement acceptance is required for ledger with id 1',).\", url=URL('http://issuer-agent:8021/wallet/did/public?did=Jqi9JNk9Z637azKekx51V4')", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mClientResponseError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36masync-def-wrapper\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/aries_basic_controller/controllers/wallet.py\u001b[0m in \u001b[0;36massign_public_did\u001b[0;34m(self, did)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\"did\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mdid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m }\n\u001b[0;32m---> 27\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_POST\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{self.base_url}/did/public\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 28\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32masync\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget_did_endpoint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/aries_basic_controller/controllers/base.py\u001b[0m in \u001b[0;36madmin_POST\u001b[0;34m(self, path, json_data, text, params, data)\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\" with data: \\n{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepr_json\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjson_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mjson_data\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m )\n\u001b[0;32m---> 95\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"POST\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 96\u001b[0m EVENT_LOGGER.debug(\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\"Response from POST %s received: \\n%s\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/aries_basic_controller/controllers/base.py\u001b[0m in \u001b[0;36madmin_request\u001b[0;34m(self, method, path, json_data, text, params, data)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madmin_url\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjson_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m ) as resp:\n\u001b[0;32m---> 63\u001b[0;31m \u001b[0mresp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_for_status\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 64\u001b[0m \u001b[0mresp_text\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mresp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mresp_text\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/conda/lib/python3.7/site-packages/aiohttp/client_reqrep.py\u001b[0m in \u001b[0;36mraise_for_status\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 944\u001b[0m \u001b[0mstatus\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 945\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreason\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 946\u001b[0;31m headers=self.headers)\n\u001b[0m\u001b[1;32m 947\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 948\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_cleanup_writer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mClientResponseError\u001b[0m: 400, message=\"Ledger rejected transaction request: client request invalid: InvalidClientTaaAcceptanceError('Txn Author Agreement acceptance is required for ledger with id 1',).\", url=URL('http://issuer-agent:8021/wallet/did/public?did=Jqi9JNk9Z637azKekx51V4')" - ] - }, - { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "Warning: Output is not a terminal (fd=None).\n" + "{'text': '\\ufeff# Transaction Author Agreement V2\\nhttps://sovrin.org/\\n\\n\\n## Summary:\\n\\n\\nThis summary is provided to help you understand your obligations when writing to\\nthe Sovrin Ledger Networks-it does not have any legal effect or replace the full\\nlegal text of the agreement provided below it.\\n\\n\\n- This agreement grants you permission to write data to the Sovrin Ledger\\n Networks under certain terms and conditions.\\n\\n\\n- You represent and warrant that the data you are writing does not violate any\\n applicable laws or infringe the rights of any other party.\\n\\n\\n- You understand the data you are writing is public and permanent and there can\\n be no guarantee of erasure. This includes public keys and payment addresses.\\n\\n\\n- If it is determined that the data you wrote violated this agreement, the\\n operators of the network can take steps to block it from public access.\\n\\n\\n- The Sovrin Foundation makes no promises about the reliability or correctness\\n of the data being stored on the Sovrin Ledger Networks or the operation of the\\n Sovrin Ledger Networks.\\n\\n\\n--------------------------------------------------------------------------------\\n\\n\\n## Agreement: Approved by the Sovrin Board of Trustees 04 December 2019\\n\\n\\nThis Transaction Author Agreement (the \"**Agreement**\") is entered into on the\\ndate you accepted this Agreement (the \"**Effective Date**\") between the Sovrin\\nFoundation, a nonprofit corporation organized under the laws of the State of\\nUtah, United States of America (\"**Sovrin Foundation**\"), and you\\n(\"**Transaction Author**\"), either an entity or a natural person acting as\\nan Individual. Sovrin Foundation and Transaction Author are individually\\nreferred to herein as a \"Party\" and collectively as the \"Parties\". All\\nreferences to \"you\" throughout this Agreement will include that person or\\nentity. You represent that you are authorized to accept this Agreement on that\\nperson’s or entity’s behalf, and in the event you or the person or entity\\nviolates this Agreement, the person or entity agrees to be responsible to the\\nSovrin Foundation.\\n\\n\\nBy clicking \"Accept\" or similar or writing Transactions to the Sovrin Ledger\\nNetworks, Transaction Author agrees to be bound by this Agreement and all terms\\nincorporated by reference. If Transaction Author does not agree to this\\nAgreement in its entirety, do not click \"Accept\" or write Transactions to the\\nSovrin Ledger Networks.\\n\\n\\nIf Sovrin Foundation makes material changes to this Agreement, Sovrin Foundation will notify you\\nby posting a notice on Sovrin Foundation’s website prior to the effective date of the changes. \\nBy continuing to act as a Transaction Author or by otherwise writing Transactions on the\\nSovrin Ledger Networks after Sovrin Foundation posts changes to their website, you agree to be\\nbound by the revised Agreement.\\n\\n\\nWHEREAS, Transaction Author desires to write Transactions to the Sovrin\\nLedger Networks (each a \"**Transaction**\"); and\\n\\n\\nWHEREAS, subject to Transaction Author complying with the terms and\\nconditions of this Agreement, Sovrin Foundation grants permission to \\nTransaction Author to write Transactions to the Sovrin Ledger Networks;\\n\\n\\nFOR GOOD AND VALUABLE CONSIDERATION, THE SUFFICIENCY OF WHICH IS HEREBY\\nACKNOWLEDGED, THE PARTIES AGREE AS FOLLOWS:\\n\\n\\n\\n\\n## 1) Definitions\\n\\n\\na. \"**Data Protection Laws**\" means the GDPR and any other data protection and\\nprivacy laws, regulations, and regulatory requirements applicable to a party\\nunder this Agreement.\\n\\n\\nb. \"**GDPR**\" means the General Data Protection Regulation (EU) 2016/679\\non the protection of natural persons with regard to the Processing of personal\\ndata and on the free movement of such data, and repealing Directive 95/46/EC,\\nand any amendment or replacement to it.\\n\\n\\nc. \"**Impermissible Personal Data**\" means the Personal Data that\\nTransaction Author writes to the Sovrin Ledger Networks that is not\\nPermissible Personal Data.\\n\\n\\nd. \"**Personal Data Transactions**\" has the meaning set forth in Section 3\\nbelow.\\n\\n\\ne. \"**Permissible Personal Data**\" means Personal Data that Transaction\\nAuthor writes to the Sovrin Ledger Networks that is permitted under this\\nAgreement and the Sovrin Governance Framework (including the\\n[Sovrin Ledger Access Policies](https://sovrin.org/wp-content/uploads/Sovrin-Ledger-Access-Policies-V1.pdf)).\\n\\n\\nf. \"**Personal Data**\" means information that relates, directly or\\nindirectly, to a data subject, including without limitation, names, email\\naddresses, postal addresses, identification numbers, location data, online\\nidentifiers or one or more factors specific to the physical, physiological,\\ngenetic, mental, economic, cultural or social identity of the data subject.\\n\\n\\ng. \"**Process**\" or \"**Processing**\" means any operation or set of\\noperations which is performed on Transactions data, whether or not by\\nautomated means, such as the access, collection, use, storage, disclosure,\\ndissemination, combination, recording, organization, structuring, adaption,\\nalteration, copying, transfer, retrieval, consultation, disposal, restriction,\\nerasure and/or destruction of Transactions data.\\n\\n\\nh. \"**Sovrin Governance Framework**\" means Sovrin Foundation’s\\ngovernance policies and rules available at\\nhttps://sovrin.org/governance-framework/ or any successor website.\\n\\n\\nUnless otherwise defined above, all capitalized terms used in this Agreement\\nshall have the meanings given to them in this Agreement or in the Sovrin\\nGovernance Framework and corresponding Sovrin Glossary. The Sovrin Governance\\nFramework and Sovrin Glossary is incorporated into this Agreement by reference\\nonly for purposes of use of such defined terms.\\n\\n\\n\\n\\n## 2) Permission to Write to the Sovrin Ledger Networks\\n\\n\\na. Sovrin Foundation hereby grants to Transaction Author a non-exclusive,\\nnon-assignable, non-sublicensable, royalty free, revocable license to write to\\nand use the Sovrin Ledger Networks in accordance with this Agreement and the\\nSovrin Governance Framework.\\n\\n\\nb. When authoring Transactions under the policy of Permissioned Write Access,\\na Transaction Author may only write to the Sovrin Ledger Networks by using an\\nauthorized Transaction Endorser. In the event that Sovrin Foundation enables\\nPublic Write Access to the Sovrin Ledger Networks, Transaction Author will\\nnot need a Transaction Endorser to endorse a Transaction.\\n\\n\\nc. Once an initial Transaction has been written to one of the Sovrin Ledger\\nNetworks by Transaction Author (\"**Initial Transaction**\"), the\\nTransaction Author is granted permission to make additional Transactions to\\nupdate the state of a previous Transaction (\"**Update Transactions**\"). Please\\nnote, an Update Transaction does not remove the Initial Transaction, which\\nwill remain on the Sovrin Ledger due to its immutability and may remain on\\nSovrin Test Networks unless they are reset. Transaction Author may make Update\\nTransactions if and only if Transaction Author was the author of the\\nInitial Transaction. Update Transactions are Transactions and are subject to\\nall the terms of this Agreement.\\n\\n\\n\\n\\n## 3) Transaction Author Obligations\\n\\n\\na. With regard to all Transactions, Transaction Author will:\\n\\n\\n 1. comply with any requirements imposed by the Transaction Endorser on the\\n Transaction Author and any Transactions endorsed by the Transaction\\n Endorser;\\n\\n\\n 2. not write Transactions containing Personal Data until Sovrin Foundation\\n approves Public Write Access and permits Transactions to contain Personal\\n Data pursuant to Section 3(b) below.\\n\\n\\nb. If Sovrin Foundation approves Public Write Access and permits\\nTransaction Authors to write Transactions that contain Permissible Personal\\nData (\"**Personal Data Transactions**\"), then Transaction Author expressly\\nagrees that:\\n\\n\\n 1. It will not write any Transactions that contain Impermissible Personal\\n Data to the Sovrin Ledger Networks;\\n\\n\\n 2. it is an independent data controller (as defined in the GDPR) of the\\n Personal Data Transactions and will be responsible for the lawfulness of the\\n Processing of such data in compliance with the Data Protection Laws;\\n\\n\\n 3. it acknowledges and will notify all data subjects whose Personal Data it\\n Processes that functions inherent in blockchain technology may render\\n fulfilling data subject requests difficult or impossible. For example, due\\n to blockchain’s immutability, data stored on a blockchain generally cannot\\n be removed or altered once the data is confirmed on the blockchain;\\n\\n\\n 4. it irrevocably waives any and all claims, rights and/or obligations it\\n may have now or in the future against Sovrin Foundation as a result of\\n being unable to fulfill data subject requests in accordance with Data\\n Protection Laws;\\n\\n\\n 5. it agrees to be bound by the terms and conditions applicable to\\n Transaction Author as a controller of Personal Data Transactions under the\\n Data Processing Agreements with Stewards and Transaction Endorsers, each in\\n their roles as processors under the GDPR, and attached as Appendices A and B\\n (the \"**DPAs**\");\\n\\n\\n 6. that Sovrin Foundation has the right to enter into the DPAs on its\\n behalf and the DPAs are made a part of the Agreement in their entirety;\\n\\n\\n 7. by signing this Agreement, each Party is deemed to have signed the DPAs,\\n including the Standard Contractual Clauses with Sovrin Foundation and\\n Transaction Author as the \"Data exporter\", and with either a Steward or a\\n Transaction Endorser as \"Data importer\", as applicable;\\n\\n\\n 8. at Sovrin Foundation’s request, Transaction Author will reimburse\\n Sovrin Foundation for any costs incurred by Sovrin Foundation in\\n enforcing Transaction Author’s rights under the GDPR, including but not\\n limited to fulfillment of data subject rights, rights of oversight and\\n audit, etc.; and\\n\\n\\n 9. it irrevocably waives any and all claims that it may have now or in the\\n future that Sovrin Foundation lacks the rights to enter into the DPAs on\\n its behalf and bind Transaction Author to the DPAs’ terms and conditions,\\n including the limitation of liability therein.\\n\\n\\n\\n\\n## 4) Term and Termination\\n\\n\\na. This Agreement commences on the Effective Date and shall remain in force\\nuntil terminated by either Party pursuant to this **Section 4 (Term and\\nTermination)**.\\n\\n\\nb. Either Party may terminate this Agreement: (i) if the other Party has\\nmaterially defaulted in the performance of any of its obligations under this\\nAgreement and has not cured such default within fifteen (15) business days of\\nreceipt of written notice from the non-defaulting Party of such default or\\n(ii) immediately in the event of any government sanctions or other legal\\nmeasures that make it unlawful for Transaction Author to write Transactions to\\nthe Sovrin Ledger Networks.\\n\\n\\nc. Additionally, Transaction Author may terminate this Agreement upon 30 days\\'\\nadvance written notice to Sovrin Foundation and ceasing all use of the\\nSovrin Ledger Networks.\\n\\n\\nd. Upon termination or expiration of this Agreement for any reason the rights\\ngranted to Transaction Author by Sovrin Foundation under this Agreement\\nautomatically terminate.\\n\\n\\n\\n\\n## 5) Representations and Warranties; Disclaimer\\n\\n\\na. By Sovrin Foundation.\\n\\n\\n 1. THE SOVRIN LEDGER NETWORKS AND THE SOVRIN NETWORK ARE PROVIDED AS-IS WITH\\n ALL FAULTS. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, THE SOVRIN\\n FOUNDATION MAKES NO REPRESENTATION OR WARRANTY CONCERNING THE ACCURACY,\\n RELIABILITY, OR COMPLETENESS OF ANY INFORMATION OR DATA OBTAINED OR DERIVED\\n THROUGH THE USE OF THE SOVRIN LEDGER NETWORKS AS THE SOVRIN LEDGER NETWORKS\\n OPERATE ON A DISTRIBUTED NETWORK AND SOVRIN FOUNDATION DOES NOT CONTROL\\n THE INFORMATION OR DATA WRITTEN TO THE SOVRIN LEDGER NETWORKS. THE SOVRIN\\n FOUNDATION DISCLAIMS ANY OTHER REPRESENTATIONS OR WARRANTIES, EXPRESS OR\\n IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR\\n FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ACCURACY OR\\n COMPLETENESS OF DATA.\\n\\n\\n 2. As the architect of the Sovrin Network and administrator of the Sovrin\\n Governance Framework, Sovrin Foundation is an independent controller of\\n the Personal Data Transactions. In no event will Sovrin Foundation be\\n held liable for the actions or omissions of Transaction Author arising out\\n any Personal Data that Transaction Author writes to the Sovrin Ledger\\n Networks in breach of this Agreement and the Sovrin Governance Framework,\\n including but not limited to any Impermissible Personal Data.\\n Notwithstanding the foregoing, if Transaction Author writes Permissible\\n Personal Data to the Sovrin Ledger Networks in express compliance with this\\n Agreement and the Sovrin Governance Framework, Sovrin Foundation is\\n responsible for the lawfulness of such Processing once such Permissible\\n Personal Data is written to the Sovrin Ledger Networks.\\n\\n\\nb. By Transaction Author. Transaction Author represents and warrants:\\n\\n\\n 1. if a natural person, he or she is 16 years of age or older;\\n\\n\\n 2. it has all necessary rights and permissions to write the Transactions;\\n\\n\\n 3. the Transactions do not and will not violate any applicable law;\\n\\n\\n 4. the Transactions will not contain data or information that infringes or\\n misappropriates the intellectual property rights of any third party;\\n\\n\\n 5. it understands that the Sovrin Ledger Networks operate on a distributed\\n network and that Sovrin Foundation disclaims any responsibilities with\\n respect to access of data from the Sovrin Ledger Networks;\\n\\n\\n 6. it understands and acknowledges that Sovrin Foundation does not control the\\n transfer of data between Nodes and over communications facilities,\\n including the internet, and that the Sovrin Ledger Networks may be subject\\n to limitations, delays, and other problems inherent in the use of such\\n communications facilities;\\n\\n\\n 7. it understands and acknowledges that there is regulatory uncertainty\\n regarding the Sovrin Ledger Networks’ compliance with Data Protection Laws\\n as it relates to Permissioned Write Access, Public Write Access, and\\n Personal Data, including cross-border transfers of data, Processing of\\n Personal Data, the right to effective erasure of data, as well as the scope\\n and nature of Personal Data itself;\\n\\n\\n 8. it understands and acknowledges that Sovrin Foundation may modify, at any\\n time, its Sovrin Ledger Access Policies and the terms of this Agreement and\\n any other agreement or document related to the Sovrin Ledger Networks based\\n on new information, guidance, or Data Protection Laws; and\\n\\n\\n 9. it understands and acknowledges that a Steward and/or Sovrin Foundation\\n may obscure a Transaction if (i) the Steward or Sovrin Foundation is\\n required to do so by a court order or applicable law or (ii) the Steward or\\n Sovrin Foundation has evidence that the Transaction violates the terms of\\n this Agreement or any applicable law.\\n\\n\\n\\n\\n## 6) Indemnification\\n\\n\\na. To the fullest extent permitted by applicable law, Transaction Author will\\nindemnify and hold harmless Sovrin Foundation, and each of its respective\\nofficers, directors, agents, partners and employees (individually and\\ncollectively, the \"**Sovrin Parties**\") from and against any losses,\\nliabilities, claims, demands, damages, expenses or costs (\"**Claims**\")\\nbrought by a third party arising out of or related to (i) Transaction Author’s\\naccess to or use of the Sovrin Ledger Networks in violation of this Agreement;\\n(ii) Transaction Author’s violation, misappropriation or infringement of any\\nrights of another (including intellectual property rights or privacy rights);\\nor (iii) Transaction Author’s violation of applicable law.\\n\\n\\nb. Transaction Author agrees to promptly notify the Sovrin Parties in writing\\nof any Claims, cooperate with the Sovrin Parties in defending such Claims and\\npay all fees, costs and expenses associated with defending such Claims\\n(including attorneys’ fees). Transaction Author also agrees that the Sovrin\\nParties will have sole control of the defense or settlement, at the Sovrin\\nFoundation’s sole option, of any Claims. This indemnity is in addition to, and\\nnot in lieu of, any other indemnities set forth in a written agreement between\\nTransaction Author and Sovrin Foundation or the other Sovrin Parties.\\n\\n\\n\\n\\n## 7) Governing Law and Forum\\n\\n\\nThis Agreement is governed by the law of the State of Delaware, without\\nreference to conflict of laws principles; provided that, if Transaction Author\\nis a governmental entity, this Agreement is governed by the law in which such\\ngovernmental entity is established. All disputes arising out of or in connection\\nwith this Agreement shall be finally settled by binding arbitration under the\\nRules of Arbitration of the International Chamber of Commerce (the \"**Rules**\")\\nby a single arbitrator appointed in accordance with said Rules. Arbitration\\nproceedings will be held in London, England. Unless the Parties otherwise\\nmutually agree, such arbitration shall be conducted in the English language by\\nelectronic exchange of documents and by video conference. The arbitrator shall\\nissue a reasoned decision, including findings of fact and conclusions of law.\\nThe arbitrator shall require exchange by the Parties of documents relevant to\\nthe issues raised by any claim, defense, or counterclaim or on which the\\nproducing Party may rely in support of or in opposition to any claim, defense,\\nor counterclaim, with due regard for eliminating undue burden and expense and\\nthe expedited and lower cost nature of arbitration. At the request of a Party,\\nthe arbitrator may at his or her discretion order the deposition of witnesses.\\nDepositions shall be limited to a maximum of three depositions per Party, each\\nof a maximum of four hours duration, unless the arbitrator otherwise determines.\\nDemand for arbitration may be initiated by either Party on fifteen (15) days\\nwritten notice by email to the other Party’s designated representative, together\\nwith a written specification of the grounds for the dispute and the relief\\nrequested. By agreeing to binding and non-appealable arbitration, each party\\nunderstands that they each forever give up and waive any right which each Party\\nmay have to resolve any such claim, difference or dispute by court or jury\\ntrial. Notwithstanding the foregoing, either Party may bring a proceeding\\nseeking equitable or injunctive relief solely and exclusively in the state and\\nfederal courts located in Wilmington, Delaware, to prevent the infringement of\\nintellectual property rights or the disclosure of confidential information. Each\\nParty hereto consents to the exclusive jurisdiction of such courts for the\\nadjudication of any such equitable or injunctive relief, as well as for any such\\nmatters that are excluded from or fall outside of this arbitration provision.\\n\\n\\n\\n\\n## 8) Limitation of Liability\\n\\n\\nEXCEPT IN THE EVENT OF EITHER PARTY’S GROSS NEGLIGENCE, WILLFUL MISCONDUCT OR\\nFRAUD, IN NO EVENT SHALL EITHER PARTY BE LIABLE FOR ANY INDIRECT, INCIDENTAL,\\nEXEMPLARY, PUNITIVE, SPECIAL, OR OTHER CONSEQUENTIAL DAMAGES UNDER THIS\\nAGREEMENT, INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS\\nINTERRUPTION, LOSS OF PROGRAMS OR DATA, OR OTHERWISE, EVEN IF THE OTHER PARTY IS\\nEXPRESSLY ADVISED OF THE POSSIBILITY OR LIKELIHOOD OF SUCH DAMAGES. EXCEPT IN\\nTHE EVENT OF EITHER PARTY’S GROSS NEGLIGENCE, WILLFUL MISCONDUCT OR FRAUD, IN NO\\nEVENT SHALL EITHER PARTY’S LIABILITY UNDER THIS AGREEMENT EXCEED $250,000 USD IN\\nTHE AGGREGATE, PROVIDED THAT THERE WILL BE NO DOLLAR CAP ON LIABILITY FOR\\nDAMAGES ARISING FROM VIOLATIONS OF DATA PROTECTION LAWS. IN THE EVENT OF EITHER\\nPARTY’S GROSS NEGLIGENCE, SUCH PARTY’S LIABILITY UNDER THIS AGREEMENT SHALL NOT\\nEXCEED $500,000 USD IN THE AGGREGATE. IN THE EVENT OF EITHER PARTY’S WILLFUL\\nMISCONDUCT OR FRAUD, THERE SHALL BE NO DOLLAR CAP ON SUCH PARTY’S LIABILITY\\nUNDER THIS AGREEMENT.\\n\\n\\n\\n\\n## 9) Miscellaneous\\n\\n\\na. Notice. Any notice, payment, demand or communication required or permitted\\nto be delivered or given by the provisions of this Agreement shall be deemed\\nto have been effectively delivered or given and received on the date\\npersonally or electronically delivered to the respective Party to whom it is\\ndirected, or when deposited by registered or certified mail, with postage and\\ncharges prepaid and addressed to each respective Party. For the Transaction\\nAuthor, notices will be sent to the agent service endpoint of the Transaction\\nAuthor’s DID as long as Transaction Author authorizes such a connection or\\nsent via other mechanism agreed to by the parties. For Sovrin Foundation,\\nnotices will be sent to Attn: Legal, 86 N University Ave, Suite 110, Provo, UT\\n84601.\\n\\n\\nb. Severability. If any provision of this Agreement is held invalid, illegal,\\nor unenforceable, the validity, legality, and enforceability of any of the\\nremaining provisions of this Agreement shall not in any way be affected or\\nimpaired.\\n\\n\\nc. Relationship of the Parties. This Agreement does not create a partnership,\\nfranchise, joint venture, agency, fiduciary or employment relationship between\\nthe Parties. Neither Party will represent that it has any authority to assume\\nor create any obligation, express or implied, on behalf of the other Party,\\nnor to represent the other Party as agent, employee, franchisee, or in any\\nother capacity. There are no third-party beneficiaries to this Agreement.\\nNeither Party shall make any proposals, promises, warranties, guarantees, or\\nrepresentations on behalf of the other Party or in the other Party’s name.\\n\\n\\nd. Assignment. Neither Party will voluntarily, or by operation of law, assign\\nor otherwise transfer this Agreement without the other Party’s express prior\\nwritten consent which will not be unreasonably withheld, provided that no such\\nconsent is required for an assignment or transfer to a wholly or majority\\nowned subsidiary or to a successor in interest by reason of merger or\\nconsolidation or sale of all or substantially all of the assets of such Party\\nrelating to the subject matter of this Agreement.\\n\\n\\ne. Waiver. The waiver by either Party of a breach, default, delay or omission\\nof any of the provisions of this Agreement by the other Party will not be\\nconstrued as a waiver of any subsequent breach of the same or other\\nprovisions.\\n\\n\\nf. Entire Agreement. This Agreement, including all documents incorporated into\\nthis Agreement by reference, constitutes the entire agreement of the Parties\\nwith respect to the subject matter of this Agreement, and supersedes any and\\nall prior agreements and understandings of the Parties, whether written or\\noral, with respect to such subject matter. This Agreement supersedes all prior\\nTransaction Author Agreements between Sovrin Foundation and Transaction Author\\nwith respect to the subject matter hereof.\\n\\n\\ng. Modification of This Agreement. Sovrin Foundation reserves the right to\\nmodify this Agreement at any time in accordance with this provision,\\nincluding, but not limited to, changes in applicable law or guidance from any\\njurisdiction. Sovrin Foundation will post an amended version of this Agreement\\non its website at least forty-five (45) days prior to the date on which all\\nTransaction Authors must begin operating under the amendment (the \"**Amendment\\nCutover Date**\"). If Transaction Author continues to Author Transactions to\\nthe Sovrin Ledger Networks after the Amendment Cutover Date, such continued\\nuse will constitute acceptance of the amended Agreement.\\n\\n\\nh. Counterparts. This Agreement may be executed in two or more counterparts,\\neach of which will be deemed an original, but all of which taken together will\\nconstitute one and the same instrument\\n\\n\\ni. Survival. Any terms that by their nature survive termination or expiration\\nof this Agreement shall survive.\\n\\n\\nj. Governmental Entities. If Transaction Author is a governmental entity and\\nit determines that GDPR does not apply to it and its Processing of\\nTransactions, then:\\n - a. to the extent that the GDPR requirements referenced in this Agreement are\\n equivalent to the requirements under Data Protection Laws in its own\\n jurisdiction, it will comply with any such requirements; and\\n\\n\\n - b. to the extent that GDPR requirements referenced in this Agreement differ\\n from requirements under Data Protection Laws in its own jurisdiction, it\\n will comply with the requirements under its own legislation.\\n\\n\\n\\n\\n## Appendix A\\n\\n\\n- **Steward Data Processing Agreement**\\n[LINK](https://sovrin.org/wp-content/uploads/Steward-Data-Processing-Agreement-V1.pdf)\\nAS OF THE EFFECTIVE DATE OF THIS TRANSACTION AUTHOR AGREEMENT\\n\\n\\n\\n\\n## Appendix B\\n\\n\\n- **Transaction Endorser Data Processing Agreement**\\n[LINK](https://sovrin.org/wp-content/uploads/Transaction-Endorser-Data-Processing-Agreement-V1.pdf)\\nAS OF THE EFFECTIVE DATE OF THIS TRANSACTION AUTHOR AGREEMENT\\n\\n\\n(c) 2019 by Sovrin Foundation. This work is licensed under the Creative Commons\\nAttribution-ShareAlike 4.0 International License\\n(http://creativecommons.org/licenses/by-sa/4.0/).', 'digest': '8cee5d7a573e4893b08ff53a0761a22a1607df3b3fcd7e75b98696c92879641f', 'ratification_ts': 1575417600, 'version': '2.0', 'mechanism': 'service_agreement'}\n" ] - }, + } + ], + "source": [ + "try:\n", + " response = await agent_controller.ledger.get_taa()\n", + " TAA = response['result']['taa_record']\n", + " TAA['mechanism'] = \"service_agreement\"\n", + " print(TAA)\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + " raise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accept the TAA" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\u001b[0m\u001b[?7h\u001b[0;34mError during POST /wallet/did/public: 400, message=\"Ledger rejected transaction request: client request invalid: InvalidClientTaaAcceptanceError('Txn Author Agreement acceptance is required for ledger with id 1',).\", url=URL('http://issuer-agent:8021/wallet/did/public?did=Jqi9JNk9Z637azKekx51V4')\u001b[0m\r\n", - "\u001b[0m" + "{}\n" ] } ], "source": [ - "# This will not work until you have accepted the TAA\n", - "response = await agent_controller.wallet.assign_public_did(did_object[\"did\"])\n", - "print(response)" + "try:\n", + " response = await agent_controller.ledger.accept_taa(TAA)\n", + " ## Will return {} if successful\n", + " print(response)\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + " raise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Set Public DID\n", + "\n", + "Now you have accepted the TAA you should be able to set your DID as public" ] }, { @@ -241,144 +237,339 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'text': '\\ufeff# Transaction Author Agreement V2\\nhttps://sovrin.org/\\n\\n\\n## Summary:\\n\\n\\nThis summary is provided to help you understand your obligations when writing to\\nthe Sovrin Ledger Networks-it does not have any legal effect or replace the full\\nlegal text of the agreement provided below it.\\n\\n\\n- This agreement grants you permission to write data to the Sovrin Ledger\\n Networks under certain terms and conditions.\\n\\n\\n- You represent and warrant that the data you are writing does not violate any\\n applicable laws or infringe the rights of any other party.\\n\\n\\n- You understand the data you are writing is public and permanent and there can\\n be no guarantee of erasure. This includes public keys and payment addresses.\\n\\n\\n- If it is determined that the data you wrote violated this agreement, the\\n operators of the network can take steps to block it from public access.\\n\\n\\n- The Sovrin Foundation makes no promises about the reliability or correctness\\n of the data being stored on the Sovrin Ledger Networks or the operation of the\\n Sovrin Ledger Networks.\\n\\n\\n--------------------------------------------------------------------------------\\n\\n\\n## Agreement: Approved by the Sovrin Board of Trustees 04 December 2019\\n\\n\\nThis Transaction Author Agreement (the \"**Agreement**\") is entered into on the\\ndate you accepted this Agreement (the \"**Effective Date**\") between the Sovrin\\nFoundation, a nonprofit corporation organized under the laws of the State of\\nUtah, United States of America (\"**Sovrin Foundation**\"), and you\\n(\"**Transaction Author**\"), either an entity or a natural person acting as\\nan Individual. Sovrin Foundation and Transaction Author are individually\\nreferred to herein as a \"Party\" and collectively as the \"Parties\". All\\nreferences to \"you\" throughout this Agreement will include that person or\\nentity. You represent that you are authorized to accept this Agreement on that\\nperson’s or entity’s behalf, and in the event you or the person or entity\\nviolates this Agreement, the person or entity agrees to be responsible to the\\nSovrin Foundation.\\n\\n\\nBy clicking \"Accept\" or similar or writing Transactions to the Sovrin Ledger\\nNetworks, Transaction Author agrees to be bound by this Agreement and all terms\\nincorporated by reference. If Transaction Author does not agree to this\\nAgreement in its entirety, do not click \"Accept\" or write Transactions to the\\nSovrin Ledger Networks.\\n\\n\\nIf Sovrin Foundation makes material changes to this Agreement, Sovrin Foundation will notify you\\nby posting a notice on Sovrin Foundation’s website prior to the effective date of the changes. \\nBy continuing to act as a Transaction Author or by otherwise writing Transactions on the\\nSovrin Ledger Networks after Sovrin Foundation posts changes to their website, you agree to be\\nbound by the revised Agreement.\\n\\n\\nWHEREAS, Transaction Author desires to write Transactions to the Sovrin\\nLedger Networks (each a \"**Transaction**\"); and\\n\\n\\nWHEREAS, subject to Transaction Author complying with the terms and\\nconditions of this Agreement, Sovrin Foundation grants permission to \\nTransaction Author to write Transactions to the Sovrin Ledger Networks;\\n\\n\\nFOR GOOD AND VALUABLE CONSIDERATION, THE SUFFICIENCY OF WHICH IS HEREBY\\nACKNOWLEDGED, THE PARTIES AGREE AS FOLLOWS:\\n\\n\\n\\n\\n## 1) Definitions\\n\\n\\na. \"**Data Protection Laws**\" means the GDPR and any other data protection and\\nprivacy laws, regulations, and regulatory requirements applicable to a party\\nunder this Agreement.\\n\\n\\nb. \"**GDPR**\" means the General Data Protection Regulation (EU) 2016/679\\non the protection of natural persons with regard to the Processing of personal\\ndata and on the free movement of such data, and repealing Directive 95/46/EC,\\nand any amendment or replacement to it.\\n\\n\\nc. \"**Impermissible Personal Data**\" means the Personal Data that\\nTransaction Author writes to the Sovrin Ledger Networks that is not\\nPermissible Personal Data.\\n\\n\\nd. \"**Personal Data Transactions**\" has the meaning set forth in Section 3\\nbelow.\\n\\n\\ne. \"**Permissible Personal Data**\" means Personal Data that Transaction\\nAuthor writes to the Sovrin Ledger Networks that is permitted under this\\nAgreement and the Sovrin Governance Framework (including the\\n[Sovrin Ledger Access Policies](https://sovrin.org/wp-content/uploads/Sovrin-Ledger-Access-Policies-V1.pdf)).\\n\\n\\nf. \"**Personal Data**\" means information that relates, directly or\\nindirectly, to a data subject, including without limitation, names, email\\naddresses, postal addresses, identification numbers, location data, online\\nidentifiers or one or more factors specific to the physical, physiological,\\ngenetic, mental, economic, cultural or social identity of the data subject.\\n\\n\\ng. \"**Process**\" or \"**Processing**\" means any operation or set of\\noperations which is performed on Transactions data, whether or not by\\nautomated means, such as the access, collection, use, storage, disclosure,\\ndissemination, combination, recording, organization, structuring, adaption,\\nalteration, copying, transfer, retrieval, consultation, disposal, restriction,\\nerasure and/or destruction of Transactions data.\\n\\n\\nh. \"**Sovrin Governance Framework**\" means Sovrin Foundation’s\\ngovernance policies and rules available at\\nhttps://sovrin.org/governance-framework/ or any successor website.\\n\\n\\nUnless otherwise defined above, all capitalized terms used in this Agreement\\nshall have the meanings given to them in this Agreement or in the Sovrin\\nGovernance Framework and corresponding Sovrin Glossary. The Sovrin Governance\\nFramework and Sovrin Glossary is incorporated into this Agreement by reference\\nonly for purposes of use of such defined terms.\\n\\n\\n\\n\\n## 2) Permission to Write to the Sovrin Ledger Networks\\n\\n\\na. Sovrin Foundation hereby grants to Transaction Author a non-exclusive,\\nnon-assignable, non-sublicensable, royalty free, revocable license to write to\\nand use the Sovrin Ledger Networks in accordance with this Agreement and the\\nSovrin Governance Framework.\\n\\n\\nb. When authoring Transactions under the policy of Permissioned Write Access,\\na Transaction Author may only write to the Sovrin Ledger Networks by using an\\nauthorized Transaction Endorser. In the event that Sovrin Foundation enables\\nPublic Write Access to the Sovrin Ledger Networks, Transaction Author will\\nnot need a Transaction Endorser to endorse a Transaction.\\n\\n\\nc. Once an initial Transaction has been written to one of the Sovrin Ledger\\nNetworks by Transaction Author (\"**Initial Transaction**\"), the\\nTransaction Author is granted permission to make additional Transactions to\\nupdate the state of a previous Transaction (\"**Update Transactions**\"). Please\\nnote, an Update Transaction does not remove the Initial Transaction, which\\nwill remain on the Sovrin Ledger due to its immutability and may remain on\\nSovrin Test Networks unless they are reset. Transaction Author may make Update\\nTransactions if and only if Transaction Author was the author of the\\nInitial Transaction. Update Transactions are Transactions and are subject to\\nall the terms of this Agreement.\\n\\n\\n\\n\\n## 3) Transaction Author Obligations\\n\\n\\na. With regard to all Transactions, Transaction Author will:\\n\\n\\n 1. comply with any requirements imposed by the Transaction Endorser on the\\n Transaction Author and any Transactions endorsed by the Transaction\\n Endorser;\\n\\n\\n 2. not write Transactions containing Personal Data until Sovrin Foundation\\n approves Public Write Access and permits Transactions to contain Personal\\n Data pursuant to Section 3(b) below.\\n\\n\\nb. If Sovrin Foundation approves Public Write Access and permits\\nTransaction Authors to write Transactions that contain Permissible Personal\\nData (\"**Personal Data Transactions**\"), then Transaction Author expressly\\nagrees that:\\n\\n\\n 1. It will not write any Transactions that contain Impermissible Personal\\n Data to the Sovrin Ledger Networks;\\n\\n\\n 2. it is an independent data controller (as defined in the GDPR) of the\\n Personal Data Transactions and will be responsible for the lawfulness of the\\n Processing of such data in compliance with the Data Protection Laws;\\n\\n\\n 3. it acknowledges and will notify all data subjects whose Personal Data it\\n Processes that functions inherent in blockchain technology may render\\n fulfilling data subject requests difficult or impossible. For example, due\\n to blockchain’s immutability, data stored on a blockchain generally cannot\\n be removed or altered once the data is confirmed on the blockchain;\\n\\n\\n 4. it irrevocably waives any and all claims, rights and/or obligations it\\n may have now or in the future against Sovrin Foundation as a result of\\n being unable to fulfill data subject requests in accordance with Data\\n Protection Laws;\\n\\n\\n 5. it agrees to be bound by the terms and conditions applicable to\\n Transaction Author as a controller of Personal Data Transactions under the\\n Data Processing Agreements with Stewards and Transaction Endorsers, each in\\n their roles as processors under the GDPR, and attached as Appendices A and B\\n (the \"**DPAs**\");\\n\\n\\n 6. that Sovrin Foundation has the right to enter into the DPAs on its\\n behalf and the DPAs are made a part of the Agreement in their entirety;\\n\\n\\n 7. by signing this Agreement, each Party is deemed to have signed the DPAs,\\n including the Standard Contractual Clauses with Sovrin Foundation and\\n Transaction Author as the \"Data exporter\", and with either a Steward or a\\n Transaction Endorser as \"Data importer\", as applicable;\\n\\n\\n 8. at Sovrin Foundation’s request, Transaction Author will reimburse\\n Sovrin Foundation for any costs incurred by Sovrin Foundation in\\n enforcing Transaction Author’s rights under the GDPR, including but not\\n limited to fulfillment of data subject rights, rights of oversight and\\n audit, etc.; and\\n\\n\\n 9. it irrevocably waives any and all claims that it may have now or in the\\n future that Sovrin Foundation lacks the rights to enter into the DPAs on\\n its behalf and bind Transaction Author to the DPAs’ terms and conditions,\\n including the limitation of liability therein.\\n\\n\\n\\n\\n## 4) Term and Termination\\n\\n\\na. This Agreement commences on the Effective Date and shall remain in force\\nuntil terminated by either Party pursuant to this **Section 4 (Term and\\nTermination)**.\\n\\n\\nb. Either Party may terminate this Agreement: (i) if the other Party has\\nmaterially defaulted in the performance of any of its obligations under this\\nAgreement and has not cured such default within fifteen (15) business days of\\nreceipt of written notice from the non-defaulting Party of such default or\\n(ii) immediately in the event of any government sanctions or other legal\\nmeasures that make it unlawful for Transaction Author to write Transactions to\\nthe Sovrin Ledger Networks.\\n\\n\\nc. Additionally, Transaction Author may terminate this Agreement upon 30 days\\'\\nadvance written notice to Sovrin Foundation and ceasing all use of the\\nSovrin Ledger Networks.\\n\\n\\nd. Upon termination or expiration of this Agreement for any reason the rights\\ngranted to Transaction Author by Sovrin Foundation under this Agreement\\nautomatically terminate.\\n\\n\\n\\n\\n## 5) Representations and Warranties; Disclaimer\\n\\n\\na. By Sovrin Foundation.\\n\\n\\n 1. THE SOVRIN LEDGER NETWORKS AND THE SOVRIN NETWORK ARE PROVIDED AS-IS WITH\\n ALL FAULTS. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, THE SOVRIN\\n FOUNDATION MAKES NO REPRESENTATION OR WARRANTY CONCERNING THE ACCURACY,\\n RELIABILITY, OR COMPLETENESS OF ANY INFORMATION OR DATA OBTAINED OR DERIVED\\n THROUGH THE USE OF THE SOVRIN LEDGER NETWORKS AS THE SOVRIN LEDGER NETWORKS\\n OPERATE ON A DISTRIBUTED NETWORK AND SOVRIN FOUNDATION DOES NOT CONTROL\\n THE INFORMATION OR DATA WRITTEN TO THE SOVRIN LEDGER NETWORKS. THE SOVRIN\\n FOUNDATION DISCLAIMS ANY OTHER REPRESENTATIONS OR WARRANTIES, EXPRESS OR\\n IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR\\n FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ACCURACY OR\\n COMPLETENESS OF DATA.\\n\\n\\n 2. As the architect of the Sovrin Network and administrator of the Sovrin\\n Governance Framework, Sovrin Foundation is an independent controller of\\n the Personal Data Transactions. In no event will Sovrin Foundation be\\n held liable for the actions or omissions of Transaction Author arising out\\n any Personal Data that Transaction Author writes to the Sovrin Ledger\\n Networks in breach of this Agreement and the Sovrin Governance Framework,\\n including but not limited to any Impermissible Personal Data.\\n Notwithstanding the foregoing, if Transaction Author writes Permissible\\n Personal Data to the Sovrin Ledger Networks in express compliance with this\\n Agreement and the Sovrin Governance Framework, Sovrin Foundation is\\n responsible for the lawfulness of such Processing once such Permissible\\n Personal Data is written to the Sovrin Ledger Networks.\\n\\n\\nb. By Transaction Author. Transaction Author represents and warrants:\\n\\n\\n 1. if a natural person, he or she is 16 years of age or older;\\n\\n\\n 2. it has all necessary rights and permissions to write the Transactions;\\n\\n\\n 3. the Transactions do not and will not violate any applicable law;\\n\\n\\n 4. the Transactions will not contain data or information that infringes or\\n misappropriates the intellectual property rights of any third party;\\n\\n\\n 5. it understands that the Sovrin Ledger Networks operate on a distributed\\n network and that Sovrin Foundation disclaims any responsibilities with\\n respect to access of data from the Sovrin Ledger Networks;\\n\\n\\n 6. it understands and acknowledges that Sovrin Foundation does not control the\\n transfer of data between Nodes and over communications facilities,\\n including the internet, and that the Sovrin Ledger Networks may be subject\\n to limitations, delays, and other problems inherent in the use of such\\n communications facilities;\\n\\n\\n 7. it understands and acknowledges that there is regulatory uncertainty\\n regarding the Sovrin Ledger Networks’ compliance with Data Protection Laws\\n as it relates to Permissioned Write Access, Public Write Access, and\\n Personal Data, including cross-border transfers of data, Processing of\\n Personal Data, the right to effective erasure of data, as well as the scope\\n and nature of Personal Data itself;\\n\\n\\n 8. it understands and acknowledges that Sovrin Foundation may modify, at any\\n time, its Sovrin Ledger Access Policies and the terms of this Agreement and\\n any other agreement or document related to the Sovrin Ledger Networks based\\n on new information, guidance, or Data Protection Laws; and\\n\\n\\n 9. it understands and acknowledges that a Steward and/or Sovrin Foundation\\n may obscure a Transaction if (i) the Steward or Sovrin Foundation is\\n required to do so by a court order or applicable law or (ii) the Steward or\\n Sovrin Foundation has evidence that the Transaction violates the terms of\\n this Agreement or any applicable law.\\n\\n\\n\\n\\n## 6) Indemnification\\n\\n\\na. To the fullest extent permitted by applicable law, Transaction Author will\\nindemnify and hold harmless Sovrin Foundation, and each of its respective\\nofficers, directors, agents, partners and employees (individually and\\ncollectively, the \"**Sovrin Parties**\") from and against any losses,\\nliabilities, claims, demands, damages, expenses or costs (\"**Claims**\")\\nbrought by a third party arising out of or related to (i) Transaction Author’s\\naccess to or use of the Sovrin Ledger Networks in violation of this Agreement;\\n(ii) Transaction Author’s violation, misappropriation or infringement of any\\nrights of another (including intellectual property rights or privacy rights);\\nor (iii) Transaction Author’s violation of applicable law.\\n\\n\\nb. Transaction Author agrees to promptly notify the Sovrin Parties in writing\\nof any Claims, cooperate with the Sovrin Parties in defending such Claims and\\npay all fees, costs and expenses associated with defending such Claims\\n(including attorneys’ fees). Transaction Author also agrees that the Sovrin\\nParties will have sole control of the defense or settlement, at the Sovrin\\nFoundation’s sole option, of any Claims. This indemnity is in addition to, and\\nnot in lieu of, any other indemnities set forth in a written agreement between\\nTransaction Author and Sovrin Foundation or the other Sovrin Parties.\\n\\n\\n\\n\\n## 7) Governing Law and Forum\\n\\n\\nThis Agreement is governed by the law of the State of Delaware, without\\nreference to conflict of laws principles; provided that, if Transaction Author\\nis a governmental entity, this Agreement is governed by the law in which such\\ngovernmental entity is established. All disputes arising out of or in connection\\nwith this Agreement shall be finally settled by binding arbitration under the\\nRules of Arbitration of the International Chamber of Commerce (the \"**Rules**\")\\nby a single arbitrator appointed in accordance with said Rules. Arbitration\\nproceedings will be held in London, England. Unless the Parties otherwise\\nmutually agree, such arbitration shall be conducted in the English language by\\nelectronic exchange of documents and by video conference. The arbitrator shall\\nissue a reasoned decision, including findings of fact and conclusions of law.\\nThe arbitrator shall require exchange by the Parties of documents relevant to\\nthe issues raised by any claim, defense, or counterclaim or on which the\\nproducing Party may rely in support of or in opposition to any claim, defense,\\nor counterclaim, with due regard for eliminating undue burden and expense and\\nthe expedited and lower cost nature of arbitration. At the request of a Party,\\nthe arbitrator may at his or her discretion order the deposition of witnesses.\\nDepositions shall be limited to a maximum of three depositions per Party, each\\nof a maximum of four hours duration, unless the arbitrator otherwise determines.\\nDemand for arbitration may be initiated by either Party on fifteen (15) days\\nwritten notice by email to the other Party’s designated representative, together\\nwith a written specification of the grounds for the dispute and the relief\\nrequested. By agreeing to binding and non-appealable arbitration, each party\\nunderstands that they each forever give up and waive any right which each Party\\nmay have to resolve any such claim, difference or dispute by court or jury\\ntrial. Notwithstanding the foregoing, either Party may bring a proceeding\\nseeking equitable or injunctive relief solely and exclusively in the state and\\nfederal courts located in Wilmington, Delaware, to prevent the infringement of\\nintellectual property rights or the disclosure of confidential information. Each\\nParty hereto consents to the exclusive jurisdiction of such courts for the\\nadjudication of any such equitable or injunctive relief, as well as for any such\\nmatters that are excluded from or fall outside of this arbitration provision.\\n\\n\\n\\n\\n## 8) Limitation of Liability\\n\\n\\nEXCEPT IN THE EVENT OF EITHER PARTY’S GROSS NEGLIGENCE, WILLFUL MISCONDUCT OR\\nFRAUD, IN NO EVENT SHALL EITHER PARTY BE LIABLE FOR ANY INDIRECT, INCIDENTAL,\\nEXEMPLARY, PUNITIVE, SPECIAL, OR OTHER CONSEQUENTIAL DAMAGES UNDER THIS\\nAGREEMENT, INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS\\nINTERRUPTION, LOSS OF PROGRAMS OR DATA, OR OTHERWISE, EVEN IF THE OTHER PARTY IS\\nEXPRESSLY ADVISED OF THE POSSIBILITY OR LIKELIHOOD OF SUCH DAMAGES. EXCEPT IN\\nTHE EVENT OF EITHER PARTY’S GROSS NEGLIGENCE, WILLFUL MISCONDUCT OR FRAUD, IN NO\\nEVENT SHALL EITHER PARTY’S LIABILITY UNDER THIS AGREEMENT EXCEED $250,000 USD IN\\nTHE AGGREGATE, PROVIDED THAT THERE WILL BE NO DOLLAR CAP ON LIABILITY FOR\\nDAMAGES ARISING FROM VIOLATIONS OF DATA PROTECTION LAWS. IN THE EVENT OF EITHER\\nPARTY’S GROSS NEGLIGENCE, SUCH PARTY’S LIABILITY UNDER THIS AGREEMENT SHALL NOT\\nEXCEED $500,000 USD IN THE AGGREGATE. IN THE EVENT OF EITHER PARTY’S WILLFUL\\nMISCONDUCT OR FRAUD, THERE SHALL BE NO DOLLAR CAP ON SUCH PARTY’S LIABILITY\\nUNDER THIS AGREEMENT.\\n\\n\\n\\n\\n## 9) Miscellaneous\\n\\n\\na. Notice. Any notice, payment, demand or communication required or permitted\\nto be delivered or given by the provisions of this Agreement shall be deemed\\nto have been effectively delivered or given and received on the date\\npersonally or electronically delivered to the respective Party to whom it is\\ndirected, or when deposited by registered or certified mail, with postage and\\ncharges prepaid and addressed to each respective Party. For the Transaction\\nAuthor, notices will be sent to the agent service endpoint of the Transaction\\nAuthor’s DID as long as Transaction Author authorizes such a connection or\\nsent via other mechanism agreed to by the parties. For Sovrin Foundation,\\nnotices will be sent to Attn: Legal, 86 N University Ave, Suite 110, Provo, UT\\n84601.\\n\\n\\nb. Severability. If any provision of this Agreement is held invalid, illegal,\\nor unenforceable, the validity, legality, and enforceability of any of the\\nremaining provisions of this Agreement shall not in any way be affected or\\nimpaired.\\n\\n\\nc. Relationship of the Parties. This Agreement does not create a partnership,\\nfranchise, joint venture, agency, fiduciary or employment relationship between\\nthe Parties. Neither Party will represent that it has any authority to assume\\nor create any obligation, express or implied, on behalf of the other Party,\\nnor to represent the other Party as agent, employee, franchisee, or in any\\nother capacity. There are no third-party beneficiaries to this Agreement.\\nNeither Party shall make any proposals, promises, warranties, guarantees, or\\nrepresentations on behalf of the other Party or in the other Party’s name.\\n\\n\\nd. Assignment. Neither Party will voluntarily, or by operation of law, assign\\nor otherwise transfer this Agreement without the other Party’s express prior\\nwritten consent which will not be unreasonably withheld, provided that no such\\nconsent is required for an assignment or transfer to a wholly or majority\\nowned subsidiary or to a successor in interest by reason of merger or\\nconsolidation or sale of all or substantially all of the assets of such Party\\nrelating to the subject matter of this Agreement.\\n\\n\\ne. Waiver. The waiver by either Party of a breach, default, delay or omission\\nof any of the provisions of this Agreement by the other Party will not be\\nconstrued as a waiver of any subsequent breach of the same or other\\nprovisions.\\n\\n\\nf. Entire Agreement. This Agreement, including all documents incorporated into\\nthis Agreement by reference, constitutes the entire agreement of the Parties\\nwith respect to the subject matter of this Agreement, and supersedes any and\\nall prior agreements and understandings of the Parties, whether written or\\noral, with respect to such subject matter. This Agreement supersedes all prior\\nTransaction Author Agreements between Sovrin Foundation and Transaction Author\\nwith respect to the subject matter hereof.\\n\\n\\ng. Modification of This Agreement. Sovrin Foundation reserves the right to\\nmodify this Agreement at any time in accordance with this provision,\\nincluding, but not limited to, changes in applicable law or guidance from any\\njurisdiction. Sovrin Foundation will post an amended version of this Agreement\\non its website at least forty-five (45) days prior to the date on which all\\nTransaction Authors must begin operating under the amendment (the \"**Amendment\\nCutover Date**\"). If Transaction Author continues to Author Transactions to\\nthe Sovrin Ledger Networks after the Amendment Cutover Date, such continued\\nuse will constitute acceptance of the amended Agreement.\\n\\n\\nh. Counterparts. This Agreement may be executed in two or more counterparts,\\neach of which will be deemed an original, but all of which taken together will\\nconstitute one and the same instrument\\n\\n\\ni. Survival. Any terms that by their nature survive termination or expiration\\nof this Agreement shall survive.\\n\\n\\nj. Governmental Entities. If Transaction Author is a governmental entity and\\nit determines that GDPR does not apply to it and its Processing of\\nTransactions, then:\\n - a. to the extent that the GDPR requirements referenced in this Agreement are\\n equivalent to the requirements under Data Protection Laws in its own\\n jurisdiction, it will comply with any such requirements; and\\n\\n\\n - b. to the extent that GDPR requirements referenced in this Agreement differ\\n from requirements under Data Protection Laws in its own jurisdiction, it\\n will comply with the requirements under its own legislation.\\n\\n\\n\\n\\n## Appendix A\\n\\n\\n- **Steward Data Processing Agreement**\\n[LINK](https://sovrin.org/wp-content/uploads/Steward-Data-Processing-Agreement-V1.pdf)\\nAS OF THE EFFECTIVE DATE OF THIS TRANSACTION AUTHOR AGREEMENT\\n\\n\\n\\n\\n## Appendix B\\n\\n\\n- **Transaction Endorser Data Processing Agreement**\\n[LINK](https://sovrin.org/wp-content/uploads/Transaction-Endorser-Data-Processing-Agreement-V1.pdf)\\nAS OF THE EFFECTIVE DATE OF THIS TRANSACTION AUTHOR AGREEMENT\\n\\n\\n(c) 2019 by Sovrin Foundation. This work is licensed under the Creative Commons\\nAttribution-ShareAlike 4.0 International License\\n(http://creativecommons.org/licenses/by-sa/4.0/).', 'digest': '8cee5d7a573e4893b08ff53a0761a22a1607df3b3fcd7e75b98696c92879641f', 'ratification_ts': 1575417600, 'version': '2.0', 'mechanism': 'service_agreement'}\n" + "{ 'result': { 'did': '7DfSpFJMUThna1uQoR1mtY',\n", + " 'posture': 'public',\n", + " 'verkey': '4PaGHfTzvxRDS2k38NGGf7pRrMmEGGSiXZZgwz87acGf'}}\n" ] } ], "source": [ - "response = await agent_controller.ledger.get_taa()\n", - "TAA = response['result']['taa_record']\n", - "TAA['mechanism'] = \"service_agreement\"\n", - "print(TAA)" + "try:\n", + " response = await agent_controller.wallet.assign_public_did(did_object[\"did\"])\n", + " pp.pprint(response)\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + "# raise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Write Credential Definition to Ledger\n", + "\n", + "This is creating the key pair required for the External agent to issue PyDentity Multi-Tennant Tutorial certificates. See schema on the ledger through [IndyScan](https://indyscan.io/tx/SOVRIN_STAGINGNET/domain/195790)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "schema_id = '7DfSpFJMUThna1uQoR1mtY:2:PyDentity Multi-Tennant Tutorial:0.0.1'" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{}\n" + "7DfSpFJMUThna1uQoR1mtY:3:CL:195790:default\n" ] } ], "source": [ - "response = await agent_controller.ledger.accept_taa(TAA)\n", - "## Will return {} if successful\n", - "print(response)" + "try:\n", + " response = await agent_controller.definitions.write_cred_def(schema_id)\n", + " cred_def_id = response[\"credential_definition_id\"]\n", + " print(cred_def_id)\n", + "except ClientConnectorError as err:\n", + " print(err)\n", + "except ClientResponseError as err:\n", + " print(err)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note: You should be able to see both schema and definition transactions on Sovrin Stagingnet network with [Indyscan](https://indyscan.io/home/SOVRIN_STAGINGNET)**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 6. Set public DID\n", + "## 6. Populate the Attributes to Issue to Idenity Holder (User)\n", "\n", - "Now you are able to assign the DID written to the ledger as public." + "We will issue a credential to the identity holder consisting of the following attributes:\n", + "\n", + "credential_attributes = [\n", + " {\"name\": \"full\", \"value\": name},\n", + " {\"name\": \"skill\", \"value\": \"PyDentity SSI Ninja\"},\n", + " {\"name\": \"age\", \"value\": age}\n", + "]\n", + "\n", + "The notebook will ask you to input the identity holder's full name and age which will be used to populate the schema above with the identity holders attribute information." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Please enter your name and surname: Will\n", + "Please enter your age: \n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "{'result': {'did': 'Jqi9JNk9Z637azKekx51V4', 'verkey': 'AixCmDcPfWvZsuMpV2swhvukSYn2e9JbQjXMq4C2GD1H', 'posture': 'public'}}\n" + "[{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n" ] } ], "source": [ - "response = await agent_controller.wallet.assign_public_did(did_object[\"did\"])\n", - "print(response)" + "name=input(\"Please enter your name and surname: \")\n", + "credential_attributes = [\n", + " {\"name\": \"name\", \"value\": name},\n", + " {\"name\": \"skill\", \"value\": \"ACA-Py Multi-Tennancy\"},\n", + "]\n", + "print(credential_attributes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Get public DID" + "## 6. Register Listeners\n", + "\n", + "The handler should get called every time the controller receives a webhook with the topic issue_credential, printing out the payload. The agent calls to this webhook every time it receives an issue-credential protocol message from a credential." ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "loop = asyncio.get_event_loop()\n", + "loop.create_task(agent_controller.listen_webhooks())\n", + "def cred_handler(payload):\n", + " print(\"Handle Credentials\")\n", + " exchange_id = payload['credential_exchange_id']\n", + " state = payload['state']\n", + " role = payload['role']\n", + " attributes = payload['credential_proposal_dict']['credential_proposal']['attributes']\n", + " print(f\"Credential exchange {exchange_id}, role: {role}, state: {state}\")\n", + " print(f\"Attributes: {attributes}\")\n", + " \n", + "cred_listener = {\n", + " \"topic\": \"issue_credential\",\n", + " \"handler\": cred_handler\n", + "}\n", + "\n", + "def connections_handler(payload):\n", + " global STATE\n", + " connection_id = payload[\"connection_id\"]\n", + " print(\"Connection message\", payload, connection_id)\n", + " STATE = payload['state']\n", + " if STATE == 'active':\n", + "# print('Connection {0} changed state to active'.format(connection_id))\n", + " print(colored(\"Connection {0} changed state to active\".format(connection_id), \"red\", attrs=[\"bold\"]))\n", + "\n", + "\n", + "connection_listener = {\n", + " \"handler\": connections_handler,\n", + " \"topic\": \"connections\"\n", + "}\n", + "agent_controller.register_listeners([cred_listener, connection_listener], defaults=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Create an Invitation" + ] + }, + { + "cell_type": "code", + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'result': {'did': 'Jqi9JNk9Z637azKekx51V4', 'verkey': 'AixCmDcPfWvZsuMpV2swhvukSYn2e9JbQjXMq4C2GD1H', 'posture': 'public'}}\n", + "Connection ID c1c49b13-4440-4601-9f95-933a15b91960\n", + "Invitation Message \n", "\n", - "Issuer public DID: Jqi9JNk9Z637azKekx51V4\n" + "\n", + "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '557a1e44-8a85-4fd2-8abb-579ebf2a6d39', 'label': 'EXTERNAL', 'recipientKeys': ['923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2'], 'serviceEndpoint': 'https://8298dfdb2447.ngrok.io'}\n", + "\n", + "\n", + "\n", + "Connection message {'updated_at': '2021-03-19 18:44:48.733756Z', 'state': 'invitation', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'rfc23_state': 'invitation-sent', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'updated_at': '2021-03-19 18:44:48.733756Z', 'state': 'invitation', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'rfc23_state': 'invitation-sent', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:41.582190Z', 'state': 'request', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'rfc23_state': 'request-received', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:41.582190Z', 'state': 'request', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'rfc23_state': 'request-received', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:41.599748Z', 'state': 'response', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'my_did': 'Dd7zXynP78FEiLEivNjW5F', 'rfc23_state': 'response-sent', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:41.599748Z', 'state': 'response', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'my_did': 'Dd7zXynP78FEiLEivNjW5F', 'rfc23_state': 'response-sent', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:42.647094Z', 'state': 'active', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'my_did': 'Dd7zXynP78FEiLEivNjW5F', 'rfc23_state': 'completed', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "\u001b[1m\u001b[31mConnection c1c49b13-4440-4601-9f95-933a15b91960 changed state to active\u001b[0m\n", + "Connection message {'their_label': 'Alice', 'updated_at': '2021-03-19 18:46:42.647094Z', 'state': 'active', 'invitation_key': '923PUqy1CvxtsmRfS43oXHfCDDm2pUgjCHdSwYiaTfk2', 'connection_id': 'c1c49b13-4440-4601-9f95-933a15b91960', 'routing_state': 'none', 'their_role': 'invitee', 'their_did': '8TLHBjUXHCkkD8aCpYKA2C', 'accept': 'auto', 'created_at': '2021-03-19 18:44:48.733756Z', 'my_did': 'Dd7zXynP78FEiLEivNjW5F', 'rfc23_state': 'completed', 'invitation_mode': 'once'} c1c49b13-4440-4601-9f95-933a15b91960\n", + "\u001b[1m\u001b[31mConnection c1c49b13-4440-4601-9f95-933a15b91960 changed state to active\u001b[0m\n" ] } ], "source": [ - "response = await agent_controller.wallet.get_public_did()\n", - "print(response)\n", - "issuer_nym = response['result']['did']\n", - "print('\\nIssuer public DID:',issuer_nym)" + "# Create Invitation\n", + "try:\n", + " invite = await agent_controller.connections.create_invitation(auto_accept=False)\n", + " connection_id = invite[\"connection_id\"]\n", + " print(\"Connection ID\", connection_id)\n", + " print(\"Invitation Message - Copy This \\n\\n\")\n", + " invite_message = invite['invitation']\n", + " print(invite_message)\n", + " print(\"\\n\\n\")\n", + "except ClientConnectorError as err:\n", + " print(err)\n", + " # raise\n", + "except ClientResponseError as err:\n", + " print(err)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 8. Fetch verkey for public DID\n", - "\n", - "Additionally, we can verify that this DID does actually resolve to the public key material on the ledger." + "### Head over to [Alice](http://localhost:8888/lab/tree/Alice/Part%203%20-%20Communicating%20with%20an%20external%20agent.ipynb) again to accept the invitation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7c. Check if established connection is in active state" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'verkey': 'AixCmDcPfWvZsuMpV2swhvukSYn2e9JbQjXMq4C2GD1H'}\n" + "\u001b[1m\u001b[35mCurrent state for ConnectionId c1c49b13-4440-4601-9f95-933a15b91960 is active\u001b[0m\n", + "\u001b[1m\u001b[32mConnectionId: c1c49b13-4440-4601-9f95-933a15b91960 is now active. Continue with notebook\u001b[0m\n" ] } ], "source": [ - "issuer_verkey = await agent_controller.ledger.get_did_verkey(issuer_nym)\n", - "print(issuer_verkey)" + "import time\n", + "\n", + "try:\n", + " # print('Current state for ConnectionId {} is {}'.format(connection_id,STATE))\n", + " print(colored(\"Current state for ConnectionId {} is {}\".format(connection_id,STATE), \"magenta\", attrs=[\"bold\"]))\n", + " while STATE != 'active':\n", + " # print('ConnectionId {0} is not in active state yet'.format(connection_id))\n", + " print(colored(\"ConnectionId {0} is not in active state yet\".format(connection_id), \"yellow\", attrs=[\"bold\"]))\n", + " trust_ping = await agent_controller.messaging.trust_ping(connection_id,'hello!')\n", + " # print('Trust ping send to ConnectionId {0} to activate connection'.format(trust_ping))\n", + " print(colored(\"Trust ping send to ConnectionId {0} to activate connection\".format(trust_ping), \"blue\", attrs=[\"bold\"]))\n", + " time.sleep(5)\n", + "\n", + " # print('ConnectionId: {0} is now active. Continue with notebook'.format(connection_id))\n", + " print(colored(\"ConnectionId: {0} is now active. Continue with notebook\".format(connection_id), \"green\", attrs=[\"bold\"]))\n", + "except ClientResponseError as err:\n", + " print(err)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 9. Get public DID endpoint\n", - "\n", - "As well as providing a publically accessible endpoint to contact the DID controller through the agent framework." + "## 8. Send Credential\n" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'endpoint': 'https://1ae2ec7766b1.ngrok.io'}\n" + "Credential exchange ID: 61213a35-501f-4cad-9025-52b1a98fba0f\n", + "\n", + "Credential ID: 7DfSpFJMUThna1uQoR1mtY:3:CL:195790:default\n", + "\n", + "\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: offer_sent\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: offer_sent\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: offer_sent\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: request_received\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: request_received\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: credential_issued\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: credential_issued\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: credential_acked\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n", + "Handle Credentials\n", + "Credential exchange 61213a35-501f-4cad-9025-52b1a98fba0f, role: issuer, state: credential_acked\n", + "Attributes: [{'name': 'name', 'value': 'Will'}, {'name': 'skill', 'value': 'ACA-Py Multi-Tennancy'}]\n" ] } ], "source": [ - "issuer_endpoint = await agent_controller.ledger.get_did_endpoint(issuer_nym)\n", - "print(issuer_endpoint)" + "try:\n", + " record = await agent_controller.issuer.send_credential(connection_id, schema_id, cred_def_id, credential_attributes, trace=False)\n", + " record_id = record['credential_exchange_id']\n", + " state = record['state']\n", + " role = record['role']\n", + " credential_ex_id = record['credential_exchange_id']\n", + " credential_id = record['credential_definition_id']\n", + " print(\"Credential exchange ID: \" + credential_ex_id + \"\\n\")\n", + " print(\"Credential ID: \" + credential_id + \"\\n\")\n", + " print()\n", + " print(f\"Credential exchange {record_id}, role: {role}, state: {state}\")\n", + "except CancelledError as err:\n", + " print(\"Asyncio CancelledError\")\n", + "except:\n", + " raise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Accept credential in [Alice's subwallet](http://localhost:8888/lab/tree/Alice/Part%203%20-%20Communicating%20with%20an%20external%20agent.ipynb)" ] }, { @@ -400,15 +591,6 @@ "print(response)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Proceed to Part 3 on [Issuer Notebook](http://localhost:8888/notebooks/Part%203%20-%20Issue%20Credential.ipynb)\n", - "\n", - "Here you will be issued with a credential into your mobile SSI wallet that will be verified in part 4." - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/tutorials/4. Multitenancy/notebooks/mediator/Configure Mediator.ipynb b/tutorials/4. Multitenancy/notebooks/mediator/Configure Mediator.ipynb new file mode 100644 index 00000000..324341ef --- /dev/null +++ b/tutorials/4. Multitenancy/notebooks/mediator/Configure Mediator.ipynb @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Mediator\n", + "\n", + "A mediator is an agent that mediates DIDComm messages on behalf of other agents. Specifically Alice can request this agent act as a mediator for their messages, then when creating an invitation, she specifies the mediators endpoint as the place for messages to be sent and adds the mediators keys for routing of these messages. Anyone wishing to send Alice a DIDComm message must first encrypt under Alice's key then again under the Mediators key before sending the message to the endpoint specified by Alice. The mediators endpoint.\n", + "\n", + "### Useful Material\n", + "\n", + "* [Aries-RFC-046 Mediators and Relays](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0046-mediators-and-relays)\n", + "* [Aries-RFC-094 Cross Domain Messaging](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0094-cross-domain-messaging)\n", + "* [DIDComm Messaging Specification - Routing](https://identity.foundation/didcomm-messaging/spec/#routing)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### 1. Pull in dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IPython autoawait is `on`, and set to use `asyncio`\n" + ] + } + ], + "source": [ + "%autoawait\n", + "import time\n", + "import asyncio" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### 2. Instatiate the controller for our Agent\n", + "\n", + "The arguments depend on how the aca-py agent was initiated. See the manage and docker-compose.yml files for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from aries_basic_controller.aries_controller import AriesAgentController\n", + " \n", + "WEBHOOK_HOST = \"0.0.0.0\"\n", + "WEBHOOK_PORT = 8042\n", + "WEBHOOK_BASE = \"\"\n", + "ADMIN_URL = \"http://mediator-agent:8041\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Based on the aca-py agent you wish to control\n", + "agent_controller = AriesAgentController(webhook_host=WEBHOOK_HOST, webhook_port=WEBHOOK_PORT,\n", + " webhook_base=WEBHOOK_BASE, admin_url=ADMIN_URL, mediation=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### 3. Listen for webhooks and register default listeners\n", + "\n", + "TODO: Implement mediator webhooks. These currently need to be implemented in ACA-Py first. We could even help do this. See issue - https://github.com/hyperledger/aries-cloudagent-python/issues/950 " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "loop = asyncio.get_event_loop()\n", + "loop.create_task(agent_controller.listen_webhooks())\n", + "\n", + "def connection_handler(payload):\n", + " print(\"Connection Handler Called\")\n", + " connection_id = payload[\"connection_id\"]\n", + " state = payload[\"state\"]\n", + " print(f\"Connection {connection_id} in State {state}\")\n", + " \n", + "connection_listener = {\n", + " \"handler\": connection_handler,\n", + " \"topic\": \"connections\"\n", + "}\n", + "\n", + "agent_controller.register_listeners([connection_listener], defaults=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### 4. Use the controller to create an invitation from our agent\n", + "\n", + "An invitation is a JSON object, as shown below, it contains the relevant information required for another agent to connect with it and exchange identifiers. This object must be passed to the agent Alice wishes to connect to out of band, in this instance we will just copy it across." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connection Handler Called\n", + "Connection 98e7d448-5a16-40b0-bab6-afe9e7b2f96f in State invitation\n", + "Connection Handler Called\n", + "Connection 98e7d448-5a16-40b0-bab6-afe9e7b2f96f in State invitation\n", + "Connection ID 98e7d448-5a16-40b0-bab6-afe9e7b2f96f\n", + "Invitation\n", + "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': '4d68d1f0-83d4-49b9-bb96-9ac35283878d', 'serviceEndpoint': 'https://6512c3c6a284.ngrok.io', 'label': 'MEDIATOR', 'recipientKeys': ['8kPjcAdzMdf8giMrsWackJRDHEUeG6uoDu7ECVm1yGrY']}\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State invitation\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State invitation\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State request\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State request\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State response\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State response\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State active\n", + "Connection Handler Called\n", + "Connection ab02b489-033d-4378-8202-ad31751d64f0 in State active\n" + ] + } + ], + "source": [ + "# Create Invitation\n", + "invite = await agent_controller.connections.create_invitation(multi_use=\"true\")\n", + "connection_id = invite[\"connection_id\"]\n", + "invite_message = invite['invitation']\n", + "print(\"Connection ID\", connection_id)\n", + "print(\"Invitation\")\n", + "print(invite_message)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### Copy the invitation output to any agent that you needs to establish a mediator. For example where you came from - [Alice](http://localhost:8888/lab/tree/Alice/Part%202%20-%20Mediation%20of%20communication%20-%20Alice.ipynb)\n", + "\n", + "**Example** invitation you're supposed to copy:\n", + "\n", + "{'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', '@id': 'fee0f080-88c2-49b1-9869-9117524f010c', 'label': 'MEDIATOR', 'serviceEndpoint': 'https://f6d0c3340147.ngrok.io', 'recipientKeys': ['53gFmaMTJXVy5xBKJKZeRJYs6dL7SNG55gR1W6WuAvpN']}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### Check Mediation Records\n", + "\n", + "If you do this before accepting the invitation in Alice's notebook, this should be empty.\n", + "\n", + "You can return here later (after accepting) and will find that this then shows the mediated conneciton we have established," + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mediation Record\n", + "connection_id ab02b489-033d-4378-8202-ad31751d64f0\n", + "State granted\n" + ] + } + ], + "source": [ + "response = await agent_controller.mediation.get_mediation_records()\n", + "\n", + "for record in response:\n", + " print(\"Mediation Record\")\n", + " print(\"connection_id\", record[\"connection_id\"])\n", + " print(\"State\", record[\"state\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Terminate Meditator Controller\n", + "\n", + "**Only do this when you have completed the full tutorial**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = await agent_controller.terminate()\n", + "print(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + }, + "pycharm": { + "stem_cell": { + "cell_type": "raw", + "metadata": { + "collapsed": false + }, + "source": [] + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tutorials/attachments/notebooks/bob/received_files/.gitkeep b/tutorials/attachments/notebooks/bob/received_files/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tutorials/attachments/notebooks/bob/received_files/openmined.jpg b/tutorials/attachments/notebooks/bob/received_files/openmined.jpg deleted file mode 100644 index 48527f17..00000000 Binary files a/tutorials/attachments/notebooks/bob/received_files/openmined.jpg and /dev/null differ diff --git a/tutorials/attachments/notebooks/bob/received_files/test_file.txt b/tutorials/attachments/notebooks/bob/received_files/test_file.txt deleted file mode 100644 index 1746e168..00000000 --- a/tutorials/attachments/notebooks/bob/received_files/test_file.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test file to attach. \ No newline at end of file