L1 DIDFacet — 实现

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 在 L1 Diamond Proxy 中实现 DIDFacet,支持 L2→L1 DID 状态同步和 L1→L2 DID 操作请求。

Architecture: Event-Driven 双向同步。L2 DID 系统合约通过 L1Messenger 发送状态变更消息,L1 DIDFacet 通过 Mailbox proof 验证并缓存。L1→L2 方向通过 Bridgehub requestL2TransactionDirect 发送优先交易。

Tech Stack: Solidity 0.8.26, Foundry (forge), MockMailbox, MockBridgehub, era-contracts system contracts


Task 1: DIDMessageLib + IDIDFacet 接口

Files:

  • Create: contracts/src/did/DIDMessageLib.sol

  • Create: contracts/src/did/IDIDFacet.sol

  • Create: contracts/test/mocks/MockBridgehub.sol

  • Test: contracts/test/DIDFacet.t.sol (初始骨架)

Step 1: Write DIDMessageLib

// contracts/src/did/DIDMessageLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/// @title DIDMessageLib
/// @notice Message type constants and encoding/decoding helpers for L2↔L1 DID sync.
library DIDMessageLib {
    // ── Message Types ────────────────────────────────────────────
    uint8 constant DID_CREATED = 0;
    uint8 constant DID_UPDATED = 1;
    uint8 constant DID_DEACTIVATED = 2;
    uint8 constant CREDENTIAL_ISSUED = 3;
    uint8 constant CREDENTIAL_REVOKED = 4;
    uint8 constant COMPLIANCE_UPDATED = 5;
    uint8 constant TRUSTED_ISSUER_ADDED = 6;
    uint8 constant TRUSTED_ISSUER_REMOVED = 7;

    // ── Errors ───────────────────────────────────────────────────
    error UnknownMessageType(uint8 messageType);

    // ── Decode helpers ───────────────────────────────────────────

    /// @notice Extract the message type (first byte) from encoded message
    function messageType(bytes calldata message) internal pure returns (uint8) {
        return abi.decode(message, (uint8));
    }

    /// @notice Decode DID_CREATED or DID_UPDATED message
    function decodeDIDDocument(bytes calldata message)
        internal
        pure
        returns (
            uint8 msgType,
            address identity,
            bytes32[] memory verificationMethods,
            bytes32 serviceEndpointHash,
            uint256 nonce
        )
    {
        (msgType, identity, verificationMethods, serviceEndpointHash, nonce) =
            abi.decode(message, (uint8, address, bytes32[], bytes32, uint256));
    }

    /// @notice Decode DID_DEACTIVATED message
    function decodeDIDDeactivated(bytes calldata message)
        internal
        pure
        returns (uint8 msgType, address identity, uint256 nonce)
    {
        (msgType, identity, nonce) = abi.decode(message, (uint8, address, uint256));
    }

    /// @notice Decode CREDENTIAL_ISSUED message
    function decodeCredentialIssued(bytes calldata message)
        internal
        pure
        returns (
            uint8 msgType,
            bytes32 credentialId,
            address issuer,
            address subject,
            bytes32 credentialType,
            bytes32 claimHash,
            uint64 issuedAt,
            uint64 expiresAt
        )
    {
        (msgType, credentialId, issuer, subject, credentialType, claimHash, issuedAt, expiresAt) =
            abi.decode(message, (uint8, bytes32, address, address, bytes32, bytes32, uint64, uint64));
    }

    /// @notice Decode CREDENTIAL_REVOKED message
    function decodeCredentialRevoked(bytes calldata message)
        internal
        pure
        returns (uint8 msgType, bytes32 credentialId, address issuer)
    {
        (msgType, credentialId, issuer) = abi.decode(message, (uint8, bytes32, address));
    }

    /// @notice Decode COMPLIANCE_UPDATED message
    function decodeComplianceUpdated(bytes calldata message)
        internal
        pure
        returns (uint8 msgType, address identity, bytes32 requirement, bool status)
    {
        (msgType, identity, requirement, status) =
            abi.decode(message, (uint8, address, bytes32, bool));
    }

    /// @notice Decode TRUSTED_ISSUER_ADDED or TRUSTED_ISSUER_REMOVED message
    function decodeTrustedIssuer(bytes calldata message)
        internal
        pure
        returns (uint8 msgType, address issuer)
    {
        (msgType, issuer) = abi.decode(message, (uint8, address));
    }
}

Step 2: Write IDIDFacet interface

Step 3: Write MockBridgehub

Step 4: Create minimal test skeleton and run to verify compilation

Run: cd contracts && forge test --match-contract DIDFacetTest -v Expected: 1 test PASS

Step 5: Commit


Task 2: DIDFacetLocal — L2→L1 Sync 实现 + 测试

Files:

  • Create: contracts/src/did/DIDFacetLocal.sol

  • Modify: contracts/test/DIDFacet.t.sol

Step 1: Write DIDFacetLocal with L2→L1 sync functions

Step 2: Write L2→L1 sync tests (14 tests)

Add these tests to contracts/test/DIDFacet.t.sol:

Step 3: Run tests

Run: cd contracts && forge test --match-contract DIDFacetTest -v Expected: 17 tests PASS

Step 4: Commit


Task 3: DIDFacetLocal — L1→L2 Request 实现 + 测试

Files:

  • Modify: contracts/src/did/DIDFacetLocal.sol

  • Modify: contracts/test/DIDFacet.t.sol

Step 1: Replace request function placeholders with real implementations

Replace the 5 placeholder request* functions in DIDFacetLocal.sol:

Note: Import L2TransactionRequestDirect struct and MockBridgehub — they must be accessible. Since DIDFacetLocal imports from MockBridgehub, move the struct to a shared location or import from the mock file. Simplest: import MockBridgehub directly in DIDFacetLocal (it's a Local test contract anyway).

Actually, better approach: create a minimal IBridgehub interface in contracts/src/interfaces/:

Then DIDFacetLocal imports IBridgehubLocal and MockBridgehub implements it.

Step 2: Write L1→L2 request tests (8 tests)

Add to contracts/test/DIDFacet.t.sol:

Step 3: Run tests

Run: cd contracts && forge test --match-contract DIDFacetTest -v Expected: 27 tests PASS (17 sync + 8 request + 2 admin)

Step 4: Commit


Task 4: E2E 流程测试 + 查询边界测试

Files:

  • Modify: contracts/test/DIDFacet.t.sol

Step 1: Add E2E and edge case tests (5 tests)

Step 2: Run tests

Run: cd contracts && forge test --match-contract DIDFacetTest -v Expected: 32 tests PASS

Step 3: Commit


Task 5: L2 系统合约 L1Messenger 集成

Files:

  • Modify: era-contracts-l1/system-contracts/contracts/DIDRegistry.sol

  • Modify: era-contracts-l1/system-contracts/contracts/CredentialRegistry.sol

  • Modify: era-contracts-l1/system-contracts/contracts/IdentityVerifier.sol

Step 1: Add L1Messenger calls to DIDRegistry.sol

Import L1Messenger and add calls to createDID(), updateDocument(), deactivateDID():

Step 2: Add L1Messenger calls to CredentialRegistry.sol

Step 3: Add L1Messenger calls to IdentityVerifier.sol

Step 4: Check IL1Messenger interface exists and has correct import path

The existing IL1Messenger.sol is at: era-contracts-l1/system-contracts/contracts/interfaces/IL1Messenger.sol

The L1_MESSENGER_CONTRACT constant is in Constants.sol at address 0x8008.

Verify correct import: import {IL1Messenger} from "./interfaces/IL1Messenger.sol"; and import {L1_MESSENGER_CONTRACT} from "./Constants.sol";

Note: Constants.sol defines address constant L1_MESSENGER_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x08); which resolves to 0x8008. However the import style varies — check existing imports in the file.

Step 5: Compile system contracts

Run: cd era-contracts-l1/system-contracts && npx hardhat compile Expected: Compilation succeeds (may need nvm use 20 first)

Step 6: Commit


Task 6: 全量回归测试 + dev-log 更新

Files:

  • Verify: all tests pass

  • Modify: docs/dev-log.md

Step 1: Run Foundry tests (contracts/)

Run: cd contracts && forge test -v Expected: All tests pass (previous 209 + new ~32 = ~241)

Step 2: Run system contract compilation (era-contracts-l1/)

Run: cd era-contracts-l1/system-contracts && nvm use 20 && npx hardhat compile Expected: Compilation succeeds

Step 3: Check era-core compilation

Run: cd era-core/core && ZKSYNC_USE_CUDA_STUBS=true cargo check Expected: No errors (era-core has no DIDFacet changes)

Step 4: Update dev-log

Append Phase 5b DIDFacet section to docs/dev-log.md.

Step 5: Commit


Summary

Task
内容
新文件
测试数

1

DIDMessageLib + IDIDFacet + MockBridgehub

4

1

2

DIDFacetLocal L2→L1 sync

1

+16 (17)

3

DIDFacetLocal L1→L2 request + admin

1

+10 (27)

4

E2E lifecycle + edge cases

0

+5 (32)

5

L2 系统合约 L1Messenger 集成

0

compile

6

全量回归 + dev-log

0

~241

Last updated