did:cow, a proposal for an ID resolution method with most of the convenience of did:plc/did:web and the robustness of a public blockchain
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

natspec comments

+64 -2
+64 -2
src/CowRegistry.sol
··· 1 1 // SPDX-License-Identifier: MIT 2 2 pragma solidity 0.8.28; 3 3 4 + /// @title CowRegistry 5 + /// @notice On-chain registry for the did:cow (Consensus Ownership Wrapper) DID method. 6 + /// @dev A did:cow DID is identified by its initial controller address and initial wrapped DID 7 + /// (stored without the leading "did:" prefix, e.g. "plc:abc123" or "web:example.com"). 8 + /// Creation is off-chain and free. This contract only needs to be called when migrating 9 + /// to a new wrapped DID, transferring control, or deactivating. 4 10 contract CowRegistry { 5 11 12 + /// @notice On-chain state for a single did:cow identifier. 13 + /// @dev The cowHash key is derived from the *initial* controller and wrapped DID, 14 + /// so these fields reflect the *current* state after any updates. 6 15 struct Cow { 7 16 address controller; 8 17 string wrappedDID; 9 18 } 10 19 20 + /// @dev Sentinel value stored in wrappedDID to mark a deactivated cow. 11 21 string constant DEACTIVATED = ":"; 12 22 23 + /// @notice Mapping from cow hash to current on-chain state. 24 + /// @dev Returns zero values if the cow has never been registered on-chain. 13 25 mapping(bytes32 => Cow) public cows; 14 26 27 + /// @notice Emitted when a cow is registered on-chain for the first time. 15 28 event CowInitialized(bytes32 indexed cowHash, address controller, string wrappedDID); 29 + 30 + /// @notice Emitted when a cow is permanently deactivated. 16 31 event CowDeactivated(bytes32 indexed cowHash); 32 + 33 + /// @notice Emitted when a cow's controller address is updated. 17 34 event ControllerUpdated(bytes32 indexed cowHash, address controller); 35 + 36 + /// @notice Emitted when a cow's wrapped DID is updated. 18 37 event WrappedDIDUpdated(bytes32 indexed cowHash, string wrappedDID); 19 38 39 + /// @notice Update the wrapped DID for an already-registered cow. 40 + /// @dev Caller must be the current controller. Use updateWrappedDID if the cow 41 + /// may not yet be registered on-chain. 42 + /// @param _cowHash The cow's registry key, as returned by calculateCowHash. 43 + /// @param _wrappedDID The new wrapped DID, without the leading "did:" prefix. 20 44 function updateWrappedDIDByHash(bytes32 _cowHash, string memory _wrappedDID) public { 21 45 require(msg.sender == cows[_cowHash].controller); 22 46 require(bytes(_wrappedDID).length > 1, "Use deactivate() to deactivate"); ··· 25 49 emit WrappedDIDUpdated(_cowHash, _wrappedDID); 26 50 } 27 51 52 + /// @notice Transfer control of an already-registered cow to a new address. 53 + /// @dev Caller must be the current controller. Use updateController if the cow 54 + /// may not yet be registered on-chain. 55 + /// Setting _controller to address(0) makes the cow permanently uncontrollable 56 + /// without deactivating it — it will continue to resolve but can never be updated. 57 + /// @param _cowHash The cow's registry key, as returned by calculateCowHash. 58 + /// @param _controller The new controller address. 28 59 function updateControllerByHash(bytes32 _cowHash, address _controller) public { 29 60 require(msg.sender == cows[_cowHash].controller); 30 61 ··· 32 63 emit ControllerUpdated(_cowHash, _controller); 33 64 } 34 65 66 + /// @notice Permanently deactivate an already-registered cow. 67 + /// @dev Caller must be the current controller. Deactivation is irreversible. 68 + /// Use deactivate if the cow may not yet be registered on-chain. 69 + /// @param _cowHash The cow's registry key, as returned by calculateCowHash. 35 70 function deactivateByHash(bytes32 _cowHash) public { 36 71 require(msg.sender == cows[_cowHash].controller); 37 72 ··· 41 76 emit CowDeactivated(_cowHash); 42 77 } 43 78 79 + /// @notice Derive the registry key for a did:cow identifier. 80 + /// @param _controller The initial controller address. 81 + /// @param _wrappedDID The initial wrapped DID, without the leading "did:" prefix. 82 + /// @return The keccak256 hash used as the key in the cows mapping. 44 83 function calculateCowHash(address _controller, string memory _wrappedDID) public pure returns (bytes32) { 45 84 return keccak256(abi.encodePacked(_controller, _wrappedDID)); 46 85 } 47 86 87 + /// @dev Register a cow on-chain if not already present, then return its hash. 48 88 function _ensureCowInitialized(address _controller, string memory _wrappedDID) internal returns (bytes32 cowHash) { 49 89 cowHash = calculateCowHash(_controller, _wrappedDID); 50 90 if (bytes(cows[cowHash].wrappedDID).length == 0) { ··· 54 94 return cowHash; 55 95 } 56 96 97 + /// @notice Resolve a did:cow identifier to its current controller and wrapped DID. 98 + /// @dev Returns the initial values if the cow has never been registered on-chain. 99 + /// Prepend "did:" to the returned wrappedDID to form the full wrapped DID string. 100 + /// @param _controller The initial controller address from the did:cow identifier. 101 + /// @param _wrappedDID The initial wrapped DID from the did:cow identifier, without "did:". 102 + /// @return controller The current controller address. 103 + /// @return wrappedDID The current wrapped DID (without "did:"), or ":" if deactivated. 57 104 function resolveCow(address _controller, string memory _wrappedDID) external view returns (address controller, string memory wrappedDID) { 58 105 bytes32 cowHash = calculateCowHash(_controller, _wrappedDID); 59 106 Cow storage cow = cows[cowHash]; 60 107 if (bytes(cow.wrappedDID).length == 0) { 61 - // Not yet on-chain — initial values are authoritative 62 108 return (_controller, _wrappedDID); 63 109 } 64 110 return (cow.controller, cow.wrappedDID); 65 111 } 66 112 67 - // You don't particularly need to call this, you can leave it until you make an update 113 + /// @notice Optionally pre-register a cow on-chain before its first update. 114 + /// @dev This is never strictly necessary — updateWrappedDID, updateController, and 115 + /// deactivate all register the cow automatically if needed. 116 + /// @param _controller The initial controller address. 117 + /// @param _wrappedDID The initial wrapped DID, without the leading "did:" prefix. 68 118 function initializeCow(address _controller, string memory _wrappedDID) external { 69 119 _ensureCowInitialized(_controller, _wrappedDID); 70 120 } 71 121 122 + /// @notice Update the wrapped DID, registering the cow on-chain if not already present. 123 + /// @param _controller The initial controller address from the did:cow identifier. 124 + /// @param _wrappedDID The initial wrapped DID from the did:cow identifier, without "did:". 125 + /// @param _newWrappedDID The new wrapped DID, without the leading "did:" prefix. 72 126 function updateWrappedDID(address _controller, string memory _wrappedDID, string memory _newWrappedDID) external { 73 127 bytes32 cowHash = _ensureCowInitialized(_controller, _wrappedDID); 74 128 updateWrappedDIDByHash(cowHash, _newWrappedDID); 75 129 } 76 130 131 + /// @notice Transfer control to a new address, registering the cow on-chain if not already present. 132 + /// @param _controller The initial controller address from the did:cow identifier. 133 + /// @param _wrappedDID The initial wrapped DID from the did:cow identifier, without "did:". 134 + /// @param _newController The new controller address. 77 135 function updateController(address _controller, string memory _wrappedDID, address _newController) external { 78 136 bytes32 cowHash = _ensureCowInitialized(_controller, _wrappedDID); 79 137 updateControllerByHash(cowHash, _newController); 80 138 } 81 139 140 + /// @notice Permanently deactivate a cow, registering it on-chain if not already present. 141 + /// @dev Deactivation is irreversible. 142 + /// @param _controller The initial controller address from the did:cow identifier. 143 + /// @param _wrappedDID The initial wrapped DID from the did:cow identifier, without "did:". 82 144 function deactivate(address _controller, string memory _wrappedDID) external { 83 145 bytes32 cowHash = _ensureCowInitialized(_controller, _wrappedDID); 84 146 deactivateByHash(cowHash);