Skip to content

Commit 11ca166

Browse files
authored
Merge pull request #6 from decentralized-identity/feature/input-doc-generation-helper
feat: add input doc generation helper
2 parents 84cb652 + 550adb6 commit 11ca166

File tree

6 files changed

+276
-7
lines changed

6 files changed

+276
-7
lines changed

README.md

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,128 @@ did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M:zQSJgiFTYiCHjQ9MktwNTh
2424
>>> from did_peer_4 import resolve
2525
>>> document = resolve(did)
2626
>>> print(document)
27-
{'hello': 'world', 'alsoKnownAs': ['did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M'], 'id': 'did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M:zQSJgiFTYiCHjQ9MktwNThRXM7a'}
27+
{'hello': 'world', 'id': 'did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M:zQSJgiFTYiCHjQ9MktwNThRXM7a', 'alsoKnownAs': ['did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M']}
2828
>>> from did_peer_4 import resolve_short
2929
>>> short_document = resolve_short(did)
3030
>>> print(short_document)
31-
{'hello': 'world', 'alsoKnownAs': ['did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M:zQSJgiFTYiCHjQ9MktwNThRXM7a'], 'id': 'did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M'}
31+
{'hello': 'world', 'id': 'did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M', 'alsoKnownAs': ['did:peer:4zQmb7xLdVY9TXx8oov5XgpGUmGELgqiAV2699s43i6Qdm3M:zQSJgiFTYiCHjQ9MktwNThRXM7a']}
32+
33+
```
34+
35+
### With Input Document generation helper
36+
37+
```python
38+
>>> import json
39+
>>> from did_peer_4 import encode, resolve
40+
>>> from did_peer_4.input_doc import input_doc_from_keys_and_services, KeySpec
41+
>>> key1 = KeySpec(
42+
... multikey="z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
43+
... relationships=["authentication", "capabilityDelegation"],
44+
... )
45+
>>> key2 = KeySpec(
46+
... multikey="z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc",
47+
... relationships=["keyAgreement"],
48+
... )
49+
>>> service = {
50+
... "id": "#didcomm-0",
51+
... "type": "DIDCommMessaging",
52+
... "serviceEndpoint": {
53+
... "uri": "didcomm:transport/queue",
54+
... "accept": ["didcomm/v2"]
55+
... }
56+
... }
57+
>>> input_doc = input_doc_from_keys_and_services([key1, key2], [service])
58+
>>> print(json.dumps(input_doc, indent=2))
59+
{
60+
"@context": [
61+
"https://www.w3.org/ns/did/v1",
62+
"https://w3id.org/security/multikey/v1"
63+
],
64+
"verificationMethod": [
65+
{
66+
"id": "#key-0",
67+
"type": "Multikey",
68+
"publicKeyMultibase": "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"
69+
},
70+
{
71+
"id": "#key-1",
72+
"type": "Multikey",
73+
"publicKeyMultibase": "z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"
74+
}
75+
],
76+
"authentication": [
77+
"#key-0"
78+
],
79+
"capabilityDelegation": [
80+
"#key-0"
81+
],
82+
"service": [
83+
{
84+
"id": "#didcomm-0",
85+
"type": "DIDCommMessaging",
86+
"serviceEndpoint": {
87+
"uri": "didcomm:transport/queue",
88+
"accept": [
89+
"didcomm/v2"
90+
]
91+
}
92+
}
93+
],
94+
"keyAgreement": [
95+
"#key-1"
96+
]
97+
}
98+
>>> did = encode(input_doc)
99+
>>> print(did)
100+
did:peer:4zQmQabhRUiFPAmn7CX6B8V6qmfrs7nQQyFb6zAD7EAWvW3c:zMoDtDfb4quiz6yXy8ftBst291RGXBJaVy8FMivQWVLPbYUSAS68WgeWNxtdR5aBNraMHsPZi4iSWizFpbbZxQ2Cw56HwPxwG3SMa3wCtUkRj1LrAjcC1EE11t7vq1mggN2Y5xHTJpEbCLNnrUHG99RBb7fDEJff2YzCFqKxW4NU6tdjtw5fEy6Kz5f3KzeybV74aZY8QwWFMi3j9brksFsNhhCWyk65tKgaE2b5qyD6tLF5u3rNuEAUGNTTaJ1hPGKgCZaAAm4TdjuaSXoVaSxiXXkWjEsxRnLnqeNbw6djogDw41v2tpEawTQX7ZqL5ZbYzPi6N6L2e9Kkf4i2K2WMVLTW41n6AGmDguJPqrgkpCb71v2WiMGSsQPzk5EyPucdfQmxyn7tj4E21nZGNfY415Sp2XQZZ7yQPAimq3WrYf54srZfVjXvJBrMosCPDdm5bVKitRLjmh8ueQ3Pa8CcT3zHy8RtQRuKTQgKyWLUvMitN1eQEtaRo1vLNLYEX4cXhG51haarXRfEFyCr3FZeE9oBRWkZioCrkZTEL8rz4GAAnpPojxCPXsecE1WJXkcqZw1bS9YwU5gugNybPAFpoc2AhwtcQNvj9UhaZisVvPiEsynRG2cmwyjqi5dD8b6FvwCMUq8FzkpyV2UR8ePMMt3Co8FofVvKCkU4a4CRWF6hWCrEQqpSC5abNscuvpcMUfzvSVNrFVZyCiSMX2Wwa7tnxvujZTjutKXr
101+
>>> document = resolve_short(did)
102+
>>> print(json.dumps(document, indent=2))
103+
{
104+
"@context": [
105+
"https://www.w3.org/ns/did/v1",
106+
"https://w3id.org/security/multikey/v1"
107+
],
108+
"verificationMethod": [
109+
{
110+
"id": "#key-0",
111+
"type": "Multikey",
112+
"publicKeyMultibase": "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
113+
"controller": "did:peer:4zQmQabhRUiFPAmn7CX6B8V6qmfrs7nQQyFb6zAD7EAWvW3c"
114+
},
115+
{
116+
"id": "#key-1",
117+
"type": "Multikey",
118+
"publicKeyMultibase": "z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc",
119+
"controller": "did:peer:4zQmQabhRUiFPAmn7CX6B8V6qmfrs7nQQyFb6zAD7EAWvW3c"
120+
}
121+
],
122+
"authentication": [
123+
"#key-0"
124+
],
125+
"capabilityDelegation": [
126+
"#key-0"
127+
],
128+
"service": [
129+
{
130+
"id": "#didcomm-0",
131+
"type": "DIDCommMessaging",
132+
"serviceEndpoint": {
133+
"uri": "didcomm:transport/queue",
134+
"accept": [
135+
"didcomm/v2"
136+
]
137+
}
138+
}
139+
],
140+
"keyAgreement": [
141+
"#key-1"
142+
],
143+
"id": "did:peer:4zQmQabhRUiFPAmn7CX6B8V6qmfrs7nQQyFb6zAD7EAWvW3c",
144+
"alsoKnownAs": [
145+
"did:peer:4zQmQabhRUiFPAmn7CX6B8V6qmfrs7nQQyFb6zAD7EAWvW3c:zMoDtDfb4quiz6yXy8ftBst291RGXBJaVy8FMivQWVLPbYUSAS68WgeWNxtdR5aBNraMHsPZi4iSWizFpbbZxQ2Cw56HwPxwG3SMa3wCtUkRj1LrAjcC1EE11t7vq1mggN2Y5xHTJpEbCLNnrUHG99RBb7fDEJff2YzCFqKxW4NU6tdjtw5fEy6Kz5f3KzeybV74aZY8QwWFMi3j9brksFsNhhCWyk65tKgaE2b5qyD6tLF5u3rNuEAUGNTTaJ1hPGKgCZaAAm4TdjuaSXoVaSxiXXkWjEsxRnLnqeNbw6djogDw41v2tpEawTQX7ZqL5ZbYzPi6N6L2e9Kkf4i2K2WMVLTW41n6AGmDguJPqrgkpCb71v2WiMGSsQPzk5EyPucdfQmxyn7tj4E21nZGNfY415Sp2XQZZ7yQPAimq3WrYf54srZfVjXvJBrMosCPDdm5bVKitRLjmh8ueQ3Pa8CcT3zHy8RtQRuKTQgKyWLUvMitN1eQEtaRo1vLNLYEX4cXhG51haarXRfEFyCr3FZeE9oBRWkZioCrkZTEL8rz4GAAnpPojxCPXsecE1WJXkcqZw1bS9YwU5gugNybPAFpoc2AhwtcQNvj9UhaZisVvPiEsynRG2cmwyjqi5dD8b6FvwCMUq8FzkpyV2UR8ePMMt3Co8FofVvKCkU4a4CRWF6hWCrEQqpSC5abNscuvpcMUfzvSVNrFVZyCiSMX2Wwa7tnxvujZTjutKXr"
146+
]
147+
}
148+
32149
```
33150

34151
## Tutorial

did_peer_4/input_doc.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Helpers for creating input documents for the DID method."""
2+
from dataclasses import dataclass
3+
from typing import Any, Dict, Literal, Optional, Sequence
4+
5+
RELATIONSHIPS = (
6+
"authentication",
7+
"assertionMethod",
8+
"keyAgreement",
9+
"capabilityDelegation",
10+
"capabilityInvocation",
11+
)
12+
13+
Relationship = Literal[
14+
"authentication",
15+
"assertionMethod",
16+
"keyAgreement",
17+
"capabilityDelegation",
18+
"capabilityInvocation",
19+
]
20+
21+
22+
@dataclass
23+
class KeySpec:
24+
"""Key specification."""
25+
26+
multikey: str
27+
relationships: Sequence[Relationship]
28+
29+
30+
def input_doc_from_keys_and_services(
31+
keys: Sequence[KeySpec], services: Optional[Sequence[dict]] = None
32+
) -> dict:
33+
"""Create an input document for a set of keys and services."""
34+
input_doc: Dict[str, Any] = {
35+
"@context": [
36+
"https://www.w3.org/ns/did/v1",
37+
"https://w3id.org/security/multikey/v1",
38+
]
39+
}
40+
for index, key in enumerate(keys):
41+
ident = f"#key-{index}"
42+
input_doc.setdefault("verificationMethod", []).append(
43+
{
44+
"id": ident,
45+
"type": "Multikey",
46+
"publicKeyMultibase": key.multikey,
47+
}
48+
)
49+
for relationship in key.relationships:
50+
if relationship not in RELATIONSHIPS:
51+
raise ValueError(f"Invalid relationship: {relationship}")
52+
53+
input_doc.setdefault(relationship, []).append(ident)
54+
55+
if services:
56+
input_doc["service"] = services
57+
58+
return input_doc

pdm.lock

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dev = [
2323
"ruff>=0.0.285",
2424
"pre-commit>=3.3.3",
2525
"pytest-cov>=4.1.0",
26+
"pytest-ruff>=0.2.1",
2627
]
2728

2829
[tool.coverage.report]
@@ -33,3 +34,9 @@ exclude_lines = [
3334
precision = 2
3435
skip_covered = true
3536
show_missing = true
37+
38+
[tool.pytest.ini_options]
39+
addopts = """
40+
--cov=did_peer_4 --cov-report=term-missing --cov-branch --cov-fail-under=85 --cov-context=test
41+
--doctest-glob README.md --ruff
42+
"""

tests/test_did_peer_4.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import json
22

3-
from did_peer_4 import decode, encode, long_to_short, resolve, resolve_short
3+
from did_peer_4 import (
4+
decode,
5+
encode,
6+
encode_short,
7+
long_to_short,
8+
resolve,
9+
resolve_short,
10+
resolve_short_from_doc,
11+
)
412

513

614
DOC = {
@@ -41,12 +49,15 @@
4149

4250

4351
def test_encode_decode():
52+
encoded = encode(DOC, validate=False)
4453
encoded = encode(DOC)
4554
print()
4655
print(encoded)
4756
print(long_to_short(encoded))
4857
decoded = decode(encoded)
4958
assert decoded == DOC
59+
assert encode_short(DOC) == long_to_short(encoded)
60+
assert resolve_short_from_doc(DOC, long_to_short(encoded)) == resolve_short(encoded)
5061

5162

5263
def test_resolve():

tests/test_input_doc.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from did_peer_4 import encode
2+
from did_peer_4.input_doc import input_doc_from_keys_and_services, KeySpec
3+
from did_peer_4.valid import validate_input_document
4+
5+
ED25519_MULTIKEY = "z6MkrCD1csqtgdj8sjrsu8jxcbeyP6m7LiK87NzhfWqio5yr"
6+
X25519_MULTIKEY = "z6LSqPZfn9krvgXma2icTMKf2uVcYhKXsudCmPoUzqGYW24U"
7+
8+
9+
def test_input_doc_generation():
10+
input_doc = input_doc_from_keys_and_services(
11+
[
12+
KeySpec(
13+
multikey=ED25519_MULTIKEY,
14+
relationships=["authentication"],
15+
),
16+
KeySpec(
17+
multikey=X25519_MULTIKEY,
18+
relationships=["keyAgreement"],
19+
),
20+
],
21+
[
22+
{
23+
"id": "#didcommmessaging-0",
24+
"type": "DIDCommMessaging",
25+
"serviceEndpoint": {
26+
"uri": "didcomm:transport/queue",
27+
"accept": ["didcomm/v2"],
28+
},
29+
}
30+
],
31+
)
32+
33+
assert input_doc == {
34+
"@context": [
35+
"https://www.w3.org/ns/did/v1",
36+
"https://w3id.org/security/multikey/v1",
37+
],
38+
"verificationMethod": [
39+
{
40+
"id": "#key-0",
41+
"type": "Multikey",
42+
"publicKeyMultibase": ED25519_MULTIKEY,
43+
},
44+
{
45+
"id": "#key-1",
46+
"type": "Multikey",
47+
"publicKeyMultibase": X25519_MULTIKEY,
48+
},
49+
],
50+
"authentication": ["#key-0"],
51+
"keyAgreement": ["#key-1"],
52+
"service": [
53+
{
54+
"id": "#didcommmessaging-0",
55+
"type": "DIDCommMessaging",
56+
"serviceEndpoint": {
57+
"uri": "didcomm:transport/queue",
58+
"accept": ["didcomm/v2"],
59+
},
60+
}
61+
],
62+
}
63+
assert validate_input_document(input_doc)
64+
assert encode(input_doc)

0 commit comments

Comments
 (0)