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.

fix update error, add full resolution

+65 -9
+64 -9
cli/cow.py
··· 7 7 from pathlib import Path 8 8 9 9 import click 10 + import requests 10 11 from dotenv import load_dotenv 11 12 from web3 import Web3 12 13 ··· 109 110 110 111 111 112 # --------------------------------------------------------------------------- 113 + # DID document resolution 114 + # --------------------------------------------------------------------------- 115 + 116 + def _resolve_did_doc(wrapped_did_no_prefix): 117 + """ 118 + Fetch the DID document for a wrapped DID (without leading 'did:'). 119 + Supports did:plc and did:web. 120 + Returns the parsed JSON document, or raises ClickException on failure. 121 + """ 122 + full_did = f"did:{wrapped_did_no_prefix}" 123 + 124 + if wrapped_did_no_prefix.startswith("plc:"): 125 + url = f"https://plc.directory/{full_did}" 126 + 127 + elif wrapped_did_no_prefix.startswith("web:"): 128 + # did:web:example.com → https://example.com/.well-known/did.json 129 + # did:web:example.com:path:to → https://example.com/path/to/did.json 130 + host_and_path = wrapped_did_no_prefix[len("web:"):] 131 + parts = host_and_path.split(":") 132 + if len(parts) == 1: 133 + url = f"https://{parts[0]}/.well-known/did.json" 134 + else: 135 + path = "/".join(parts[1:]) 136 + url = f"https://{parts[0]}/{path}/did.json" 137 + 138 + else: 139 + raise click.ClickException( 140 + f"Resolution not supported for did:{wrapped_did_no_prefix} " 141 + f"(only did:plc and did:web are supported)" 142 + ) 143 + 144 + try: 145 + resp = requests.get(url, timeout=10) 146 + resp.raise_for_status() 147 + except requests.exceptions.HTTPError as e: 148 + raise click.ClickException(f"Failed to fetch DID document from {url}: {e}") 149 + except requests.exceptions.RequestException as e: 150 + raise click.ClickException(f"Network error fetching {url}: {e}") 151 + 152 + return url, resp.json() 153 + 154 + 155 + # --------------------------------------------------------------------------- 112 156 # Commands 113 157 # --------------------------------------------------------------------------- 114 158 ··· 124 168 125 169 @cli.command() 126 170 @click.argument("did") 127 - def resolve(did): 128 - """Resolve a did:cow DID to its current state. 171 + @click.option("--no-doc", is_flag=True, help="Skip fetching the wrapped DID document.") 172 + def resolve(did, no_doc): 173 + """Resolve a did:cow DID to its current state and wrapped DID document. 129 174 130 175 \b 131 176 DID format: did:cow:<controller_address>:<method>:<id> ··· 142 187 143 188 controller, wrapped_did = contract.functions.cows(cow_hash).call() 144 189 190 + if wrapped_did == ":": 191 + click.echo("status: deactivated") 192 + return 193 + 145 194 if wrapped_did == "": 146 195 # No on-chain state — initial values from the DID string are authoritative 147 - click.echo(f"status: not yet registered on-chain") 196 + click.echo("status: not yet registered on-chain") 148 197 click.echo(f"wrapped: did:{initial_wrapped} (from DID)") 149 198 click.echo(f"controller: {_controller_address(controller_hex)} (initial)") 150 - elif wrapped_did == ":": 151 - click.echo(f"status: deactivated") 199 + current_wrapped = initial_wrapped 152 200 else: 153 - click.echo(f"status: active") 201 + click.echo("status: active") 154 202 click.echo(f"wrapped: did:{wrapped_did}") 155 203 click.echo(f"controller: {controller}") 204 + current_wrapped = wrapped_did 205 + 206 + if not no_doc: 207 + click.echo("") 208 + url, doc = _resolve_did_doc(current_wrapped) 209 + click.echo(f"resolved from: {url}") 210 + click.echo(json.dumps(doc, indent=2)) 156 211 157 212 158 213 @cli.command("update-wrapped") ··· 176 231 _controller_address(controller_hex), 177 232 initial_wrapped, 178 233 new_wrapped, 179 - ).build_transaction({}) 234 + ).build_transaction({"from": account.address}) 180 235 181 236 _send(w3, account, tx) 182 237 click.echo(f"wrapped: did:{new_wrapped}") ··· 202 257 _controller_address(controller_hex), 203 258 initial_wrapped, 204 259 Web3.to_checksum_address(new_controller), 205 - ).build_transaction({}) 260 + ).build_transaction({"from": account.address}) 206 261 207 262 _send(w3, account, tx) 208 263 click.echo(f"controller: {Web3.to_checksum_address(new_controller)}") ··· 224 279 initial_wrapped, 225 280 ).call() 226 281 227 - tx = contract.functions.deactivate(cow_hash).build_transaction({}) 282 + tx = contract.functions.deactivate(cow_hash).build_transaction({"from": account.address}) 228 283 229 284 _send(w3, account, tx) 230 285 click.echo("deactivated.")
+1
cli/requirements.txt
··· 1 1 web3>=7.0.0 2 2 click>=8.0.0 3 3 python-dotenv>=1.0.0 4 + requests>=2.31.0