# Public DID Mediator Bug: Creating an OOB invitation #### Error: Cannot connect via public DID that has no associated DIDComm services ACA-Py Branch: https://github.com/Indicio-tech/aries-cloudagent-python/tree/feature/0.7.4-ssl-public-did * Changed the pydid entry in `requirements.txt` to `pydid@git+https://github.com/cjhowland/pydid@fix/oob-invitation-temp3` PyDID Branch: https://github.com/cjhowland/pydid/tree/fix/oob-invitation-temp3 Steps: 1. Start up Alice and Bob demo agents from ACA-Py 2. If using mediator, connection Alice and Bob, Alice requests and is granted mediation from Bob 3. Create local did 4. Register with self-serve 5. Register public did (if using mediator, register with `mediation_id`) 6. Create OOB invitation using the following JSON: ```json= { "alias": "Barry", "handshake_protocols": [ "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0" ], "metadata": {}, "my_label": "Invitation to Barry", "use_public_did": true } ``` In `BaseConnectionManager.resolve_invitation()` (which is called from `OutOfBandManager.create_invitation()`), I have logged `doc_dict` before it is passed into `pydid.deserialize_document`, and the `doc` that is returned from this method. I have also added logs directly in Pydid. ### Behind a mediator: #### `doc_dict["service"]` (pre-pydid.deserialize_document()): ``` [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}] ``` #### `doc.service` (post-pydid.deserialize_document()): ``` [UnknownService(id='did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), recipient_keys=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], priority=1, accept=['didcomm/aip2;env=rfc19'], routing_keys=['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']) ``` The fact that this is an `UnknownService`, rather than a `DIDCommService`, is what is causing the error. More extensive logs: ```json alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid.doc.builder DEBUG ServiceBuilder.add(): type did-communication alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid.doc.builder DEBUG ServiceBuilder.add() service endpoint: http://agent-bob:3006 alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid.doc.builder DEBUG DIDDocumentBuilder.build(): [Service(id='did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), recipient_keys=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], priority=1, accept=['didcomm/aip2;env=rfc19'], routing_keys=['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj'])] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 aries_cloudagent.connections.base_manager WARNING doc_dict pre-pydid (BaseConnectionManager): {'@context': ['https://www.w3.org/ns/did/v1'], 'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ', 'verificationMethod': [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#key-1', 'type': 'Ed25519VerificationKey2018', 'controller': 'did:sov:H69whNmZkdqZpNunPKqzkZ', 'publicKeyBase58': '9mbqvkC8MjpQ8bhtrkrUNHSFSdtN97i4VouowTnHNkwT'}], 'authentication': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'assertionMethod': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'service': [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}]} alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid DEBUG Deserializing the DIDDoc alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid DEBUG cls: <class 'pydid.doc.doc.DIDDocument'> alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,375 pydid DEBUG strict value service: [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,376 pydid.resource DEBUG Resource deserialize.service [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,376 pydid.doc.doc DEBUG DIDDocument.deserialize service: [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,376 pydid.resource DEBUG Resource deserialize.service [{'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj']}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,377 pydid.doc.doc DEBUG DIDDocument.deserialize service post super: [UnknownService(id='did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), recipient_keys=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], priority=1, accept=['didcomm/aip2;env=rfc19'], routing_keys=['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj'])] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,377 aries_cloudagent.connections.base_manager WARNING DIDDoc post-pydid (BaseConnectionManager): context=['https://www.w3.org/ns/did/v1'] id='did:sov:H69whNmZkdqZpNunPKqzkZ' also_known_as=None controller=None verification_method=[Ed25519VerificationKey2018(id='did:sov:H69whNmZkdqZpNunPKqzkZ#key-1', type='Ed25519VerificationKey2018', controller='did:sov:H69whNmZkdqZpNunPKqzkZ', public_key_hex=None, public_key_base58='9mbqvkC8MjpQ8bhtrkrUNHSFSdtN97i4VouowTnHNkwT', public_key_pem=None, public_key_multibase=None, blockchain_account_id=None, ethereum_address=None, public_key_jwk=None)] authentication=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'] assertion_method=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'] key_agreement=None capability_invocation=None capability_delegation=None service=[UnknownService(id='did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), recipient_keys=['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], priority=1, accept=['didcomm/aip2;env=rfc19'], routing_keys=['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj'])] alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,378 aries_cloudagent.core.dispatcher ERROR Handler error: invitation_create alice_bob_demo-agent-alice-1 | Traceback (most recent call last): alice_bob_demo-agent-alice-1 | File "/home/indy/.pyenv/versions/3.6.13/lib/python3.6/asyncio/tasks.py", line 180, in _step alice_bob_demo-agent-alice-1 | result = coro.send(None) alice_bob_demo-agent-alice-1 | File "/home/indy/aries_cloudagent/protocols/out_of_band/v1_0/routes.py", line 177, in invitation_create alice_bob_demo-agent-alice-1 | mediation_id=mediation_id, alice_bob_demo-agent-alice-1 | File "/home/indy/aries_cloudagent/protocols/out_of_band/v1_0/manager.py", line 245, in create_invitation alice_bob_demo-agent-alice-1 | endpoint, *_ = await self.resolve_invitation(public_did.did) alice_bob_demo-agent-alice-1 | File "/home/indy/aries_cloudagent/connections/base_manager.py", line 267, in resolve_invitation alice_bob_demo-agent-alice-1 | "Cannot connect via public DID that has no associated DIDComm services" alice_bob_demo-agent-alice-1 | aries_cloudagent.connections.base_manager.BaseConnectionManagerError: Cannot connect via public DID that has no associated DIDComm services alice_bob_demo-agent-alice-1 | 2022-10-13 21:13:08,378 aries_cloudagent.admin.server ERROR Handler error with exception: Cannot connect via public DID that has no associated DIDComm services ``` ### No mediator: #### `doc_dict["service"]` (pre-pydid): ``` [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}] ``` #### `doc.service` (post-pydid): ``` [DIDCommService(id='did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), recipient_keys=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], routing_keys=[], accept=['didcomm/aip2;env=rfc19'], priority=1)] ``` The `DIDCommService` gets us past the "public DID that has no associated DIDComm services" error. More extensive logs (these, to me, look the same as the logs from behind a mediator pasted above, except for the last two logs which include `DIDCommService`): ```json alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,863 pydid.doc.builder DEBUG ServiceBuilder.add(): type did-communication alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,863 pydid.doc.builder DEBUG ServiceBuilder.add() service endpoint: http://agent-alice:3000 alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,970 pydid.doc.builder DEBUG DIDDocumentBuilder.build(): [Service(id='did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), recipient_keys=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], priority=1, accept=['didcomm/aip2;env=rfc19'], routing_keys=[])] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,971 aries_cloudagent.connections.base_manager WARNING doc_dict pre-pydid (BaseConnectionManager): {'@context': ['https://www.w3.org/ns/did/v1'], 'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz', 'verificationMethod': [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1', 'type': 'Ed25519VerificationKey2018', 'controller': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz', 'publicKeyBase58': 'J2y11VGVVocJWGphk63DmRi5ESRSEdCbJ7WgEnwNRbC3'}], 'authentication': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'assertionMethod': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'service': [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}]} alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,972 pydid DEBUG Deserializing the DIDDoc alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,972 pydid DEBUG cls: <class 'pydid.doc.doc.DIDDocument'> alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,972 pydid DEBUG strict value service: [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,972 pydid.resource DEBUG Resource deserialize.service [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,975 pydid.doc.doc DEBUG DIDDocument.deserialize service: [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,975 pydid.resource DEBUG Resource deserialize.service [{'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': []}] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,976 pydid.doc.doc DEBUG DIDDocument.deserialize service post super: [DIDCommService(id='did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), recipient_keys=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], routing_keys=[], accept=['didcomm/aip2;env=rfc19'], priority=1)] alice_bob_demo-agent-alice-1 | 2022-10-13 21:04:59,976 aries_cloudagent.connections.base_manager WARNING DIDDoc post-pydid (BaseConnectionManager): context=['https://www.w3.org/ns/did/v1'] id='did:sov:YFfjDUUuvEZ1rMjRxChoNz' also_known_as=None controller=None verification_method=[Ed25519VerificationKey2018(id='did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1', type='Ed25519VerificationKey2018', controller='did:sov:YFfjDUUuvEZ1rMjRxChoNz', public_key_hex=None, public_key_base58='J2y11VGVVocJWGphk63DmRi5ESRSEdCbJ7WgEnwNRbC3', public_key_pem=None, public_key_multibase=None, blockchain_account_id=None, ethereum_address=None, public_key_jwk=None)] authentication=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'] assertion_method=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'] key_agreement=None capability_invocation=None capability_delegation=None service=[DIDCommService(id='did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', type='did-communication', service_endpoint=AnyUrl('http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), recipient_keys=['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], routing_keys=[], accept=['didcomm/aip2;env=rfc19'], priority=1)] ``` ## Problem pydid/doc/doc.py: ```python= class DIDDocument(BasicDIDDocument): ... @classmethod def deserialize(cls, value: dict) -> "DIDDocument": """Wrap deserialization with a basic validation pass before matching to type.""" DIDDocumentRoot.deserialize(value) return super(DIDDocument, cls).deserialize(value) ``` The above `super(DIDDocument, cls).deserialize(value)` returns a service of either `DIDCommService` (without a mediator) or `UnknownService` (behind a mediator). The only differences in the input value I can see between the flow with/without a mediator are the serviceEndpoint values and the presence of keys in the routing_keys list. I can't figure out why one case produces `UnknownService` and the other produces `DIDCommService`. Here are the service attributes of those input values displayed nicely: ##### `value.service` (behind a mediator): ```json [ { 'id': 'did:sov:H69whNmZkdqZpNunPKqzkZ#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl( 'http://agent-bob:3006', scheme='http', host='agent-bob', host_type='int_domain', port='3006'), 'recipient_keys': ['did:sov:H69whNmZkdqZpNunPKqzkZ#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': ['EhbHJ5197z8Q9UNWuJiAo8ZMGCVaKgy9wwBVNHRvqjWj'] } ] ``` ##### `value.service` (no mediator): ```json [ { 'id': 'did:sov:YFfjDUUuvEZ1rMjRxChoNz#did-communication', 'type': 'did-communication', 'serviceEndpoint': AnyUrl( 'http://agent-alice:3000', scheme='http', host='agent-alice', host_type='int_domain', port='3000'), 'recipient_keys': ['did:sov:YFfjDUUuvEZ1rMjRxChoNz#key-1'], 'priority': 1, 'accept': ['didcomm/aip2;env=rfc19'], 'routing_keys': [] } ] ``` I would've thought that we would call `ServiceBuilder.add_didcomm` to create `DIDCommService` when not behind a mediator, but I haven't hit those logs (only the logs in `ServiceBuilder.add()`).