did-btcr2-js

ADR 110: Resolution reports the required document metadata and DID Resolution error codes

Context

Four specification pull requests changed the resolve operation between ADR 105 and this decision:

The implementation differed on each point. The result of a never-updated identifier carried no confirmations. The api returned an empty didResolutionMetadata, and tryResolveDid reported the string internalError for every failure. A genesis document that the CAS did not return raised a plain Error; a missing signed update or CAS announcement raised a plain Error. CasApi.retrieve parsed the bytes with no hash check, so a gateway that served other content produced a JSON parse error or a document that failed later. Identifier.decode let the error of the Bech32m decoder escape for a bad checksum, bad padding, a bad length, or a bad character. The genesis hash mismatch raised INVALID_DID_DOCUMENT.

The identifier examples of the specification text (the encoding example with the secp256k1 generator point, the decoding example) were not in the tests. Every identifier vector in the suite was generated by this implementation.

Decision

The result carries the required metadata. DidResolutionResponse.metadata types confirmations and deactivated as required. The no-update result reports confirmations: 0. updated is absent until the resolver applies an update; the empty string default is gone. The api sets didResolutionMetadata.contentType to application/did.

The read path raises the error codes of the specification. Identifier.decode wraps the Bech32m decoder and raises an IdentifierError of type INVALID_DID for every decoding failure. Resolver.external raises INVALID_DID when the hash of the genesis document is not the genesis bytes. The api raises a ResolveError of type NOT_FOUND when the genesis document is not in the sidecar and the CAS does not return it, also when no CAS driver is configured. The api raises MISSING_UPDATE_DATA for a signed update or a CAS announcement in the same state. tryResolveDid reports the DID Resolution error code of the nearest typed failure in the cause chain, through the new helper resolutionErrorCode; every other failure reports INTERNAL_ERROR. common exports NOT_FOUND from the named error code list.

CasApi.retrieve checks the content hash. The method hashes the bytes that the executor returns and compares the hash to the requested address before it parses them. Content that does not match, content that is not JSON, and content that is not a JSON object raise a ResolveError of type MISSING_UPDATE_DATA: the specification says the resolver must not use the content, so the data is not available from that source. The genesis document path re-types the failure to NOT_FOUND and keeps the original error as the cause.

The identifier examples of the specification text are tests. encode-identifier.spec.ts encodes the generator point on bitcoin to the encoding example. decode-identifier.spec.ts decodes the encoding example and the decoding example. These vectors come from the specification text, not from the output of this implementation.

A copy of the specification example corpus (src/example-data) as test fixtures was rejected. The specification repository generates the corpus with the published packages of this repository, so a test against it compares the implementation with its own earlier output. The generator in the specification repository is the place where a divergence shows: a bump of its package pins re-runs the corpus through the new release.

Scope boundary

Consequences

Positive. A consumer of the api reads the DID Resolution error code from tryResolveDid().error and does not parse messages. A resolver that reads a CAS cannot use content that does not hash to the address.

Negative. tryResolveDid().error changes from the string internalError to INTERNAL_ERROR: a consumer that compares the old string must update. The genesis hash mismatch changes its code from INVALID_DID_DOCUMENT to INVALID_DID. DidResolutionResponse.metadata now requires confirmations and deactivated: a consumer that constructs the type must add them. A hand-written resolutionOptions with versionId still labels the genesis document with the requested version when no update exists; this stays until the resolver loop decision.

Implementation

References