Skip to content

Commit

Permalink
Merge branch 'main' into pydoclint
Browse files Browse the repository at this point in the history
  • Loading branch information
mvadari authored Jan 2, 2025
2 parents 455b60d + 8c6b489 commit 91c4223
Show file tree
Hide file tree
Showing 37 changed files with 1,472 additions and 131 deletions.
5 changes: 4 additions & 1 deletion .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,11 @@ PriceOracle
fixEmptyDID
fixXChainRewardRounding
fixPreviousTxnID
# 2.3.0-rc1 Amendments
# 2.3.0 Amendments
fixAMMv1_1
fixAMMv1_2
AMMClawback
InvariantsV1_1
Credentials
NFTokenMintOffer
MPTokensV1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Integration test

env:
POETRY_VERSION: 1.8.3
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.3.0-rc1
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.3.0

on:
push:
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [[Unreleased]]

## [4.0.0] - 2024-12-23

### Added
- Support for the Multi-Purpose Tokens (MPT) amendment (XLS-33)
- Add `include_deleted` to ledger_entry request
- Add support for XLS-70d (Credentials)

### BREAKING CHANGE:
- Remove Python 3.7 support to fix dependency installation and use 3.8 as new default.
Expand All @@ -28,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed
- Allow empty strings for the purpose of removing fields in DIDSet transaction
- Use `NetworkID` in faucet processing to produce a non-ambiguous URL for faucet hosts.

### Removed
- Remove deprecated `full`, `accounts`, and `type` parameters from ledger request model
Expand Down Expand Up @@ -92,7 +96,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.0.0] - 2023-07-05
### BREAKING CHANGE
- The default signing algorithm in the `Wallet` was changed from secp256k1 to ed25519
-
### Added:
- Wallet support for regular key compatibility
- Added new ways of wallet generation: `from_seed`, `from_secret`, `from_entropy`, `from_secret_numbers`
Expand Down
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,17 @@ poetry run poe test_unit
To run integration tests, you'll need a standalone rippled node running with WS port `6006` and JSON RPC port `5005`. You can run a docker container for this:

```bash
docker run -p 5005:5005 -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/ripple/etc/ --platform linux/amd64 rippleci/rippled:2.2.0-b3 /opt/ripple/bin/rippled -a --conf /opt/ripple/etc/rippled.cfg
docker run -dit -p 5005:5005 -p 6006:6006 --volume $PWD/.ci-config/:/etc/opt/ripple/ --entrypoint bash rippleci/rippled:develop -c 'rippled -a'
```

Breaking down the command:
* `docker run -p 5005:5005 -p 6006:6006` starts a Docker container with an open port for admin JsonRPC and WebSocket requests.
* `--interactive` allows you to interact with the container.
* `-it` allows you to interact with the container.
* `-d` runs the docker container in detached mode. The container will run in the background and developer gets back control of the terminal
* `-t` starts a terminal in the container for you to send commands to.
* `--volume $PWD/.ci-config:/config/` identifies the `rippled.cfg` and `validators.txt` to import. It must be an absolute path, so we use `$PWD` instead of `./`.
* `xrpllabsofficial/xrpld:1.12.0` is an image that is regularly updated with the latest `rippled` releases and can be found here: https://github.com/WietseWind/docker-rippled
* `--volume $PWD/.ci-config:/etc/opt/ripple/` mounts the directories as indicated. It must be an absolute path, so we use `$PWD` instead of `./`. `rippled` software searches the location `/etc/opt/ripple/` (default behavior) for the config files. Hence there is no need to explicitly specify the config-file path.
* `rippleci/rippled:develop` is an image that is regularly updated with the latest build of the `develop` branch of `rippled`.
* `-a` starts `rippled` in standalone mode
* `--start` signals to start `rippled` with the specified amendments in `rippled.cfg` enabled immediately instead of voting for 2 weeks on them.

Then to actually run the tests, run the command:

Expand Down
78 changes: 42 additions & 36 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

122 changes: 122 additions & 0 deletions tests/integration/transactions/test_credential.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
sign_and_reliable_submission_async,
test_async_and_sync,
)
from tests.integration.reusable_values import DESTINATION, WALLET
from xrpl.models import AccountObjects, AccountObjectType
from xrpl.models.requests.ledger_entry import Credential, LedgerEntry
from xrpl.models.response import ResponseStatus
from xrpl.models.transactions.credential_accept import CredentialAccept
from xrpl.models.transactions.credential_create import CredentialCreate
from xrpl.models.transactions.credential_delete import CredentialDelete
from xrpl.utils import str_to_hex

_URI = "www.my-id.com/username"


def is_cred_object_present(
result: dict, issuer: str, subject: str, cred_type: str
) -> bool:
"""
Args:
result: JSON response from account_objects RPC
issuer: Address of the credential issuer
subject: Address of the credential subject
cred_type: Type of the credential
Returns:
bool: True if credential exists, False otherwise
"""

for val in result["account_objects"]:
if (
val["Issuer"] == issuer
and val["Subject"] == subject
and val["CredentialType"] == cred_type
):
return True

return False


class TestCredentials(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_valid(self, client):
# Define entities helpful for Credential lifecycle
_ISSUER = WALLET.address
_SUBJECT = DESTINATION.address
_SUBJECT_WALLET = DESTINATION

# Disambiguate the sync/async, json/websocket tests with different
# credential type values -- this avoids tecDUPLICATE error
# self.value is defined inside the above decorator
cred_type = str_to_hex("Passport_" + str(self.value))

tx = CredentialCreate(
account=_ISSUER,
subject=_SUBJECT,
credential_type=cred_type,
uri=str_to_hex(_URI),
)
response = await sign_and_reliable_submission_async(tx, WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Use the LedgerEntry RPC to validate the creation of the credential object
ledger_entry_response = await client.request(
LedgerEntry(
credential=Credential(
subject=_SUBJECT, issuer=_ISSUER, credential_type=cred_type
)
)
)

self.assertEqual(ledger_entry_response.status, ResponseStatus.SUCCESS)

# Execute the CredentialAccept transaction on the above Credential ledger object
tx = CredentialAccept(
issuer=_ISSUER, account=_SUBJECT, credential_type=cred_type
)
# CredentialAccept transaction is submitted by SUBJECT
response = await sign_and_reliable_submission_async(tx, _SUBJECT_WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Execute the CredentialDelete transaction
# Subject initiates the deletion of the Credential ledger object
tx = CredentialDelete(
issuer=_ISSUER, account=_SUBJECT, credential_type=cred_type
)

response = await sign_and_reliable_submission_async(tx, _SUBJECT_WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# The credential ledger object must be deleted from both the Issuer and Subject
# account's directory pages
account_objects_response = await client.request(
AccountObjects(account=_ISSUER, type=AccountObjectType.CREDENTIAL)
)
self.assertFalse(
is_cred_object_present(
account_objects_response.result,
issuer=_ISSUER,
subject=_SUBJECT,
cred_type=cred_type,
)
)

# Verify that the Credential object has been deleted from the Subject's
# directory page as well
account_objects_response = await client.request(
AccountObjects(account=_SUBJECT, type=AccountObjectType.CREDENTIAL)
)
self.assertFalse(
is_cred_object_present(
account_objects_response.result,
issuer=_ISSUER,
subject=_SUBJECT,
cred_type=cred_type,
)
)
Loading

0 comments on commit 91c4223

Please sign in to comment.