Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from hyperledger:main #181

Merged
75 commits merged into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
84903a8
feat: add protocol skeleton for did-rotate
dbluhm Sep 12, 2023
85470cd
fix: keys for problem report out of alignment with spec
dbluhm Sep 14, 2023
533222d
feat: add did rotate messages
dbluhm Sep 14, 2023
5fe679d
feat: stub handlers for did rotate
dbluhm Sep 15, 2023
3aaf38d
refactor: rename to record_keys_for_resolvable_did
dbluhm Sep 15, 2023
9bd7a70
feat: add manager (stubs) and models
dbluhm Sep 15, 2023
360189e
feat: finish off rotate manager implementation
dbluhm Sep 15, 2023
087b7b9
fix: noticed_time vs time_noticed in schema
dbluhm Sep 15, 2023
e25d082
fix: mock references to renamed method
dbluhm Sep 15, 2023
729f0a4
Merge branch 'main' into feature/did-rotate
amanji Feb 22, 2024
c53d5ed
Fix connection reuse handling in demo
ianco Feb 23, 2024
3223096
A couple of updates
ianco Feb 27, 2024
11d2245
feat: add routes with tests
amanji Feb 27, 2024
04e93a2
Add did:peer support for OOB create_invitation()
ianco Feb 27, 2024
74f1744
feat: add message handlers with test
amanji Feb 27, 2024
4a2c5f2
Merge remote-tracking branch 'upstream/main' into feature/did-rotate
amanji Feb 27, 2024
3ef1d56
fix: test failures
amanji Feb 27, 2024
2a3de73
fix: test failures
amanji Feb 27, 2024
de93c71
Merge branch 'wallet_create_did_peer' into issue/2703
ianco Feb 28, 2024
17c6207
Demo options for multi-use
ianco Feb 28, 2024
7743c20
Big fixes generating did for invite
ianco Feb 28, 2024
9073602
Allow did:peer in invitations
ianco Feb 29, 2024
99caee7
Add some connection test scenarios
ianco Feb 29, 2024
726ff1c
Merge branch 'main' into feature/did-rotate
swcurran Feb 29, 2024
a328954
Reuse peer2 and peer4 dids in invitations (WIP)
ianco Mar 1, 2024
7508685
Merge remote-tracking branch 'upstream/main' into issue/2703
ianco Mar 1, 2024
d7bef00
GHA update for doc publishing, fix doc file that was blanked
swcurran Feb 29, 2024
1cc41fd
Merge branch 'main' into feature/did-rotate
amanji Mar 1, 2024
00fa718
Merge branch 'main' into feature/did-rotate
swcurran Mar 4, 2024
42db88e
Merge remote-tracking branch 'upstream/main' into issue/2703
ianco Mar 4, 2024
db05f1e
Merge branch 'main' into feature/did-rotate
amanji Mar 4, 2024
1ae7114
Fix connection reuse with did:peer WIP
ianco Mar 4, 2024
9c323da
feat: add tests for did-rotate messages
amanji Mar 5, 2024
1d13b7b
chore: update package dependencies
amanji Mar 5, 2024
2d32f05
feat: add addional reportable errors
amanji Mar 5, 2024
d432281
feat: delete rotate record once ack is received
amanji Mar 5, 2024
15b6895
Clean up debugging code
ianco Mar 5, 2024
e847bf2
Clean up unit tests and docs
ianco Mar 5, 2024
c67d680
Merge branch 'main' into issue/2703
ianco Mar 5, 2024
3e718b6
Merge branch 'main' into issue/2703
ianco Mar 6, 2024
94f72ba
Couple of bug fixes
ianco Mar 7, 2024
7cfc9b0
Merge branch 'issue/2703' of https://github.com/ianco/aries-cloudagen…
ianco Mar 7, 2024
5a17be7
Formatting isse
ianco Mar 8, 2024
bfc610c
Some did:peer:4 fixes to work with connection reuse
ianco Mar 11, 2024
31e67b9
Merge branch 'main' into issue/2703
ianco Mar 11, 2024
d5a5907
Merge branch 'main' into feature/did-rotate
dbluhm Mar 12, 2024
03b8e49
feat: create did:peer:2/4 in wallet did endpoint
amanji Mar 13, 2024
9062a9f
chore: fix linter errors
amanji Mar 13, 2024
fa1290b
fix: test errors
amanji Mar 13, 2024
bdb2600
feat: tests for did rotate manager
amanji Mar 14, 2024
ac6ee8e
Remove requirement for write ledger in read-only mode.
esune Mar 14, 2024
a8913ff
Merge branch 'main' into fix/read-only-ledger-tweaks
jamshale Mar 14, 2024
ec5ea8d
Merge branch 'main' into issue/2703
ianco Mar 14, 2024
c3c58e5
Tweak integration tests for connection reuse
ianco Mar 14, 2024
7856a5e
Fix docs
ianco Mar 14, 2024
3eb0bf7
Merge pull request #2816 from petridishdev/feature/did-rotate
dbluhm Mar 15, 2024
f4fce81
Merge branch 'main' into fix/read-only-ledger-tweaks
esune Mar 15, 2024
74d12db
Merge pull request #2836 from esune/fix/read-only-ledger-tweaks
swcurran Mar 15, 2024
c8308ed
Update from main branch
ianco Mar 18, 2024
7056727
Update to run_demo script to support Apple M1 CPUs
swcurran Mar 18, 2024
e357480
Add documentation to the Demo readme
swcurran Mar 18, 2024
ca7efb6
chore(deps): Bump the all-actions group with 1 update
dependabot[bot] Mar 18, 2024
1da1947
Merge pull request #2844 from hyperledger/dependabot/github_actions/a…
WadeBarnes Mar 19, 2024
915b60e
Merge branch 'main' into m1-build-issue
swcurran Mar 19, 2024
04fddc9
Merge remote-tracking branch 'upstream/main' into issue/2703
ianco Mar 19, 2024
22ecfcc
Minor fix
ianco Mar 19, 2024
3066c40
Merge pull request #2823 from ianco/issue/2703
ianco Mar 19, 2024
795b0d3
Add 'resolution' functionality for building and running agents seprately
sarthakvijayvergiya Mar 19, 2024
f6c00b0
update docs
sarthakvijayvergiya Mar 19, 2024
5b32130
update docs
sarthakvijayvergiya Mar 19, 2024
9da989c
Merge pull request #2845 from sarthakvijayvergiya/feat/docker-volume-…
ianco Mar 19, 2024
997a20f
Merge branch 'main' into m1-build-issue
swcurran Mar 19, 2024
5501672
Merge pull request #2843 from swcurran/m1-build-issue
swcurran Mar 20, 2024
a08b661
chore(deps-dev): Bump black from 24.1.1 to 24.3.0
dependabot[bot] Mar 20, 2024
9059e87
Merge pull request #2847 from hyperledger/dependabot/pip/black-24.3.0
swcurran Mar 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/blackformat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
- name: Black Code Formatter Check
# The version of black should be adjusted at the same time dev
# dependencies are updated.
uses: psf/black@24.2.0
uses: psf/black@24.3.0
20 changes: 10 additions & 10 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,6 @@ def add_arguments(self, parser: ArgumentParser):
env_var="ACAPY_PROFILE_ENDPOINT",
help="Specifies the profile endpoint for the (public) DID.",
)
parser.add_argument(
"--read-only-ledger",
action="store_true",
env_var="ACAPY_READ_ONLY_LEDGER",
help="Sets ledger to read-only to prevent updates. Default: false.",
)
parser.add_argument(
"--universal-resolver",
type=str,
Expand Down Expand Up @@ -691,9 +685,6 @@ def get_settings(self, args: Namespace) -> dict:
if args.profile_endpoint:
settings["profile_endpoint"] = args.profile_endpoint

if args.read_only_ledger:
settings["read_only_ledger"] = True

if args.universal_resolver_regex and not args.universal_resolver:
raise ArgsParseError(
"--universal-resolver-regex cannot be used without --universal-resolver"
Expand Down Expand Up @@ -855,6 +846,12 @@ def add_arguments(self, parser: ArgumentParser):
"specified ledger or genesis configurations. Default: false."
),
)
parser.add_argument(
"--read-only-ledger",
action="store_true",
env_var="ACAPY_READ_ONLY_LEDGER",
help="Sets ledger to read-only to prevent updates. Default: false.",
)
parser.add_argument(
"--ledger-keepalive",
default=5,
Expand Down Expand Up @@ -912,6 +909,9 @@ def get_settings(self, args: Namespace) -> dict:
multi_configured = False
update_pool_name = False
write_ledger_specified = False

if args.read_only_ledger:
settings["read_only_ledger"] = True
if args.genesis_url:
settings["ledger.genesis_url"] = args.genesis_url
single_configured = True
Expand Down Expand Up @@ -940,7 +940,7 @@ def get_settings(self, args: Namespace) -> dict:
txn_config["pool_name"] = txn_config["id"]
update_pool_name = True
ledger_config_list.append(txn_config)
if not write_ledger_specified:
if not write_ledger_specified and not args.read_only_ledger:
raise ArgsParseError(
"No write ledger genesis provided in multi-ledger config"
)
Expand Down
12 changes: 8 additions & 4 deletions aries_cloudagent/config/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,14 @@ async def load_multiple_genesis_transactions_from_config(settings: Settings):
if "endorser_did" in config:
config_item["endorser_did"] = config.get("endorser_did")
ledger_txns_list.append(config_item)
if not write_ledger_set and not (
settings.get("ledger.genesis_transactions")
or settings.get("ledger.genesis_file")
or settings.get("ledger.genesis_url")
if (
not write_ledger_set
and not settings.get("ledger.read_only")
and not (
settings.get("ledger.genesis_transactions")
or settings.get("ledger.genesis_file")
or settings.get("ledger.genesis_url")
)
):
raise ConfigError(
"No is_write ledger set and no genesis_url,"
Expand Down
74 changes: 66 additions & 8 deletions aries_cloudagent/connections/base_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import json
import logging
from typing import List, Optional, Sequence, Text, Tuple, Union
from typing import Dict, List, Optional, Sequence, Text, Tuple, Union

import pydid
from base58 import b58decode
Expand Down Expand Up @@ -52,7 +52,7 @@
from ..utils.multiformats import multibase, multicodec
from ..wallet.base import BaseWallet
from ..wallet.crypto import create_keypair, seed_to_did
from ..wallet.did_info import DIDInfo, KeyInfo
from ..wallet.did_info import DIDInfo, KeyInfo, INVITATION_REUSE_KEY
from ..wallet.did_method import PEER2, PEER4, SOV
from ..wallet.error import WalletNotFoundError
from ..wallet.key_type import ED25519
Expand Down Expand Up @@ -89,6 +89,12 @@ def _key_info_to_multikey(key_info: KeyInfo) -> str:
multicodec.wrap("ed25519-pub", b58decode(key_info.verkey)), "base58btc"
)

def long_did_peer_to_short(self, long_did: str) -> DIDInfo:
"""Convert did:peer:4 long format to short format and return."""

short_did_peer = long_to_short(long_did)
return short_did_peer

async def long_did_peer_4_to_short(self, long_dp4: str) -> DIDInfo:
"""Convert did:peer:4 long format to short format and store in wallet."""

Expand All @@ -113,6 +119,7 @@ async def create_did_peer_4(
self,
svc_endpoints: Optional[Sequence[str]] = None,
mediation_records: Optional[List[MediationRecord]] = None,
metadata: Optional[Dict] = None,
) -> DIDInfo:
"""Create a did:peer:4 DID for a connection.

Expand Down Expand Up @@ -159,8 +166,13 @@ async def create_did_peer_4(
)
did = encode(input_doc)

did_metadata = metadata if metadata else {}
did_info = DIDInfo(
did=did, method=PEER4, verkey=key.verkey, metadata={}, key_type=ED25519
did=did,
method=PEER4,
verkey=key.verkey,
metadata=did_metadata,
key_type=ED25519,
)
await wallet.store_did(did_info)

Expand All @@ -170,6 +182,7 @@ async def create_did_peer_2(
self,
svc_endpoints: Optional[Sequence[str]] = None,
mediation_records: Optional[List[MediationRecord]] = None,
metadata: Optional[Dict] = None,
) -> DIDInfo:
"""Create a did:peer:2 DID for a connection.

Expand Down Expand Up @@ -215,13 +228,39 @@ async def create_did_peer_2(
[KeySpec.verification(self._key_info_to_multikey(key))], services
)

did_metadata = metadata if metadata else {}
did_info = DIDInfo(
did=did, method=PEER2, verkey=key.verkey, metadata={}, key_type=ED25519
did=did,
method=PEER2,
verkey=key.verkey,
metadata=did_metadata,
key_type=ED25519,
)
await wallet.store_did(did_info)

return did_info

async def fetch_invitation_reuse_did(
self,
did_method: str,
) -> DIDDoc:
"""Fetch a DID from the wallet to use across multiple invitations.

Args:
did_method: The DID method used (e.g. PEER2 or PEER4)

Returns:
The `DIDDoc` instance, or "None" if no DID is found
"""
did_info = None
async with self._profile.session() as session:
wallet = session.inject(BaseWallet)
did_list = await wallet.get_local_dids()
for did in did_list:
if did.method == did_method and INVITATION_REUSE_KEY in did.metadata:
return did
return did_info

async def create_did_document(
self,
did_info: DIDInfo,
Expand Down Expand Up @@ -314,7 +353,7 @@ async def store_did_document(self, value: Union[DIDDoc, dict]):
await storage.update_record(record, doc, {"did": did})

await self.remove_keys_for_did(did)
await self.record_did(did)
await self.record_keys_for_resolvable_did(did)

async def add_key_for_did(self, did: str, key: str):
"""Store a verkey for lookup against a DID.
Expand Down Expand Up @@ -346,7 +385,10 @@ async def find_did_for_key(self, key: str) -> str:
async with self._profile.session() as session:
storage: BaseStorage = session.inject(BaseStorage)
record = await storage.find_record(self.RECORD_TYPE_DID_KEY, {"key": key})
return record.tags["did"]
ret_did = record.tags["did"]
if ret_did.startswith("did:peer:4"):
ret_did = self.long_did_peer_to_short(ret_did)
return ret_did

async def remove_keys_for_did(self, did: str):
"""Remove all keys associated with a DID.
Expand Down Expand Up @@ -441,8 +483,8 @@ async def resolve_invitation(
[self._extract_key_material_in_base58_format(key) for key in routing_keys],
)

async def record_did(self, did: str):
"""Record DID for later use.
async def record_keys_for_resolvable_did(self, did: str):
"""Record the keys for a public DID.

This is required to correlate sender verkeys back to a connection.
"""
Expand Down Expand Up @@ -739,6 +781,21 @@ async def get_connection_targets(
targets = await self.fetch_connection_targets(connection)
return targets

async def clear_connection_targets_cache(self, connection_id: str):
"""Clear the connection targets cache for a given connection ID.

Historically, connections have not been updatable after the protocol
completes. However, with DID Rotation, we need to be able to update
the connection targets and clear the cache of targets.
"""
# TODO it would be better to include the DIDs of the connection in the
# target cache key This solution only works when using whole cluster
# caching or have only a single instance with local caching
cache = self._profile.inject_or(BaseCache)
if cache:
cache_key = f"connection_target::{connection_id}"
await cache.clear(cache_key)

def diddoc_connection_targets(
self,
doc: Optional[Union[DIDDoc, dict]],
Expand Down Expand Up @@ -959,6 +1016,7 @@ async def get_endpoints(self, conn_id: str) -> Tuple[Optional[str], Optional[str
connection = await ConnRecord.retrieve_by_id(session, conn_id)
wallet = session.inject(BaseWallet)
my_did_info = await wallet.get_local_did(connection.my_did)

my_endpoint = my_did_info.metadata.get(
"endpoint",
self._profile.settings.get("default_endpoint"),
Expand Down
9 changes: 6 additions & 3 deletions aries_cloudagent/connections/models/conn_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,16 @@ async def retrieve_by_invitation_msg_id(
async def find_existing_connection(
cls, session: ProfileSession, their_public_did: str
) -> Optional["ConnRecord"]:
"""Retrieve existing active connection records (public did).
"""Retrieve existing active connection records (public did or did:peer).

Args:
session: The active profile session
their_public_did: Inviter public DID
their_public_did: Inviter public DID (or did:peer)
"""
tag_filter = {"their_public_did": their_public_did}
if their_public_did.startswith("did:peer"):
tag_filter = {"their_did": their_public_did}
else:
tag_filter = {"their_public_did": their_public_did}
conn_records = await cls.query(
session,
tag_filter=tag_filter,
Expand Down
8 changes: 4 additions & 4 deletions aries_cloudagent/connections/tests/test_base_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ async def test_resolve_connection_targets_x_unsupported_key(self):
await self.manager.resolve_connection_targets(did)
assert "not supported" in str(cm.exception)

async def test_record_did_empty(self):
async def test_record_keys_for_resolvable_did_empty(self):
did = "did:sov:" + self.test_did
service_builder = ServiceBuilder(DID(did))
service_builder.add_didcomm(
Expand All @@ -1083,9 +1083,9 @@ async def test_record_did_empty(self):
self.manager.resolve_didcomm_services = mock.CoroutineMock(
return_value=(DIDDocument(id=DID(did)), service_builder.services)
)
await self.manager.record_did(did)
await self.manager.record_keys_for_resolvable_did(did)

async def test_record_did(self):
async def test_record_keys_for_resolvable_did(self):
did = "did:sov:" + self.test_did
doc_builder = DIDDocumentBuilder(did)
vm = doc_builder.verification_method.add(
Expand All @@ -1099,7 +1099,7 @@ async def test_record_did(self):
self.manager.resolve_didcomm_services = mock.CoroutineMock(
return_value=(doc, doc.service)
)
await self.manager.record_did(did)
await self.manager.record_keys_for_resolvable_did(did)

async def test_diddoc_connection_targets_diddoc_underspecified(self):
with self.assertRaises(BaseConnectionManagerError):
Expand Down
3 changes: 3 additions & 0 deletions aries_cloudagent/messaging/valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,9 @@ def __init__(
DID_POSTURE_VALIDATE = DIDPosture()
DID_POSTURE_EXAMPLE = DIDPosture.EXAMPLE

DID_WEB_VALIDATE = DIDWeb()
DID_WEB_EXAMPLE = DIDWeb.EXAMPLE

ROUTING_KEY_VALIDATE = RoutingKey()
ROUTING_KEY_EXAMPLE = RoutingKey.EXAMPLE

Expand Down
Empty file.
10 changes: 10 additions & 0 deletions aries_cloudagent/protocols/did_rotate/definition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Version definitions for this protocol."""

versions = [
{
"major_version": 1,
"minimum_minor_version": 0,
"current_minor_version": 0,
"path": "v1_0",
}
]
Empty file.
Empty file.
29 changes: 29 additions & 0 deletions aries_cloudagent/protocols/did_rotate/v1_0/handlers/ack_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Rotate ack handler."""

from .....messaging.base_handler import BaseHandler
from .....messaging.request_context import RequestContext
from .....messaging.responder import BaseResponder
from ..manager import DIDRotateManager
from ..messages.ack import RotateAck


class RotateAckHandler(BaseHandler):
"""Message handler class for rotate ack message."""

async def handle(self, context: RequestContext, responder: BaseResponder):
"""Handle rotate ack message.

Args:
context: request context
responder: responder callback
"""
self._logger.debug("RotateAckHandler called with context %s", context)
assert isinstance(context.message, RotateAck)

connection_record = context.connection_record
ack = context.message

profile = context.profile
did_rotate_mgr = DIDRotateManager(profile)

await did_rotate_mgr.receive_ack(connection_record, ack)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Rotate hangup handler."""

from .....messaging.base_handler import BaseHandler
from .....messaging.request_context import RequestContext
from .....messaging.responder import BaseResponder
from ..manager import DIDRotateManager
from ..messages.hangup import Hangup


class HangupHandler(BaseHandler):
"""Message handler class for rotate message."""

async def handle(self, context: RequestContext, responder: BaseResponder):
"""Handle rotate hangup message.

Args:
context: request context
responder: responder callback
"""
self._logger.debug("HangupHandler called with context %s", context)
assert isinstance(context.message, Hangup)

connection_record = context.connection_record

profile = context.profile
did_rotate_mgr = DIDRotateManager(profile)

await did_rotate_mgr.receive_hangup(connection_record)
Loading