Skip to content

did:ethr spec compliance#698

Open
mirceanis wants to merge 36 commits intospruceid:mainfrom
mirceanis:did-ethr-spec-compliance
Open

did:ethr spec compliance#698
mirceanis wants to merge 36 commits intospruceid:mainfrom
mirceanis:did-ethr-spec-compliance

Conversation

@mirceanis
Copy link
Copy Markdown

@mirceanis mirceanis commented Mar 23, 2026

Description

Aligns the Rust did:ethr resolver with the ERC-1056 / did:ethr method spec.
The resolver is now generic over an EthProvider trait, enabling both offline (genesis document) and on-chain resolution modes.

I only kept the offline mode for some form of backward compatibility but would prefer to error if no provider is configured since the genesis document behavior alone is not spec-compliant. The on-chain resolution mode is the main new feature here, and it fully implements the ERC-1056 event processing logic to build accurate DID documents reflecting all on-chain changes.

fixes #287

What changed

  • DIDEthr is now DIDEthr<P = ()> — generic over an Ethereum provider. DIDEthr<()> retains the existing offline-only behavior (genesis documents). When a provider implementing EthProvider is configured, the resolver walks the ERC-1056 DIDRegistry event log to build the full document.
  • EthProvider trait — a minimal async trait (call, get_logs, block_timestamp) that users implement with their preferred Ethereum client (ethers, alloy, raw HTTP, etc.). A working example using a bare JSON-RPC HTTP client is in examples/resolve_with_provider.rs.
  • Network configuration — providers are registered per network name via DIDEthr::add_network(). Named networks (mainnet, sepolia, etc) and hex chain IDs (0x5) are supported. Deprecated testnets (goerli, ropsten, rinkeby, etc.) still parse for backward compatibility.
  • On-chain event processing — full linked-list log walk of ERC-1056 events:
    • DIDOwnerChanged — ownership transfers
    • DIDDelegateChanged — veriKey and sigAuth delegate types
    • DIDAttributeChanged — attribute-based verification methods (secp256k1, Ed25519, X25519) and service endpoints (URL and JSON)
    • Deactivation via null-address owner transfer
    • Expiry and revocation handling with stable #delegate-N / #service-N fragment IDs
  • Historical resolution — ?versionId=N resolves the document as of block N, with versionId, updated, nextVersionId, and nextUpdate in document metadata.
  • Metadata struct extended — added version_id, updated, next_version_id, next_update fields. Copy removed (breaking — now contains String fields).
  • Crate restructured — lib.rs split into focused modules: abi, events, network, provider, resolver, vm, json_ld_context.

Deliberate deviations from spec

  • Genesis document resolver behavior is preserved but spec-inaccurate! — the existing offline resolver (DIDEthr<()>) produces genesis documents containing EcdsaSecp256k1RecoveryMethod2020 and Eip712Method2021 verification methods. These are not strictly correct per the did:ethr spec but are retained for backward compatibility with existing VC/VP signing and verification workflows in this project.
  • JWK-only key representation — the key encoding hint from DIDAttributeChanged attribute names (e.g. did/pub/secp256k1/veriKey/hex) is ignored. All attribute-based keys are materialized as JWK (publicKeyJwk) for secp256k1 or multibase for Ed25519/X25519, regardless of the hint. This is because I plan to remove the encoding hint from the spec.
  • RSA keys not supported — mentioned in the spec but not implemented as nobody is actually using this.

Usage changes

  // Before:
  let resolver = DIDEthr;
  
  // After (offline, equivalent behavior):
  let resolver = DIDEthr::<()>::default();
  
  // After (with on-chain resolution):
  let mut resolver = DIDEthr::new();
  resolver.add_network("mainnet", NetworkConfig {
    chain_id: 1,
    registry: REGISTRY_ADDRESS,
    provider: my_eth_provider,
  });
  
  DIDEthr::generate() is unchanged.

Other changes

multibase for Ed25519/X25519 encoding was inaccurate, missing the varint prefix.

Now we encode the raw key bytes as multibase(base58btc) with a multicodec varint prefix.
This matches the encoding expected by Ed25519VerificationKey2020 and X25519KeyAgreementKey2020 verification method types per W3C spec.

Optional section

  • Only workspace dependencies were added to the resolver crate.
  • Most of this PR is test code. Even so, it's a big feature which I'm not sure how to make smaller since processing on-chain events is the core new feature here.

Tested

Extensive unit test suite was added to the ethr crate and an example was added demonstrating on-chain resolution with a custom provider:

cargo run --example resolve_with_provider

will resolve a live mainnet DID that had an owner change. The blockchainAccountId of of the #controller will show a different address than the DID identifier's address, confirming that the on-chain event processing is working correctly.

You can test other DIDs and networks:

cargo run --example resolve_with_provider did:ethr:mainnet:0x0a327968f9f2592b566279357fb05e04498ac81f`
cargo run --example resolve_with_provider did:ethr:sepolia:0xa1e6186864ad83d315fc630b371967aab38228ff https://sepolia.gateway.tenderly.co

I'm the maintainer of the did:ethr spec and reference implementation. Feel free to question. I'm looking for feedback from real-world usage.

mirceanis added 30 commits March 9, 2026 20:16
add mock provider tests for changed=0 and identityOwner=same;
Erc1056Event enum, keccak256 topic hashes, collect_events(),
parse_erc1056_event(), enhanced LogFilter (topic0/topic1),
MockProvider with logs support. 4 new tests.
filter events by target block, use block timestamp for validTo, populate nextVersionId/nextUpdate, genesis fallback
remove deprecated testnets, add sepolia, fix Georli→Goerli typo, backward-compat aliases for deprecated names
- Switch attribute key encoding to canonical property per VM type (e.g. secp256k1 keys use publicKeyJwk, Ed25519/X25519 use publicKeyMultibase)
- Add support for Ed25519VerificationKey2020 and X25519KeyAgreementKey2020 attribute keys
- Revise JsonLdContext to bind only relevant properties and VM types
- Use content-keyed maps for event deduplication and revocation handling
- Ensure revoked delegates/services are removed from the DID document
- Update tests for new encoding, revocation, and context bindings
- Add bs58 and indexmap workspace dependencies
Add visited set to break cycles when multiple registry calls in the same
block cause previousChange == current_block. Follow only strictly
retreating pointers (prev < current_block), taking max to avoid skipping
intermediate blocks.

Add test: collect_events_same_block_events_all_collected
- provider.rs: BlockRef, LogFilter, Log, EthProvider, NetworkConfig
- abi.rs: ABI selectors, encode/decode helpers, formatting utils
- events.rs: Erc1056Event, parse/collect logic + collect_events tests
- network.rs: NetworkChain, InvalidNetwork, DecodedMethodSpecificId, parse_address_bytes
- vm.rs: VerificationMethod, VerificationMethodType, Pending* structs, decode_delegate_type
- resolver.rs: DIDEthr, apply_events, resolve_*, serialize_document + MockProvider tests
- lib.rs: module declarations + public re-exports + offline-only tests
- re-export keccak::keccak256 instead of wrapping
- network_name() returns &str to avoid clone
- remove calldata.clone() (moved by value)
- partition all_events in one pass via Iterator::partition
…TTP JSON-RPC

- Implements EthProvider over raw HTTP using reqwest
- Adds example to resolve did:ethr DIDs against real Ethereum endpoints
- Updates dependencies in Cargo.toml for example support
- Fixes DIDEthr instantiation for generic parameter compliance
- Accept DID and RPC URL as command-line arguments with sensible defaults
- Enhance error messages for missing or malformed JSON fields
- Refactor main logic into resolve_did for clarity and testability
- Print input parameters and handle resolution errors gracefully
…iry; improve uint256 decoding and multibase encoding

- Fix delegate/attribute expiry logic to use the target block's timestamp, not meta_block's, for historical resolution
- Make decode_uint256 return Result and handle overflows/short data
- Update event decoding to propagate uint256 decode errors
- Refactor account_address_hex to return Option and handle malformed keys
- Add encode_multibase_multicodec for correct Ed25519/X25519 multibase encoding per spec
- Add tests for historical expiry and multibase encoding
- Update dependencies for ssi-multicodec and multibase
Copilot AI review requested due to automatic review settings March 23, 2026 12:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the Rust did:ethr resolver to align with the ERC-1056 / did:ethr spec by adding an on-chain resolution path (via an EthProvider abstraction) while retaining the existing offline “genesis document” behavior for backward compatibility.

Changes:

  • Introduces EthProvider + per-network configuration and makes DIDEthr generic to support on-chain resolution.
  • Implements ERC-1056 event-log walking and event application to construct accurate DID Documents (including historical ?versionId= resolution and metadata).
  • Fixes/extends key materialization and JSON-LD context generation (including corrected multibase+multicodec encoding for Ed25519/X25519).

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
examples/resolve_with_provider.rs Adds a runnable JSON-RPC-based example provider and live resolution demo.
crates/dids/src/lib.rs Updates the “any DID method” resolver to use DIDEthr::<()>::default() for offline mode.
crates/dids/methods/ethr/tests/did-pk.jsonld Updates expected JSON-LD to include Eip712Method2021 for pubkey DIDs.
crates/dids/methods/ethr/src/abi.rs Adds ERC-1056 selectors, ABI encoding/decoding helpers, and timestamp formatting.
crates/dids/methods/ethr/src/events.rs Adds ERC-1056 log parsing and linked-list event collection logic (+ tests).
crates/dids/methods/ethr/src/json_ld_context.rs Extends context generation to cover more VM types and properties.
crates/dids/methods/ethr/src/lib.rs Restructures the crate into modules and re-exports the new public API.
crates/dids/methods/ethr/src/network.rs Implements network parsing (names + hex chain IDs) and address derivation for pubkey DIDs.
crates/dids/methods/ethr/src/provider.rs Introduces EthProvider, LogFilter, Log, BlockRef, and NetworkConfig.
crates/dids/methods/ethr/src/resolver.rs Implements the new resolver (offline + on-chain + historical resolution) and extensive tests.
crates/dids/methods/ethr/src/vm.rs Adds intermediate VM/service representations and VM type definitions.
crates/dids/methods/ethr/Cargo.toml Adds dependencies needed for on-chain resolution and multibase/multicodec encoding.
crates/dids/core/src/document.rs Extends DID resolution metadata with versionId/updated/nextVersionId/nextUpdate.
crates/crypto/src/hashes/keccak.rs Exposes a keccak256 helper used by the resolver crate.
Cargo.toml Adds workspace deps to support the new example and resolver usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…collection

- Add log_index to Log struct and propagate throughout event collection
- Sort events by (block_number, log_index) to maintain correct intra-block ordering
- Update tests to verify intra-block order is preserved
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

did:ethr doesn't support the spec it references

3 participants