Skip to content

decentralized-identity/didwebvh-test-suite

Repository files navigation

didwebvh-test-suite

Language-agnostic compliance test vectors for the did:webvh v1.0 specification.

Approach

Multi-implementation cross-resolution pattern:

  1. Human-readable YAML scripts in vectors/<scenario>/script.yaml describe each test scenario.
  2. Each implementation reads the scripts, generates its own signed DID log artifacts, and resolves them to produce expected resolution results — all committed under vectors/<scenario>/<impl>/.
  3. Each implementation then resolves every implementation's committed logs (including its own) and compares raw resolver output against the committed expected results, writing implementations/<impl>/status.md.
  4. Divergences surface as differences in status.md — candidates for Working Group discussion and spec clarification.

There is no single "reference" implementation. The spec and Working Group are the arbiter of correctness.

Each implementation stands on its own. The resolutionResult.json files committed for each implementation are the raw output of that implementation's own resolver — no normalization is applied to make them conform to any other implementation's format. When an implementation resolves its own logs, the result should always match exactly (PASS). Cross-resolution DIFFs represent genuine differences in resolver behaviour between implementations and are the primary analytical output of this test suite.

Running the Test Suite

Prerequisites

  • Docker (used to build and run each implementation in isolation)
  • No other toolchain installation required — everything runs inside containers

Run all implementations (routine)

scripts/run-all

This does two passes of all five implementations. Pass 1 regenerates each implementation's own vectors; pass 2 cross-resolves with everyone's fresh vectors. Results are written directly into vectors/ and implementations/<impl>/status.md.

To run a subset:

scripts/run-all ts python

Run a single implementation

scripts/run ts
scripts/run python
scripts/run rust
scripts/run java
scripts/run java-eecc

Each call:

  1. Builds (or rebuilds from cache) a Docker image for that implementation at the main branch of its library.
  2. Runs the container, mounting vectors/ and implementations/<impl>/ from the local repo.
  3. Appends a provenance footer to status.md recording the exact library commit used.

Test a PR or feature branch

# Use the default repo, custom branch
scripts/run ts my-feature-branch

# Use a fork at a specific branch
scripts/run ts https://github.com/me/didwebvh-ts:feat/my-pr
scripts/run python https://github.com/me/didwebvh-py:fix/next-key-hashes
scripts/run rust https://github.com/me/didwebvh-rs:fix/witness-format

Docker layer caching means rebuilds are fast when only the branch tip has changed.

Reading the results

After a run, check implementations/<impl>/status.md. Each file has three sections:

Section Meaning
DID Creation Did the implementation successfully generate its own vectors from the scripts?
Negative Resolution Does the implementation correctly reject each intentionally invalid log?
Cross-Resolution Does the implementation resolve every other impl's committed logs correctly?

Result codes:

Code Meaning
✅ PASS Exact match (raw resolver output matches committed expected result)
🔶 DIFF Resolved successfully but output differs from the committed expected result — a genuine interop difference to analyse
❌ FAIL Resolution threw an error or was incorrectly accepted/rejected
⚠️ SKIP No artifacts committed (impl doesn't support this scenario)

Comparisons use JCS (JSON Canonicalization Scheme) to eliminate irrelevant differences in key ordering and whitespace, but no semantic normalizations are applied. A DIFF means the two resolvers genuinely disagree on the output.

The footer of each status.md records the exact library commit used for that run:

---
Built from: https://github.com/decentralized-identity/didwebvh-ts @ main (ab41190)

Repository Structure

vectors/                        # Committed artifacts, organized by scenario then implementation
  <scenario-name>/
    script.yaml                 # YAML DSL describing the scenario
    ts/
      did.jsonl                 # DID log generated by the TS implementation
      resolutionResult.json     # Resolution result at HEAD
      resolutionResult.<n>.json # Resolution result at version N (optional)
      did-witness.json          # Witness proofs (optional)
    python/
      did.jsonl
      resolutionResult*.json
    rust/
      did.jsonl
      resolutionResult*.json
    java/
      did.jsonl
      resolutionResult*.json
    java-eecc/
      did.jsonl
      resolutionResult*.json

implementations/
  ts/
    Dockerfile                  # Docker build for the TS harness
    src/generator.ts            # Generates vectors + runs cross-resolution
    status.md                   # Generated results (do not edit by hand)
    diffs.txt
    config.yaml                 # Library home repo, version, commit
  python/
    Dockerfile
    generate.py
    test_vectors.py
    status.md
    config.yaml
  rust/
    Dockerfile
    src/main.rs
    src/generate.rs
    status.md
    config.yaml
  java/
    Dockerfile
    src/...
    status.md
    config.yaml
  java-eecc/
    Dockerfile
    src/...
    status.md
    config.yaml

scripts/
  run          # Build + run a single implementation
  run-all      # Two-pass run of all implementations

Scenario Coverage

Happy-path scenarios

Scenario Description
basic-create Minimal create + resolve at HEAD
basic-update Create + single update + resolve
key-rotation Create + update rotating the update key
pre-rotation Create with nextKeyHashes commitment
pre-rotation-consume Create with nextKeyHashes; update signed by the pre-rotated key
deactivate Create + deactivate + resolve (deactivated state)
portable Create with portable: true
portable-move Create portable DID; update migrating to a new domain
witness-threshold Create with a witness list; witness proof provided on resolve
witness-update Create with 2-of-2 witness config; update reducing to 1-of-1
multi-update Create + two updates; resolve at v1, v2, and HEAD
multiple-update-keys Create with two updateKeys; update signed by the second key
services Create with service endpoints; update adding a second service

Negative scenarios

16 negative-* scenarios cover intentionally invalid logs. Each implementation resolves them and reports PASS (correctly rejected) or FAIL (incorrectly accepted).


DSL Script Format

Each script.yaml uses a small DSL:

description: "Create a DID and resolve at genesis"
spec_ref: "https://identity.foundation/didwebvh/v1.0/#creating-a-did"

keys:
  - id: key-0
    type: ed25519
    seed: "0000000000000000000000000000000000000000000000000000000000000001"

steps:
  - op: create
    domain: example.com
    signer: key-0
    params:
      updateKeys: ["key-0"]

  - op: resolve
    expect: resolutionResult.json

Supported op values

op description
create Create the DID log (first entry)
update Append an update entry
deactivate Append a deactivation entry
resolve Assert resolution output matches the named expect file

params (on create / update)

param type notes
updateKeys string[] Keys authorised for future updates
nextKeyHashes string[] Pre-rotated key hashes
witness object {threshold, witnesses:[{id,weight}]}
portable boolean Whether the DID is portable
context string[] Additional @context entries
alsoKnownAs string[] alsoKnownAs entries
services object[] Service endpoint entries
verificationMethods object[] Additional verification methods

Keys are always derived deterministically from seed — the generator never calls crypto.getRandomValues(), so re-running it produces bit-for-bit identical output.


Development: Running Harnesses Directly

The Docker approach above is the recommended way to run. For local development without Docker:

TypeScript

corepack enable             # one-time: activates the pinned pnpm version
pnpm install
pnpm run generate            # regenerate all TS vectors + cross-resolution
pnpm run generate <scenario> # single scenario
pnpm run verify              # CI check: verify committed artifacts match generator output

Python

cd implementations/python
python -m venv .venv && source .venv/bin/activate
pip install did-webvh pytest pytest-asyncio pyyaml
python generate.py          # generate Python vectors
pytest test_vectors.py      # cross-resolution tests

Rust

cd implementations/rust
cargo run --bin generate-vectors   # generate Rust vectors
cargo run --bin test-vectors       # cross-resolution + negative tests

Java

cd implementations/java
mvn compile exec:java@generate-vectors   # generate Java vectors
mvn compile exec:java                    # cross-resolution + negative tests

Java-EECC

cd implementations/java-eecc
mvn compile exec:java@generate-vectors
mvn compile exec:java

License

Apache License Version 2.0

About

A test suite for the did:webvh DID Method for use by did:webvh implementations.

Resources

License

Stars

2 stars

Watchers

4 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors