Skip to content

Compact Contract Surface

The Compact contract stores Midnight DID state and proves controller-authorized state transitions. The TypeScript API exposes ergonomic add/update/remove helpers, but the contract intentionally keeps the exported circuit count small.

Contract Responsibilities

The contract enforces:

  • controller authorization through wallet-local Jubjub Schnorr signatures;
  • active/deactivated state checks;
  • exact ledger identifier existence and uniqueness;
  • supported opaque JWK key/curve profiles;
  • native SchnorrJubjub point storage;
  • verification-method deletion guards that reject every still-referenced method.

The SDK/domain/resolver layers enforce DID URL subject binding, fragment normalization, DID Core object shape, service endpoint shape, JWK canonicality, and resolved DID Document output.

Circuit Map

CircuitAPI helperLedger fieldsMutation style
rotateControllerKeyrotateControllerKeycontrollerPublicKey, updated, versionReplaces the controller Jubjub public key with a locally derived public key
setVerificationMethodaddVerificationMethod, updateVerificationMethodverificationMethodsMapMutation.Insert or MapMutation.Update
removeVerificationMethodremoveVerificationMethodverificationMethodsReject while any relation set references the method
setSchnorrJubjubVerificationMethodaddSchnorrJubjubVerificationMethod, updateSchnorrJubjubVerificationMethodschnorrJubjubVerificationMethodsMapMutation.Insert or MapMutation.Update
removeSchnorrJubjubVerificationMethodremoveSchnorrJubjubVerificationMethodschnorrJubjubVerificationMethodsReject while any relation set references the method
verifySchnorrJubjubDigestSignatureverifySchnorrJubjubDigestSignatureReads schnorrJubjubVerificationMethodsNon-mutating transaction-backed proof
setVerificationMethodRelationaddVerificationMethodRelation, removeVerificationMethodRelationauthentication, assertionMethod, keyAgreement, capabilityInvocation, capabilityDelegationSetMutation.Insert or SetMutation.Remove
setServiceaddService, updateServiceservicesMapMutation.Insert or MapMutation.Update
removeServiceremoveServiceservicesRemove by id
setAlsoKnownAsaddAlsoKnownAs, removeAlsoKnownAsalsoKnownAsSetMutation.Insert or SetMutation.Remove
deactivatedeactivateactive, deactivated, updated, versionFinal lifecycle transition

Circuit Artifact Profile

The following profile is generated from the managed DID artifacts compiled with Compact toolchain 0.31.1. The k and row values come from zkir compile -v; artifact sizes are byte sizes for files under packages/contract/src/managed/did.

Circuitkrowsprover keyverifier keybzkirzkir
rotateControllerKey111,930689,028 B1,591 B968 B14,057 B
setVerificationMethod122,3591,349,033 B1,591 B2,023 B27,314 B
removeVerificationMethod111,831688,875 B1,591 B1,057 B16,187 B
setSchnorrJubjubVerificationMethod112,030689,481 B1,591 B1,191 B16,425 B
removeSchnorrJubjubVerificationMethod111,831688,838 B1,591 B1,050 B16,142 B
verifySchnorrJubjubDigestSignature111,608687,799 B1,591 B381 B4,847 B
setVerificationMethodRelation122,7181,352,930 B1,591 B3,600 B46,279 B
setService111,991689,331 B1,591 B1,037 B14,594 B
removeService111,806688,589 B1,591 B699 B10,019 B
setAlsoKnownAs111,974689,248 B1,591 B1,086 B14,729 B
deactivate111,804688,594 B1,591 B670 B9,955 B

Why The Surface Is Small

Every exported Compact circuit produces proving/verifier artifacts and contributes to deployment footprint. A symmetric add/update/remove circuit for every API helper can exceed current standalone Midnight block limits.

The contract therefore exports compact set/toggle circuits and uses explicit mutation enums instead of ambiguous booleans. The API can still expose natural helpers such as addVerificationMethod and updateVerificationMethod; those helpers map to the same circuit with the appropriate mutation value.

Each API mutation maps to one circuit call. In particular, verification-method removal does not compose relationship cleanup: applications explicitly call removeVerificationMethodRelation for each selected relationship before the method-removal circuit. Those transactions finalize independently and cannot be made atomic by merging Midnight contract-call sections. The contract remains authoritative by checking every relation set during method removal, while the API mirrors the check as typed preflight feedback.

Key Storage

Non-native JWK keys are stored as opaque canonical strings in verificationMethods. SchnorrJubjub keys are stored as native JubjubPoint values in schnorrJubjubVerificationMethods. Resolvers merge both maps into the final DID Document.

See Key Model for the supported key profiles and controller authorization signature model.

Ledger-Bound SchnorrJubjub Verification

verifySchnorrJubjubDigestSignature accepts a verification method id, private digest, and private signature. The private circuit inputs are the digest and signature, not the controller secret. It reads the public key from schnorrJubjubVerificationMethods, so the proof is tied to the current DID ledger state instead of a caller-supplied public key.

Use the provider-aware overload so the SDK can fail closed while selecting the sole canonical or compatible legacy ledger key. The deprecated four-argument overload performs the same state-aware lookup for contract handles returned by deploy, createDID, or joinContract; unregistered handles retain only the historical fragment-key fallback.

Last updated:

Midnight DID reference implementation