---
# System prepended metadata

title: Implementing DID Indy

---

# Implementing DID Indy

Discussion moved here:
Spec: [Rendered](https://hyperledger.github.io/indy-did-method/)/[Github](https://github.com/hyperledger/indy-did-method)
Presentation and discussion in [Indy Contributors Call](https://wiki.hyperledger.org/display/indy/2021-09-28+Indy+Contributors+Call)

## Approach

We started with implementing relevant changes to indy node. Although the NYM transaction is defined in Plenum we did not need to change Plenum yet, because the validation is not performed for some reason.

Clients (e.g. Indy SDK and Indy VDR) need (at least) to support the new `buildNymRequest` and `buildGetNymRequest`. This is an API change. In addtion a new DID focused API layer might be included or left to resolver library sitting on top of the client software.

In order to have a working PoC, we wrote a small server to resolve and update DIDs based on the current indy sdk and custom `buildNymRequest` and `buildGetNymRequest`functions.
The server can be used with an instance of von network running nodes with did:indy support.

### Changes to Indy/Plenum Transactions

We added additional optional properties to `NYM` and `GET_NYM` transactions. Since these are optional and inside the operations object in a request, we might not need to change the protocol version or create new versions of transactions. But not sure about this.

#### NYM Transaction

Additional property in operation object: `diddoc_content`

NYM transactions are [defined in Plenum](https://github.com/hyperledger/indy-plenum/blob/406afdeca1630be688f803a3cba15115faa20e2b/plenum/common/messages/client_request.py#L55). However, validation is currently not performed. Hence, we can add additional properties to the transaction without changes to Plenum. See [this test](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_node/test/nym_txn/test_nym_did_indy.py#L191). This needs further investigation. Validation should be performed.


#### GET_NYM Transaction

Additional properties in operation object:
- `timestamp [Int]` 
- `seqNo [Int]`

See in [Code](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_common/types.py#L67)
### Changes to transaction handlers

#### NYM

##### DID Doc

If a transaction inlcudes `diddoc_content` there is [additional static validation](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py#L210):

- Must not have an id property
- if `@context` is present, it must be a string or array and contain `https://www.w3.org/ns/did/v1`
- No element in diddoc is allowed to have same id as verkey in base diddoc

Each check might throw an [`InvalidDIDDocException`](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_common/exceptions.py#L9), but provides no reason currently.

Currently no exhaustive validation of the DID doc is performed. We might want to merge the `diddoc_content` with the base did doc (The base did doc is defined [here](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_common/base_diddoc_template.py) but not used at the moment) and use [pyDID](https://github.com/dbluhm/pydid) to validate the complete DID document. Alternatively, we don't enforce this on server/node side and leave this to the client.

`diddoc_content` is directly added to the nym state and stored in the ledger. This might not be a good solution for large did docs as the content of an `ATTRIB` transaction is handled differently. See [issue on Github](https://github.com/hyperledger/indy-did-method/issues/20).

##### Self-certifying identifier

If a new nym is written to the ledger a [self-certification check](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py#L197) is performed. This seems to break quite some tests in indy node, because there are many occassions where a trustee or steward creates a nym without a verkey. We've added an `ENABLE_DID_INDY` flag in the [config](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_common/config.py#L121) to [toggle the self-certification check](https://github.com/boschresearch/indy-node/blob/4b211646c8556fa0ba7f44d9c54b2cac5d15a53c/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py#L150). We need to check if and how we adapt/change the tests.

[Code](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py)



#### GET NYM

We check for the additional properties `seqNo` and `timestamp`. If none of them are present, then we return the current state of the nym. Nothing has changed here except that the state might now contain a `diddoc_content` field.

##### Getting previous states by timestamp and seqNo

Because of the requirements for the revocation registry deltas, there's a mapping between time and state of the domain ledger. We can use this mapping to retrieve the state of a nym at a given timestamp. The relevant functionality is [implemented in Plenum](https://github.com/hyperledger/indy-plenum/blob/f812a72197bc0740d4c6b75b796beff8f88c73f9/storage/state_ts_store.py).

There seems to be no mapping between the sequence number and the state (Is this true?). Hence, we currently circumvent this by first retrieving the transaction by `seqNo`, parse the `txnTime` field and then use the timestamp to state mapping to get the state. 

[Code](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py)

[Code without support of seqNo](https://github.com/boschresearch/indy-node/blob/1d0c477fe40d057414dabece2d3948e6977a56f6/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py)

### Tests

Indy node uses indy sdk for tests. In order to perform tests for the new functionality, we added [mock functions](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/test/mock.py) for `buildNymRequest` and `buildGetNymRequest` that are used in the relevant tests. 

Relevant test files:
- [nym_txn/test_nym_additional](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/test/nym_txn/test_nym_additional.py)
- [nym_txn/test_nym_did_indy](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/test/nym_txn/test_nym_did_indy.py)
- [request_handlers/test_nym_handler](https://github.com/boschresearch/indy-node/blob/feature/did-indy/indy_node/test/request_handlers/test_nym_handler.py)

## Proof of Concept

### Network

VON Network with adapted [Indy node image](https://github.com/domwoe/von-image/tree/feature/did-indy) to build from feature/did-indy branch:
https://github.com/domwoe/von-network/tree/feature/did-indy

The Dockerfile for the indy node is [here](https://github.com/domwoe/von-network/blob/feature/did-indy/Dockerfile).

After changing code of indy node on the feature/did-indy branch you can create a new image with
```
python make_node_image.py --name indy-node did-indy  
```
in the von-image folder.

Starts also Indy DID Server described below.


### Indy DID Server

The Indy DID Server is started with a trustee seed and provides two endpoints available at `port 3000`:

`GET: /1.0/identifiers/:identifier`

where `identifier` needs to be a fully qualified DID on the network, e.g. `did:indy:sandbox:V4SGRU86Z58d6TV7PBUe6f`.

Three different query parameters are supported, but only one can used at a time:

* `version_id`: To get the DID Document at a specific version given by a sequence number.
* `version_time`: To get the DID Document at a specific time given by a UNIX timestamp.
* `resource`: If `resource=true` the raw nym data is returned

`POST: /update`

with payload:
```
{
    "diddoc": {...}
}
```

Only the update of the diddoc content of the trustee DID is supported (`did:indy:sandbox:V4SGRU86Z58d6TV7PBUe6f`).

The code is in [Azure EoT Common](https://dev.azure.com/economy-of-things/eot-common/_git/indy-did-server).

## How to start

```
git clone --single-branch --branch feature/did-indy https://github.com/domwoe/von-network
cd von-network
./manage build
./manage start
```
Indy DID server should be available at `http://localhost:9001`.

## Development

I used the Devcontainer extension from VSCode to develop in a container with all the Indy dependencies. 

The docker container is based on the image used by Github actions.

I needed to fix the installation of the Python VScode extension to an older version in order that discovering tests from vscode worked.

You can find my configuration [here](https://github.com/boschresearch/indy-node/tree/feature/did-indy-with-devcontainer-setup/.devcontainer)

I couldn't figure out how to debug properly and fell back to print statements...
Print statements in tests can be captured using [this method](https://docs.pytest.org/en/6.2.x/capture.html#accessing-captured-output-from-a-test-function).


