diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 485c21f..f21ceb9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,11 +53,18 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Prepare Hiero Solo + id: solo + uses: hiero-ledger/hiero-solo-action@b76850c1ac44466900f8e7412b309c3aa0f539c1 # v0.14 + with: + installMirrorNode: true + - name: Run tests run: poetry run pytest tests --cov --cov-config=pyproject.toml --cov-report=xml env: - OPERATOR_ID: ${{ secrets.OPERATOR_ID }} - OPERATOR_KEY: ${{ secrets.OPERATOR_KEY }} + OPERATOR_ID: ${{ steps.solo.outputs.accountId }} + OPERATOR_KEY: ${{ steps.solo.outputs.privateKey }} + NETWORK: solo - name: Check typing run: poetry run pyright diff --git a/hiero_did_sdk_python/did/did_syntax.py b/hiero_did_sdk_python/did/did_syntax.py index fcb47a1..7bd3149 100644 --- a/hiero_did_sdk_python/did/did_syntax.py +++ b/hiero_did_sdk_python/did/did_syntax.py @@ -8,6 +8,7 @@ HEDERA_NETWORK_MAINNET = "mainnet" HEDERA_NETWORK_TESTNET = "testnet" HEDERA_NETWORK_PREVIEWNET = "previewnet" +HEDERA_NETWORK_SOLO = "solo" HEDERA_DID_METHOD = "hedera" diff --git a/hiero_did_sdk_python/did/utils.py b/hiero_did_sdk_python/did/utils.py index 5ba4884..f70010f 100644 --- a/hiero_did_sdk_python/did/utils.py +++ b/hiero_did_sdk_python/did/utils.py @@ -9,6 +9,7 @@ HEDERA_DID_METHOD, HEDERA_NETWORK_MAINNET, HEDERA_NETWORK_PREVIEWNET, + HEDERA_NETWORK_SOLO, HEDERA_NETWORK_TESTNET, ) @@ -94,6 +95,7 @@ def parse_identifier(identifier: str) -> ParsedIdentifier: network_name != HEDERA_NETWORK_MAINNET and network_name != HEDERA_NETWORK_TESTNET and network_name != HEDERA_NETWORK_PREVIEWNET + and network_name != HEDERA_NETWORK_SOLO ): raise DidException("DID string is invalid. Invalid Hedera network.", DidErrorCode.INVALID_NETWORK) diff --git a/hiero_did_sdk_python/hcs/hcs_message_resolver.py b/hiero_did_sdk_python/hcs/hcs_message_resolver.py index c983ba9..7209408 100644 --- a/hiero_did_sdk_python/hcs/hcs_message_resolver.py +++ b/hiero_did_sdk_python/hcs/hcs_message_resolver.py @@ -10,7 +10,7 @@ from .hcs_message_envelope import HcsMessageEnvelope from .hcs_topic_listener import HcsTopicListener -DEFAULT_TIMEOUT_SECONDS = float(5) +DEFAULT_TIMEOUT_SECONDS = float(20) TOPIC_UNSUBSCRIBED_ERROR = "CANCELLED: unsubscribe" @@ -61,13 +61,16 @@ def handle_error(error: Exception): if self._timestamp_from: self._topic_listener.set_start_time(self._timestamp_from) + if self._timestamp_to: + self._topic_listener.set_end_time(self._timestamp_to) + if self._limit: self._topic_listener.set_limit(self._limit) ( - self._topic_listener.set_end_time(self._timestamp_to or Timestamp(seconds=int(time.time()), nanos=0)) - .set_completion_handler(handle_completion) - .subscribe(client, self._handle_message, handle_error) + self._topic_listener.set_completion_handler(handle_completion).subscribe( + client, self._handle_message, handle_error + ) ) self._last_message_arrival_time = time.time() diff --git a/hiero_did_sdk_python/hcs/hcs_topic_listener.py b/hiero_did_sdk_python/hcs/hcs_topic_listener.py index 6e86df6..77ce092 100644 --- a/hiero_did_sdk_python/hcs/hcs_topic_listener.py +++ b/hiero_did_sdk_python/hcs/hcs_topic_listener.py @@ -28,7 +28,7 @@ def __init__( self._query = ( TopicMessageQuery(topic_id=TopicId.from_string(topic_id), start_time=Timestamp(0, 0).to_date()) .set_max_backoff(2.0) - .set_max_attempts(5) + .set_max_attempts(10) ) def set_start_time(self, start_time: Timestamp): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 6471df6..1615927 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -14,6 +14,8 @@ "You can obtain them by creating developer account on https://portal.hedera.com/" ) +NETWORK: str = os.environ.get("NETWORK") or "testnet" + OPERATOR_KEY = PrivateKey.from_string(OPERATOR_KEY_DER) OPERATOR_KEY_TYPE = get_key_type(OPERATOR_KEY) @@ -21,7 +23,7 @@ @pytest.fixture(scope="class") def client(): - client = Client(network=Network("testnet")) + client = Client(network=Network(NETWORK)) client.set_operator(AccountId.from_string(OPERATOR_ID), private_key=OPERATOR_KEY) yield client diff --git a/tests/integration/demo.py b/tests/integration/demo.py index 7dd8af0..9d57d11 100644 --- a/tests/integration/demo.py +++ b/tests/integration/demo.py @@ -93,7 +93,7 @@ async def test_demo(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) # Create Hedera DID resolver instance did_resolver = HederaDidResolver(client) @@ -135,7 +135,7 @@ async def test_demo(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) print("Resolving schema object...") @@ -174,7 +174,7 @@ async def test_demo(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) print("Resolving credential definition object...") @@ -224,7 +224,7 @@ async def test_demo(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) print("Resolving revocation registry definition object...") @@ -257,7 +257,7 @@ async def test_demo(self, client: Client): rev_list_timestamp = int(time.time()) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) print( f"Revocation list has been registered, initial revocation entry can be found in HCS topic: https://hashscan.io/testnet/topic/{rev_reg_entries_topic_id}" @@ -281,7 +281,7 @@ async def test_demo(self, client: Client): assert rev_list_update_result.revocation_list_state.state == "finished" # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) print("Resolving revocation list states...") diff --git a/tests/integration/test_hcs_file_service.py b/tests/integration/test_hcs_file_service.py index 0779610..e66fb2b 100644 --- a/tests/integration/test_hcs_file_service.py +++ b/tests/integration/test_hcs_file_service.py @@ -10,21 +10,30 @@ from .conftest import OPERATOR_KEY_DER -@pytest.mark.flaky(retries=3, delay=1) +# @pytest.mark.flaky(retries=3, delay=1) @pytest.mark.asyncio(loop_scope="session") class TestHcsFileService: @pytest.mark.parametrize( - "test_file_path, expected_chunks_count", - [("./tests/test_data/test_file.txt", 1), ("./tests/test_data/test_file_large.txt", 6)], + "test_file_path, expected_chunks_count, expected_hash", + [ + ("./tests/test_data/test_file.txt", 1, "48457c7f847ac24fc2419106eae4bb62cf90e25cfb4de0e334fb57df4f7aa4c4"), + ( + "./tests/test_data/test_file_large.txt", + 6, + "96ec5e6330a0850610a81b24b103b1b61ccf38884f5453427229c27e818f42ef", + ), + ], ) - async def test_submit_file(self, test_file_path: str, expected_chunks_count: int, client: Client): + async def test_submit_and_resolve_file( + self, test_file_path: str, expected_chunks_count: int, expected_hash: str, client: Client + ): service = HcsFileService(client) file_payload = Path(test_file_path).read_bytes() topic_id = await service.submit_file(file_payload, OPERATOR_KEY_DER) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) topic_info = await execute_hcs_query_async(TopicInfoQuery(topic_id=TopicId.from_string(topic_id)), client) topic_messages = await HcsMessageResolver(topic_id, HcsFileChunkMessage).execute(client) @@ -32,20 +41,9 @@ async def test_submit_file(self, test_file_path: str, expected_chunks_count: int assert topic_info.memo == f"{sha256(file_payload).hexdigest()}:zstd:base64" assert len(topic_messages) == expected_chunks_count - @pytest.mark.parametrize( - "topic_id, expected_hash", - [ - ("0.0.5123926", "ea4d17b8c0cf44c215ade6b4ad36832672ea3188b1dad12b68c2472dfbcdeff1"), - ("0.0.5123993", "dce9e97491cb7bbaeb6f1af9c236a62f5b6f3f4c07952b5431daa52ec58fbc0b"), - ], - ) - async def test_resolve_file(self, topic_id: str, expected_hash: str, client: Client): - service = HcsFileService(client) - resolved_payload = await service.resolve_file(topic_id) assert resolved_payload - topic_info = await execute_hcs_query_async(TopicInfoQuery(topic_id=TopicId.from_string(topic_id)), client) topic_file_hash, _, _ = topic_info.memo.split(":") assert topic_file_hash == sha256(resolved_payload).hexdigest() diff --git a/tests/integration/test_hcs_topic_service.py b/tests/integration/test_hcs_topic_service.py index eea3aa3..c7f2986 100644 --- a/tests/integration/test_hcs_topic_service.py +++ b/tests/integration/test_hcs_topic_service.py @@ -6,7 +6,7 @@ from .conftest import OPERATOR_KEY -@pytest.mark.flaky(retries=3, delay=1) +# @pytest.mark.flaky(retries=3, delay=1) @pytest.mark.asyncio(loop_scope="session") class TestHcsTopicService: async def test_creates_and_updates_hcs_topic(self, client: Client): diff --git a/tests/integration/test_hedera_anoncreds_registry.py b/tests/integration/test_hedera_anoncreds_registry.py index 2a08d68..f093b4f 100644 --- a/tests/integration/test_hedera_anoncreds_registry.py +++ b/tests/integration/test_hedera_anoncreds_registry.py @@ -32,9 +32,9 @@ from hiero_did_sdk_python.anoncreds.utils import AnonCredsObjectType, parse_anoncreds_identifier from hiero_did_sdk_python.hcs import HcsMessageResolver -from .conftest import OPERATOR_KEY_DER +from .conftest import NETWORK, OPERATOR_KEY_DER -ISSUER_ID = "did:hedera:testnet:zvAQyPeUecGck2EsxcsihxhAB6jZurFrBbj2gC7CNkS5o_0.0.5063027" +ISSUER_ID = f"did:hedera:{NETWORK}:zvAQyPeUecGck2EsxcsihxhAB6jZurFrBbj2gC7CNkS5o_0.0.5063027" MOCK_SCHEMA_PARAMS = { "name": "mock-schema", @@ -69,7 +69,7 @@ ACCUM_2 = "mock-accum-2" -@pytest.mark.flaky(retries=3, delay=1) +# @pytest.mark.flaky(retries=3, delay=1) @pytest.mark.asyncio(loop_scope="session") class TestHederaAnonCredsRegistry: async def test_creates_anoncreds_schema(self, client: Client, Something): @@ -92,7 +92,7 @@ async def test_creates_anoncreds_schema(self, client: Client, Something): assert parsed_identifier.object_type == AnonCredsObjectType.SCHEMA # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) resolution_result = await registry.get_schema(schema_id) @@ -124,7 +124,7 @@ async def test_creates_anoncreds_cred_def(self, client: Client, Something): assert parsed_identifier.object_type == AnonCredsObjectType.PUBLIC_CRED_DEF # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) resolution_result = await registry.get_cred_def(cred_def_id) @@ -159,7 +159,7 @@ async def test_creates_anoncreds_rev_reg_def(self, client: Client, Something): assert parsed_identifier.object_type == AnonCredsObjectType.REV_REG # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) resolution_result = await registry.get_rev_reg_def(rev_reg_def_id) @@ -182,7 +182,7 @@ async def test_creates_and_updates_rev_list(self, client: Client, Something): assert rev_reg_def_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) rev_list = AnonCredsRevList( issuer_id=ISSUER_ID, @@ -205,7 +205,7 @@ async def test_creates_and_updates_rev_list(self, client: Client, Something): assert rev_reg_entries_topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) entries_messages = await HcsMessageResolver(rev_reg_entries_topic_id, HcsRevRegEntryMessage).execute(client) @@ -240,7 +240,7 @@ async def test_creates_and_updates_rev_list(self, client: Client, Something): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) entries_messages = await HcsMessageResolver(rev_reg_entries_topic_id, HcsRevRegEntryMessage).execute(client) diff --git a/tests/integration/test_hedera_did.py b/tests/integration/test_hedera_did.py index 9bf8608..6b548e6 100644 --- a/tests/integration/test_hedera_did.py +++ b/tests/integration/test_hedera_did.py @@ -11,17 +11,17 @@ from hiero_did_sdk_python.utils.encoding import b58_to_bytes, bytes_to_b58, multibase_encode from hiero_did_sdk_python.utils.keys import get_key_type -from .conftest import OPERATOR_KEY, OPERATOR_KEY_DER, OPERATOR_KEY_TYPE +from .conftest import NETWORK, OPERATOR_KEY, OPERATOR_KEY_DER, OPERATOR_KEY_TYPE TOPIC_REGEX = re.compile(r"^0\.0\.[0-9]{3,}") -VALID_DID = "did:hedera:testnet:z6MkgUv5CvjRP6AsvEYqSRN7djB6p4zK9bcMQ93g5yK6Td7N_0.0.29613327" +VALID_DID = f"did:hedera:{NETWORK}:z6MkgUv5CvjRP6AsvEYqSRN7djB6p4zK9bcMQ93g5yK6Td7N_0.0.29613327" VERIFICATION_PUBLIC_KEY_BASE58 = "87meAWt7t2zrDxo7qw3PVTjexKWReYWS75LH29THy8kb" VERIFICATION_PUBLIC_KEY = PublicKey.from_bytes(b58_to_bytes(VERIFICATION_PUBLIC_KEY_BASE58)) VERIFICATION_PUBLIC_KEY_DER = VERIFICATION_PUBLIC_KEY.to_string_raw() VERIFICATION_PUBLIC_KEY_TYPE = "Ed25519VerificationKey2018" -VERIFICATION_ID = f"did:hedera:testnet:z{VERIFICATION_PUBLIC_KEY_BASE58}_0.0.29617801#key-1" +VERIFICATION_ID = f"did:hedera:{NETWORK}:z{VERIFICATION_PUBLIC_KEY_BASE58}_0.0.29617801#key-1" async def create_and_register_new_did(client: Client, private_key_der=OPERATOR_KEY_DER): @@ -34,7 +34,7 @@ async def resolve_did_topic_messages(topic_id: str, client: Client): return await HcsMessageResolver(topic_id, HcsDidMessageEnvelope).execute(client) -@pytest.mark.flaky(retries=3, delay=1) +# @pytest.mark.flaky(retries=3, delay=1) @pytest.mark.asyncio(loop_scope="session") class TestHederaDid: class TestRegister: @@ -43,7 +43,7 @@ async def test_throws_if_already_registered(self, client: Client): did = await create_and_register_new_did(client) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) with pytest.raises(DidException, match="DID is already registered"): await did.register() @@ -60,15 +60,15 @@ async def test_creates_new_did(self, client: Client): did = await create_and_register_new_did(client) assert did.topic_id - expected_identifier = f"did:hedera:testnet:{multibase_encode(bytes(OPERATOR_KEY.public_key().to_bytes_raw()), "base58btc")}_{did.topic_id}" + expected_identifier = f"did:hedera:{NETWORK}:{multibase_encode(bytes(OPERATOR_KEY.public_key().to_bytes_raw()), "base58btc")}_{did.topic_id}" assert bool(TOPIC_REGEX.match(did.topic_id)) assert did.identifier == expected_identifier assert cast(PrivateKey, did._private_key).to_string() == OPERATOR_KEY.to_string() - assert did.network == "testnet" + assert did.network == NETWORK # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_topic_messages = await resolve_did_topic_messages(did.topic_id, client) assert len(did_topic_messages) == 1 @@ -81,7 +81,7 @@ async def test_recreates_deleted_did_with_same_topic(self, client: Client): original_topic_id = did.topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_topic_messages = await resolve_did_topic_messages(did.topic_id, client) assert len(did_topic_messages) == 1 @@ -90,7 +90,7 @@ async def test_recreates_deleted_did_with_same_topic(self, client: Client): assert did.topic_id == original_topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_topic_messages = await resolve_did_topic_messages(did.topic_id, client) assert len(did_topic_messages) == 2 @@ -99,7 +99,7 @@ async def test_recreates_deleted_did_with_same_topic(self, client: Client): assert did.topic_id == original_topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_topic_messages = await resolve_did_topic_messages(did.topic_id, client) assert len(did_topic_messages) == 2 @@ -117,7 +117,7 @@ async def test_successfully_resolves_registered(self, client: Client): did = await create_and_register_new_did(client) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -158,7 +158,7 @@ async def test_deletes_did_document(self, client: Client): assert did.topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -181,7 +181,7 @@ async def test_deletes_did_document(self, client: Client): await did.delete() # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -198,7 +198,7 @@ async def test_deletes_did_document(self, client: Client): assert len(did_topic_messages) == 2 class TestChangeOwner: - NEW_OWNER_ID = "did:hedera:testnet:z6MkgUv5CvjRP6AsvEYqSRN7djB6p4zK9bcMQ93g5yK6Td7N_0.0.99999999" + NEW_OWNER_ID = f"did:hedera:{NETWORK}:z6MkgUv5CvjRP6AsvEYqSRN7djB6p4zK9bcMQ93g5yK6Td7N_0.0.99999999" async def test_throws_error_if_not_registered(self, client: Client): """Throws error if DID is not registered""" @@ -223,14 +223,14 @@ async def test_changes_document_owner(self, client: Client): assert did.topic_id # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) new_private_key = PrivateKey.generate_ed25519() new_private_key_type = get_key_type(new_private_key) await did.change_owner(controller=self.NEW_OWNER_ID, new_private_key_der=new_private_key.to_string()) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -326,7 +326,7 @@ async def test_adds_new_service(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -373,7 +373,7 @@ async def test_updates_existing_service(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -416,7 +416,7 @@ async def test_revokes_existing_service(self, client: Client): await did.revoke_service(id_=f"{did.identifier}#service-1") # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -459,7 +459,7 @@ async def test_recreates_service(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -573,7 +573,7 @@ async def test_adds_new_verification_method(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -626,7 +626,7 @@ async def test_updates_existing_verification_method(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -670,7 +670,7 @@ async def test_revokes_existing_verification_method(self, client: Client): await did.revoke_verification_method(id_=VERIFICATION_ID) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -786,7 +786,7 @@ async def test_adds_new_verification_relationship(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -841,7 +841,7 @@ async def test_updates_existing_verification_relationship(self, client: Client): ) # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() @@ -886,7 +886,7 @@ async def test_revokes_existing_verification_relationship(self, client: Client): await did.revoke_verification_relationship(id_=VERIFICATION_ID, relationship_type="keyAgreement") # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) did_document = await did.resolve() diff --git a/tests/integration/test_hedera_did_resolver.py b/tests/integration/test_hedera_did_resolver.py index 65345b6..cca373d 100644 --- a/tests/integration/test_hedera_did_resolver.py +++ b/tests/integration/test_hedera_did_resolver.py @@ -9,7 +9,7 @@ from .conftest import OPERATOR_KEY, OPERATOR_KEY_DER, OPERATOR_KEY_TYPE -@pytest.mark.flaky(retries=3, delay=1) +# @pytest.mark.flaky(retries=3, delay=1) @pytest.mark.asyncio(loop_scope="session") class TestHederaDidResolver: async def test_returns_error_response(self, client: Client): @@ -60,7 +60,7 @@ async def test_returns_success_response(self, client: Client, Something): assert did.identifier # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) result = await HederaDidResolver(client).resolve(did.identifier) @@ -106,7 +106,7 @@ async def test_returns_deactivated_document(self, client: Client): assert did.identifier # Wait until changes are propagated to Hedera Mirror node - await asyncio.sleep(5) + await asyncio.sleep(10) result = await HederaDidResolver(client).resolve(did.identifier)