{"action":"created","comment":{"url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/comments/229155545","pull_request_review_id":169587627,"id":229155545,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyOTE1NTU0NQ==","diff_hunk":"@@ -1,16 +1,338 @@\n+import { DidDocument } from '@decentralized-identity/did-common-typescript';\n+import * as Base58 from 'bs58';\n+import { Cas } from './Cas';\n+import Multihash from './Multihash';\n+import { WriteOperation, OperationType } from './Operation';\n import Transaction from './Transaction';\n-import { WriteOperation } from './Operation';\n \n /**\n- * Interface for caching all the processed DIDs and related states.\n- * This interface is mainly useful for creating a mock instances for testing purposes.\n+ * VersionId identifies the version of a DID document. We use the hash of the\n+ * operation that produces a particular version of a DID document as its versionId.\n+ * This usage is guaranteed to produce unique VersionId's since the operation contains\n+ * as one of its properties the previous VersionId. Since the operation hash is\n+ * just a string we alias VersionId to string.\n+ *\n+ * With this usage, the operation hash serves two roles (1) an identifier for an operation\n+ * (2) an identifier for the DID document produced by the operation. In the code below,\n+ * we always use VersionId in places where we mean (2) and an OperationHash defined below\n+ * when we mean (1).\n  */\n-export interface DidCache {\n-  /** The transaction that was COMPLETELY processed. */\n-  readonly lastProcessedTransaction?: Transaction;\n+export type VersionId = string;\n+\n+/**\n+ * Alias OperationHash to string - see comment above\n+ */\n+export type OperationHash = string;\n+\n+/**\n+ * Function type that updates a Did document given an operation. This would be instantiated\n+ * with a function that implements json patch application. Using the interface instead of\n+ * the actual function hides details of json patching from Did cache.\n+ */\n+export interface DidDocumentUpdater {\n+  (didDoc: DidDocument, operation: WriteOperation): DidDocument;\n+}\n+\n+/**\n+ * Function type that creates a Did document given a create/recover operation.\n+ */\n+export interface DidDocumentCreator {\n+  (createOp: WriteOperation): DidDocument;\n+}\n+\n+/**\n+ * The timestamp of an operation. We define a linear ordering of\n+ * timestamps using the function lesser below.\n+ */\n+interface OperationTimestamp {\n+  readonly transactionNumber: number;\n+  readonly operationIndex: number;\n+}\n+\n+function lesser (ts1: OperationTimestamp, ts2: OperationTimestamp): boolean {\n+  return ((ts1.transactionNumber < ts2.transactionNumber) ||\n+          (ts1.transactionNumber === ts2.transactionNumber) && (ts1.operationIndex < ts2.operationIndex));\n+}\n+\n+/**\n+ * Information about a write operation relevant for the DID cache, a subset of the properties exposed by\n+ * WriteOperation.\n+ */\n+interface OperationInfo extends OperationTimestamp {\n+  readonly batchFileHash: string;\n+  readonly type: OperationType;\n+}\n+\n+/**\n+ * DIDCache is a singleton class whose instance holds most of the state in Sidetree node.\n+ * It exposes methods to record sidetree DID state changes (create, update, delete, recover)\n+ * and methods to retrieve current and historical states of a DID document.\n+ *\n+ * The current implementation is a main-memory implementation without any persistence. This\n+ * means that when a node is powered down and restarted DID operations need to be applied\n+ * from the beginning of time. This implementation will be extended in the future to support\n+ * persistence.\n+ */\n+export class DidCache {\n+  /**\n+   * Map a versionId to the next versionId whenever one exists.\n+   */\n+  private nextVersion: Map<VersionId, VersionId> = new Map();\n+\n+  /**\n+   * Map a operation hash to the OperationInfo which contains sufficient\n+   * information to reconstruct the operation.\n+   */\n+  private opHashToInfo: Map<OperationHash, OperationInfo> = new Map();\n+\n+  /**\n+   * Apply (perform) a specified DID state changing operation.\n+   */\n+  public apply (operation: WriteOperation): string | null {\n+    const opHash = DidCache.getHash(operation);\n+\n+    // Ignore operations without the required metadata - any operation anchored\n+    // in a blockchain should have this metadata.\n+    if (operation.transactionNumber === undefined ||\n+        operation.operationIndex === undefined ||\n+        operation.batchFileHash === undefined) {\n+      return null;\n+    }\n+\n+    // opInfo is operation with derivable properties projected out\n+    const opInfo: OperationInfo = {\n+      transactionNumber: operation.transactionNumber,\n+      operationIndex: operation.operationIndex,\n+      batchFileHash: operation.batchFileHash,\n+      type: operation.type\n+    };\n+\n+    // If this is a duplicate of an earlier operation, we can\n+    // ignore this operation. Note that we might have a previous\n+    // operation with the same hash, but that previous operation\n+    // need not be earlier in timestamp order - hence the check\n+    // with lesser().\n+    const prevOperation = this.opHashToInfo.get(opHash);\n+    if (prevOperation !== undefined && lesser(prevOperation, opInfo)) {\n+      return null;\n+    }\n+    // Update our mapping of operation hash to operation info overwriting\n+    // previous info if it exists\n+    this.opHashToInfo.set(opHash, opInfo);\n+\n+    // For operations that have a previous version, we need additional\n+    // bookkeeping\n+    if (operation.previousOperationHash) {\n+      this.applyOpWithPrev(opHash, opInfo, operation.previousOperationHash);\n+    }\n+\n+    return opHash;\n+  }\n+\n+  public constructor (\n+    private readonly cas: Cas,\n+    private readonly didDocUpdate: DidDocumentUpdater,\n+    private readonly didDocCreate: DidDocumentCreator) {\n+\n+  }\n+\n+  /**\n+   * Rollback the state of the DidCache by removing all operations\n+   * with transactionNumber greater than the provided parameter value.\n+   * The intended use case for this method is to handle rollbacks\n+   * in the blockchain.\n+   *\n+   * The current implementation is inefficient: It simply scans the two\n+   * hashmaps storing the core Did state and removes all entries with\n+   * a greater transaction number.  In future, the implementation should be optimized\n+   * for the common case by keeping a sliding window of recent operations.\n+   */\n+  public rollback (transactionNumber: number) {\n+\n+    // Iterate over all nextVersion entries and remove all versions\n+    // with \"next\" operation with transactionNumber greater than the provided\n+    // parameter.\n+    this.nextVersion.forEach((opHash, version, map) => {\n+      const opInfo = this.opHashToInfo.get(opHash) as OperationInfo;\n+      if (opInfo.transactionNumber > transactionNumber) {\n+        map.delete(version);\n+      }\n+    });\n+\n+    // Iterate over all operations and remove those with with\n+    // transactionNumber greater than the provided parameter.\n+    this.opHashToInfo.forEach((opInfo, opHash, map) => {\n+      if (opInfo.transactionNumber > transactionNumber) {\n+        map.delete(opHash);\n+      }\n+    });\n+  }\n+\n+  /**\n+   * Resolve a did.\n+   */\n+  public async resolve (did: VersionId): Promise<DidDocument | null> {\n+    const latestVersion = this.last(did);\n+\n+    // lastVersion === null implies we do not know about the did\n+    if (latestVersion === null) {\n+      return null;\n+    }\n+\n+    return this.lookup(latestVersion);\n+  }\n+\n+  /**\n+   * Returns the Did document for a given version identifier.\n+   */\n+  public async lookup (versionId: VersionId): Promise<DidDocument | null> {\n+    // Version id is also the operation hash that produces the document\n+    const opHash = versionId;\n+\n+    const opInfo = this.opHashToInfo.get(opHash);\n+\n+    // We don't know anything about this operation\n+    if (opInfo === undefined) {\n+      return null;\n+    }\n+\n+    // Construct the operation using a CAS lookup\n+    const op = await this.getOperation(opInfo);\n+\n+    if (this.isInitialVersion(opInfo)) {\n+      return this.didDocCreate(op);\n+    } else {\n+      const prevVersion = op.previousOperationHash as VersionId;\n+      const prevDidDoc = await this.lookup(prevVersion);\n+      if (prevDidDoc === null) {\n+        return null;\n+      } else {\n+        return this.didDocUpdate(prevDidDoc, op);\n+      }\n+    }\n+  }\n+\n+  /**\n+   * Return the previous version id of a given DID version. The implementation\n+   * is inefficient and involves an async cas read. This should not be a problem\n+   * since this method is not hit for any of the externally exposed DID operations.\n+   */\n+  public async prev (versionId: VersionId): Promise<VersionId | null> {\n+    const opInfo = this.opHashToInfo.get(versionId);\n+    if (opInfo) {\n+      const op = await this.getOperation(opInfo);\n+      if (op.previousOperationHash) {\n+        return op.previousOperationHash;\n+      }\n+    }\n+    return null;\n+  }\n+\n+  /**\n+   * Return the first version of a DID document given a possibly later version.\n+   * A simple recursive implementation using prev; not very efficient but should\n+   * not matter since this method is not hit for any externally exposed DID\n+   * operations.\n+   */\n+  public async first (versionId: VersionId): Promise<VersionId | null> {\n+    const opInfo = this.opHashToInfo.get(versionId);\n+    if (opInfo === undefined) {\n+      return null;\n+    }\n+\n+    if (this.isInitialVersion(opInfo)) {\n+      return versionId;\n+    }\n+\n+    const prevVersionId = await this.prev(versionId);\n+    if (prevVersionId === null) {\n+      return null;\n+    }\n+\n+    return this.first(prevVersionId);\n+  }\n+\n+  /**\n+   * Return the next version of a DID document if it exists or null, otherwise.\n+   */\n+  public next (versionId: VersionId): VersionId | null {\n+    const nextVersionId = this.nextVersion.get(versionId);\n+    if (nextVersionId === undefined) {\n+      return null;\n+    } else {\n+      return nextVersionId;\n+    }\n+  }\n+\n+  /**\n+   * Return the latest (most recent) version of a DID document. Return null if\n+   * the version is unknown.\n+   */\n+  public last (versionId: VersionId): VersionId | null {\n+    const opInfo = this.opHashToInfo.get(versionId);\n+    if (opInfo === undefined) {\n+      return null;\n+    }\n+\n+    const nextVersionId = this.next(versionId);\n+    if (nextVersionId === null) {\n+      return versionId;\n+    } else {\n+      return this.last(nextVersionId);\n+    }\n+  }\n+\n+  /**\n+   * Get the last processed transaction.\n+   * TODO: fix this after discussing the intended semantics.\n+   */\n+  public get lastProcessedTransaction (): Transaction | undefined {\n+    return undefined;\n+  }\n+\n+  /**\n+   * Get a cryptographic hash of the write operation. Currently, uses\n+   * SHA256 to get hashes (TODO: Fix it to be consistent DID generation)\n+   */\n+  private static getHash (operation: WriteOperation): OperationHash {\n+    const sha256HashCode = 18;","path":"src/DidCache.ts","position":366,"original_position":304,"commit_id":"882015823751d2e39c69dcb77594a71d3614a350","original_commit_id":"daf8f79aeb6b624e1c7c995e20428fc7525a4375","user":{"login":"thehenrytsai","id":17891086,"node_id":"MDQ6VXNlcjE3ODkxMDg2","avatar_url":"https://avatars0.githubusercontent.com/u/17891086?v=4","gravatar_id":"","url":"https://api.github.com/users/thehenrytsai","html_url":"https://github.com/thehenrytsai","followers_url":"https://api.github.com/users/thehenrytsai/followers","following_url":"https://api.github.com/users/thehenrytsai/following{/other_user}","gists_url":"https://api.github.com/users/thehenrytsai/gists{/gist_id}","starred_url":"https://api.github.com/users/thehenrytsai/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thehenrytsai/subscriptions","organizations_url":"https://api.github.com/users/thehenrytsai/orgs","repos_url":"https://api.github.com/users/thehenrytsai/repos","events_url":"https://api.github.com/users/thehenrytsai/events{/privacy}","received_events_url":"https://api.github.com/users/thehenrytsai/received_events","type":"User","site_admin":false},"body":"We are in agreement, creating an issue to track this issue.","created_at":"2018-10-30T02:06:16Z","updated_at":"2018-10-30T02:06:16Z","html_url":"https://github.com/decentralized-identity/sidetree-core/pull/45#discussion_r229155545","pull_request_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/comments/229155545"},"html":{"href":"https://github.com/decentralized-identity/sidetree-core/pull/45#discussion_r229155545"},"pull_request":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45"}},"in_reply_to_id":228295429},"pull_request":{"url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45","id":225543125,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI1NTQzMTI1","html_url":"https://github.com/decentralized-identity/sidetree-core/pull/45","diff_url":"https://github.com/decentralized-identity/sidetree-core/pull/45.diff","patch_url":"https://github.com/decentralized-identity/sidetree-core/pull/45.patch","issue_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/45","number":45,"state":"closed","locked":false,"title":"Initial code for DID cache","user":{"login":"arvinda","id":4951270,"node_id":"MDQ6VXNlcjQ5NTEyNzA=","avatar_url":"https://avatars3.githubusercontent.com/u/4951270?v=4","gravatar_id":"","url":"https://api.github.com/users/arvinda","html_url":"https://github.com/arvinda","followers_url":"https://api.github.com/users/arvinda/followers","following_url":"https://api.github.com/users/arvinda/following{/other_user}","gists_url":"https://api.github.com/users/arvinda/gists{/gist_id}","starred_url":"https://api.github.com/users/arvinda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arvinda/subscriptions","organizations_url":"https://api.github.com/users/arvinda/orgs","repos_url":"https://api.github.com/users/arvinda/repos","events_url":"https://api.github.com/users/arvinda/events{/privacy}","received_events_url":"https://api.github.com/users/arvinda/received_events","type":"User","site_admin":false},"body":"Near-duplicate of a previous pull request that we accidentally merged and reverted.","created_at":"2018-10-24T20:39:31Z","updated_at":"2018-10-30T02:06:16Z","closed_at":"2018-10-26T19:28:55Z","merged_at":"2018-10-26T19:28:55Z","merge_commit_sha":"5200cad7d6cfd813bda9462bd73823b7e9471d84","assignee":null,"assignees":[],"requested_reviewers":[{"login":"csuwildcat","id":131786,"node_id":"MDQ6VXNlcjEzMTc4Ng==","avatar_url":"https://avatars1.githubusercontent.com/u/131786?v=4","gravatar_id":"","url":"https://api.github.com/users/csuwildcat","html_url":"https://github.com/csuwildcat","followers_url":"https://api.github.com/users/csuwildcat/followers","following_url":"https://api.github.com/users/csuwildcat/following{/other_user}","gists_url":"https://api.github.com/users/csuwildcat/gists{/gist_id}","starred_url":"https://api.github.com/users/csuwildcat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/csuwildcat/subscriptions","organizations_url":"https://api.github.com/users/csuwildcat/orgs","repos_url":"https://api.github.com/users/csuwildcat/repos","events_url":"https://api.github.com/users/csuwildcat/events{/privacy}","received_events_url":"https://api.github.com/users/csuwildcat/received_events","type":"User","site_admin":false},{"login":"sylar217","id":5566353,"node_id":"MDQ6VXNlcjU1NjYzNTM=","avatar_url":"https://avatars2.githubusercontent.com/u/5566353?v=4","gravatar_id":"","url":"https://api.github.com/users/sylar217","html_url":"https://github.com/sylar217","followers_url":"https://api.github.com/users/sylar217/followers","following_url":"https://api.github.com/users/sylar217/following{/other_user}","gists_url":"https://api.github.com/users/sylar217/gists{/gist_id}","starred_url":"https://api.github.com/users/sylar217/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sylar217/subscriptions","organizations_url":"https://api.github.com/users/sylar217/orgs","repos_url":"https://api.github.com/users/sylar217/repos","events_url":"https://api.github.com/users/sylar217/events{/privacy}","received_events_url":"https://api.github.com/users/sylar217/received_events","type":"User","site_admin":false},{"login":"srinathsetty","id":14947526,"node_id":"MDQ6VXNlcjE0OTQ3NTI2","avatar_url":"https://avatars2.githubusercontent.com/u/14947526?v=4","gravatar_id":"","url":"https://api.github.com/users/srinathsetty","html_url":"https://github.com/srinathsetty","followers_url":"https://api.github.com/users/srinathsetty/followers","following_url":"https://api.github.com/users/srinathsetty/following{/other_user}","gists_url":"https://api.github.com/users/srinathsetty/gists{/gist_id}","starred_url":"https://api.github.com/users/srinathsetty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/srinathsetty/subscriptions","organizations_url":"https://api.github.com/users/srinathsetty/orgs","repos_url":"https://api.github.com/users/srinathsetty/repos","events_url":"https://api.github.com/users/srinathsetty/events{/privacy}","received_events_url":"https://api.github.com/users/srinathsetty/received_events","type":"User","site_admin":false},{"login":"vihanda","id":38287496,"node_id":"MDQ6VXNlcjM4Mjg3NDk2","avatar_url":"https://avatars2.githubusercontent.com/u/38287496?v=4","gravatar_id":"","url":"https://api.github.com/users/vihanda","html_url":"https://github.com/vihanda","followers_url":"https://api.github.com/users/vihanda/followers","following_url":"https://api.github.com/users/vihanda/following{/other_user}","gists_url":"https://api.github.com/users/vihanda/gists{/gist_id}","starred_url":"https://api.github.com/users/vihanda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vihanda/subscriptions","organizations_url":"https://api.github.com/users/vihanda/orgs","repos_url":"https://api.github.com/users/vihanda/repos","events_url":"https://api.github.com/users/vihanda/events{/privacy}","received_events_url":"https://api.github.com/users/vihanda/received_events","type":"User","site_admin":false},{"login":"codeglobally","id":43512892,"node_id":"MDQ6VXNlcjQzNTEyODky","avatar_url":"https://avatars3.githubusercontent.com/u/43512892?v=4","gravatar_id":"","url":"https://api.github.com/users/codeglobally","html_url":"https://github.com/codeglobally","followers_url":"https://api.github.com/users/codeglobally/followers","following_url":"https://api.github.com/users/codeglobally/following{/other_user}","gists_url":"https://api.github.com/users/codeglobally/gists{/gist_id}","starred_url":"https://api.github.com/users/codeglobally/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/codeglobally/subscriptions","organizations_url":"https://api.github.com/users/codeglobally/orgs","repos_url":"https://api.github.com/users/codeglobally/repos","events_url":"https://api.github.com/users/codeglobally/events{/privacy}","received_events_url":"https://api.github.com/users/codeglobally/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45/commits","review_comments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45/comments","review_comment_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/45/comments","statuses_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/statuses/882015823751d2e39c69dcb77594a71d3614a350","head":{"label":"decentralized-identity:arvind/didcacheinitial","ref":"arvind/didcacheinitial","sha":"882015823751d2e39c69dcb77594a71d3614a350","user":{"login":"decentralized-identity","id":23265915,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzMjY1OTE1","avatar_url":"https://avatars3.githubusercontent.com/u/23265915?v=4","gravatar_id":"","url":"https://api.github.com/users/decentralized-identity","html_url":"https://github.com/decentralized-identity","followers_url":"https://api.github.com/users/decentralized-identity/followers","following_url":"https://api.github.com/users/decentralized-identity/following{/other_user}","gists_url":"https://api.github.com/users/decentralized-identity/gists{/gist_id}","starred_url":"https://api.github.com/users/decentralized-identity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/decentralized-identity/subscriptions","organizations_url":"https://api.github.com/users/decentralized-identity/orgs","repos_url":"https://api.github.com/users/decentralized-identity/repos","events_url":"https://api.github.com/users/decentralized-identity/events{/privacy}","received_events_url":"https://api.github.com/users/decentralized-identity/received_events","type":"Organization","site_admin":false},"repo":{"id":147382214,"node_id":"MDEwOlJlcG9zaXRvcnkxNDczODIyMTQ=","name":"sidetree-core","full_name":"decentralized-identity/sidetree-core","private":false,"owner":{"login":"decentralized-identity","id":23265915,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzMjY1OTE1","avatar_url":"https://avatars3.githubusercontent.com/u/23265915?v=4","gravatar_id":"","url":"https://api.github.com/users/decentralized-identity","html_url":"https://github.com/decentralized-identity","followers_url":"https://api.github.com/users/decentralized-identity/followers","following_url":"https://api.github.com/users/decentralized-identity/following{/other_user}","gists_url":"https://api.github.com/users/decentralized-identity/gists{/gist_id}","starred_url":"https://api.github.com/users/decentralized-identity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/decentralized-identity/subscriptions","organizations_url":"https://api.github.com/users/decentralized-identity/orgs","repos_url":"https://api.github.com/users/decentralized-identity/repos","events_url":"https://api.github.com/users/decentralized-identity/events{/privacy}","received_events_url":"https://api.github.com/users/decentralized-identity/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/decentralized-identity/sidetree-core","description":"The blockchain-agnostic server implementation of the Sidetree entity protocol","fork":false,"url":"https://api.github.com/repos/decentralized-identity/sidetree-core","forks_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/forks","keys_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/teams","hooks_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/hooks","issue_events_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/events{/number}","events_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/events","assignees_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/assignees{/user}","branches_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/branches{/branch}","tags_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/tags","blobs_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/statuses/{sha}","languages_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/languages","stargazers_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/stargazers","contributors_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/contributors","subscribers_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/subscribers","subscription_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/subscription","commits_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/contents/{+path}","compare_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/merges","archive_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/downloads","issues_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues{/number}","pulls_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls{/number}","milestones_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/milestones{/number}","notifications_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/labels{/name}","releases_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/releases{/id}","deployments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/deployments","created_at":"2018-09-04T17:20:14Z","updated_at":"2018-10-30T01:25:28Z","pushed_at":"2018-10-30T01:00:42Z","git_url":"git://github.com/decentralized-identity/sidetree-core.git","ssh_url":"git@github.com:decentralized-identity/sidetree-core.git","clone_url":"https://github.com/decentralized-identity/sidetree-core.git","svn_url":"https://github.com/decentralized-identity/sidetree-core","homepage":null,"size":644,"stargazers_count":8,"watchers_count":8,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"archived":false,"open_issues_count":16,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":3,"open_issues":16,"watchers":8,"default_branch":"master"}},"base":{"label":"decentralized-identity:master","ref":"master","sha":"4651fb0a505ebdf6bd599186a0053140b857f914","user":{"login":"decentralized-identity","id":23265915,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzMjY1OTE1","avatar_url":"https://avatars3.githubusercontent.com/u/23265915?v=4","gravatar_id":"","url":"https://api.github.com/users/decentralized-identity","html_url":"https://github.com/decentralized-identity","followers_url":"https://api.github.com/users/decentralized-identity/followers","following_url":"https://api.github.com/users/decentralized-identity/following{/other_user}","gists_url":"https://api.github.com/users/decentralized-identity/gists{/gist_id}","starred_url":"https://api.github.com/users/decentralized-identity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/decentralized-identity/subscriptions","organizations_url":"https://api.github.com/users/decentralized-identity/orgs","repos_url":"https://api.github.com/users/decentralized-identity/repos","events_url":"https://api.github.com/users/decentralized-identity/events{/privacy}","received_events_url":"https://api.github.com/users/decentralized-identity/received_events","type":"Organization","site_admin":false},"repo":{"id":147382214,"node_id":"MDEwOlJlcG9zaXRvcnkxNDczODIyMTQ=","name":"sidetree-core","full_name":"decentralized-identity/sidetree-core","private":false,"owner":{"login":"decentralized-identity","id":23265915,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzMjY1OTE1","avatar_url":"https://avatars3.githubusercontent.com/u/23265915?v=4","gravatar_id":"","url":"https://api.github.com/users/decentralized-identity","html_url":"https://github.com/decentralized-identity","followers_url":"https://api.github.com/users/decentralized-identity/followers","following_url":"https://api.github.com/users/decentralized-identity/following{/other_user}","gists_url":"https://api.github.com/users/decentralized-identity/gists{/gist_id}","starred_url":"https://api.github.com/users/decentralized-identity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/decentralized-identity/subscriptions","organizations_url":"https://api.github.com/users/decentralized-identity/orgs","repos_url":"https://api.github.com/users/decentralized-identity/repos","events_url":"https://api.github.com/users/decentralized-identity/events{/privacy}","received_events_url":"https://api.github.com/users/decentralized-identity/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/decentralized-identity/sidetree-core","description":"The blockchain-agnostic server implementation of the Sidetree entity protocol","fork":false,"url":"https://api.github.com/repos/decentralized-identity/sidetree-core","forks_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/forks","keys_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/teams","hooks_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/hooks","issue_events_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/events{/number}","events_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/events","assignees_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/assignees{/user}","branches_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/branches{/branch}","tags_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/tags","blobs_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/statuses/{sha}","languages_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/languages","stargazers_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/stargazers","contributors_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/contributors","subscribers_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/subscribers","subscription_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/subscription","commits_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/contents/{+path}","compare_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/merges","archive_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/downloads","issues_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues{/number}","pulls_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls{/number}","milestones_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/milestones{/number}","notifications_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/labels{/name}","releases_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/releases{/id}","deployments_url":"https://api.github.com/repos/decentralized-identity/sidetree-core/deployments","created_at":"2018-09-04T17:20:14Z","updated_at":"2018-10-30T01:25:28Z","pushed_at":"2018-10-30T01:00:42Z","git_url":"git://github.com/decentralized-identity/sidetree-core.git","ssh_url":"git@github.com:decentralized-identity/sidetree-core.git","clone_url":"https://github.com/decentralized-identity/sidetree-core.git","svn_url":"https://github.com/decentralized-identity/sidetree-core","homepage":null,"size":644,"stargazers_count":8,"watchers_count":8,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"archived":false,"open_issues_count":16,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":3,"open_issues":16,"watchers":8,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45"},"html":{"href":"https://github.com/decentralized-identity/sidetree-core/pull/45"},"issue":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/45"},"comments":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/issues/45/comments"},"review_comments":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45/comments"},"review_comment":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/pulls/45/commits"},"statuses":{"href":"https://api.github.com/repos/decentralized-identity/sidetree-core/statuses/882015823751d2e39c69dcb77594a71d3614a350"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"thehenrytsai"}}