decentralized-identity / element

DID Method implementation using the Sidetree protocol on top of Ethereum and IPFS
https://element-did.com/
Apache License 2.0
102 stars 28 forks source link
crypto dapp did ethereum ipfs protocol sidetree wg-sidetree

See sidetree.js

This repo is no longer maintained.

Element

Build Status codecov

🔥 Experimental Sidetree Protocol based DID Method elem with Ethereum and IPFS

See the DID method specification

See our blog post

[OUTDATED] Click below image for demo video.

Element Testnet Demo

See also ion, sidetree, sidetree-ethereum.

Useful resources

Getting Started

git clone git@github.com:decentralized-identity/element.git
cd element
npm install

Element follows the Mono Repo structure. Running npm install will install dependencies in the top level npm project as well as in the following packages:

How to use element-lib

cd packages/element-lib

Running the tests

In order to run the tests, you need to start Element services

npm run services:start

This will start 3 services:

Check that services are properly initalized with

npm run services:healthcheck

Then you can run the tests (note that running this command will initialize the services if they have not been initialized)

npm test

When you are done, you can stop the Element services by running

npm run services:stop

Initializing the Sidetree class

In order to use element-lib in node or in the browser, you will need to initalize the Sidetree class by providing three interfaces:

See several examples for how to initialize the Sidetree class:

Using element-lib to Create Read Update Delete DIDs

Once you have an instance of the Sidetree class with the suitable adapters, you can access all the helper functions (sidetree.func) and perform CRUD operations (sidetree.op). Here are a few code snippet to get you started:

Create a DID

const { Sidetree, MnemonicKeySystem } = require("@transmute/element-lib");

// Instantiate the Sidetree class
const element = new Sidetree(/* See previous section for how to initialize the Sidetree class*/);

// Generate a simple did document model
const mks = new MnemonicKeySystem(MnemonicKeySystem.generateMnemonic());
const primaryKey = await mks.getKeyForPurpose("primary", 0);
const recoveryKey = await mks.getKeyForPurpose("recovery", 0);
const didDocumentModel = element.op.getDidDocumentModel(
  primaryKey.publicKey,
  recoveryKey.publicKey
);

// Generate Sidetree Create payload
const createPayload = element.op.getCreatePayload(didDocumentModel, primaryKey);

// Create the Sidetree transaction.
// This can potentially take a few minutes if you're not on a local network
const createTransaction = await element.batchScheduler.writeNow(createPayload);
const didUniqueSuffix = element.func.getDidUniqueSuffix(createPayload);
const did = `did:elem:ropsten:${didUniqueSuffix}`;
console.log(`${did} was successfully created`);

Read a DID (aka resolve a DID)

const didDocument = await element.resolve(didUniqueSuffix, true);
console.log(
  `${did} was successfully resolved into ${JSON.stringify(
    didDocument,
    null,
    2
  )}`
);

Update a DID document

Add a new key to the did document

// Get last operation data
const operations = await element.db.readCollection(didUniqueSuffix);
const lastOperation = operations.pop();

// Generate update payload for adding a new key
const newKey = await mks.getKeyForPurpose("primary", 1);
const newPublicKey = {
  id: "#newKey",
  usage: "signing",
  type: "Secp256k1VerificationKey2018",
  publicKeyHex: newKey.publicKey,
};
const updatePayload = await element.op.getUpdatePayloadForAddingAKey(
  lastOperation,
  newPublicKey,
  primaryKey.privateKey
);

// Create the Sidetree transaction.
const updateTransaction = await element.batchScheduler.writeNow(updatePayload);
const newDidDocument = await element.resolve(didUniqueSuffix, true);
console.log(`${JSON.stringify(newDidDocument, null, 2)} has a new publicKey`);

Recover a did document

How to recover a did document using the recovery key if the private key is lost:

// Generate a recovery payload with the inital did document model
const recoveryPayload = await element.op.getRecoverPayload(
  didUniqueSuffix,
  didDocumentModel,
  recoveryKey.privateKey
);

// Send Sidetree transaction
const recoveryTransaction = await element.batchScheduler.writeNow(
  recoveryPayload
);
const recoveredDidDocument = await element.resolve(didUniqueSuffix, true);
console.log(`${JSON.stringify(recoveredDidDocument, null, 2)} was recovered`);

Delete a did document

// Generate a delete payload this will brick the did forever
const deletePayload = await element.op.getDeletePayload(
  didUniqueSuffix,
  recoveryKey.privateKey
);

// Send Sidetree transaction
const deleteTransaction = await element.batchScheduler.writeNow(deletePayload);
const deletedDidDocument = await element.resolve(didUniqueSuffix, true);
console.log(`${JSON.stringify(deletedDidDocument, null, 2)} was deleted`);

How to use element-api

Define the environment file

In the root level directory copy the example config

cp example.env .env

then fill the following values:

Then run the following create the api config file

cd packages/element-api
npm run env:create:prod

You may now start the API by running

npm run start # if you have setup a firebase project
npm run start:standalone # to run the standalone express version of the API

How to use element-app

All config is checked into source so you can run the app by:

npm run start

Useful commands

Install:

npm i

Run smart contract tests:

npm run test:contracts

Run lib, api and app tests:

npm run test

Lint

npm run lint

Coverage

npm run coverage

Publishing

If you have 2fa enabled for npm (and you should!).

lerna version patch
NPM_CONFIG_OTP=123456 lerna publish

Testing Documentation

npm i -g http-server
serve ./docs

See .travis.yml for setup and test commands for linux.

Docker

To run the APP in docker, run

docker run --rm -p 80:80 gjgd/element-app:latest

To run the API in docker, run

docker run --rm -p 80:5002 gjgd/element-api:latest

How to build Element APP with a different domain for the API:

  1. Clone Element
  2. cd packages/element-app
  3. edit the content of .env.production to the API_URL you want to use
  4. docker build -t my-tag .
  5. docker run --rm -p 80:5002 my-tag
  6. Now the app runs on port 80 and will use the API_URL specified in 3)

Release process

lerna publish