IdentityRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract IdentityRegistry {
mapping(string => bytes) public issuerKeys;
mapping(string => address) public issuerOwners;
event IssuerRegistered(string did, address owner);
event IssuerUpdated(string did);
/// Register DID → ML-DSA public key mapping
function registerIssuer(
string calldata did,
bytes calldata mlDsaPublicKey
) external {
require(issuerOwners[did] == address(0)
|| issuerOwners[did] == msg.sender,
"Not owner");
issuerKeys[did] = mlDsaPublicKey;
issuerOwners[did] = msg.sender;
emit IssuerRegistered(did, msg.sender);
}
/// Free read — no gas
function getIssuerKey(string calldata did)
external view returns (bytes memory)
{ return issuerKeys[did]; }
function isRegistered(string calldata did)
external view returns (bool)
{ return issuerKeys[did].length > 0; }
}
RevocationRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract RevocationRegistry {
mapping(address => bytes32) public merkleRoots;
mapping(bytes32 => bool) private _revoked;
event RootUpdated(address issuer, bytes32 newRoot);
event CredentialRevoked(bytes32 credId);
/// Issuer anchors new Merkle root (~50k gas)
function updateRoot(bytes32 newRoot) external {
merkleRoots[msg.sender] = newRoot;
emit RootUpdated(msg.sender, newRoot);
}
/// Explicitly revoke a credential ID
function revoke(bytes32 credentialId) external {
_revoked[credentialId] = true;
emit CredentialRevoked(credentialId);
}
/// Free read — no gas, instant
function isRevoked(bytes32 credentialId)
external view returns (bool)
{ return _revoked[credentialId]; }
function getRoot(address issuer)
external view returns (bytes32)
{ return merkleRoots[issuer]; }
}
LightweightVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IIdentityRegistry { function isRegistered(string calldata did) external view returns(bool); }
interface IRevocationRegistry { function isRevoked(bytes32 credId) external view returns(bool); }
contract LightweightVerifier {
IIdentityRegistry public immutable idReg;
IRevocationRegistry public immutable revReg;
constructor(address _id, address _rev) { idReg = IIdentityRegistry(_id); revReg = IRevocationRegistry(_rev); }
/**
* Light verification: checks on-chain state only.
* Full STARK proof verification happens off-chain.
* @param issuerDid DID of credential issuer
* @param credentialId keccak256 of credential JSON
* @param merkleRoot Root submitted by prover — must match on-chain root
*/
function verify(
string calldata issuerDid,
bytes32 credentialId,
bytes32 merkleRoot
) external view returns (bool valid, string memory reason) {
if (!idReg.isRegistered(issuerDid)) return (false, "Issuer not registered");
if (revReg.isRevoked(credentialId)) return (false, "Credential revoked");
// merkleRoot match checked off-chain against RevocationRegistry.getRoot()
return (true, "Valid");
}
}