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

Credentials #759

Merged
merged 120 commits into from
Dec 21, 2024
Merged

Credentials #759

merged 120 commits into from
Dec 21, 2024

Conversation

ckeshava
Copy link
Collaborator

@ckeshava ckeshava commented Oct 17, 2024

High Level Overview of Change

Python Client SDK implementation for https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0070d-credentials

Corresponding c++ changes: https://github.com/XRPLF/rippled/pull/5103/files

Context of Change

Following changes are introduced in this PR:
(Quoting Verbatim from the XLS specification document)

  • Creating a Credential ledger object
  • Creating a CredentialCreate transaction type
  • Creating a CredentialAccept transaction type
  • Creating a CredentialDelete transaction type
  • Modifying the DepositPreauth ledger object
  • Modifying the DepositPreauth transaction
  • Modifying other transactions that are affected by Deposit Authorization -- Payment, PaymentChannelClaim, AccountDelete and EscrowFinish

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update CHANGELOG.md?

  • Yes
  • No, this change does not impact library users

Test Plan

Note: The integration tests will not pass the CI because we don't have an appropriate docker container with the custom branch. At the moment, integration tests are to be run with custom builds of rippled.

… at commit ea8e77ffec065cf1a8d1cd4517f9cebdab27cc17

Explicity specify featureCredentials inside the conf file. This enables the features inside the genesis ledger
Refactor common elements within Credential-related transactions
…ommit

Deposit_preauth: array length checks on the authcreds and unauthcreds fields
@@ -1,2775 +1,2933 @@
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the formatting the same in this JSON.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 509ecd1

tests/unit/models/transactions/test_deposit_preauth.py Outdated Show resolved Hide resolved
sample_credentials = [
Credential(issuer="SampleIssuer", credential_type="SampleCredType")
]
for val in range(0, 16):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you're trying to accomplish here, but this test is really hard to read and IMO a bit unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I agree. Shall I test two-three cases where the inputs will throw an error? Will that suffice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The tests don't need to be fully exhaustive, as long as you're getting the code coverage.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, simplified the tests in 379620a

)

if (
sum(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sum(
len(

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I cannot accept this code-suggestion because it doesn't work in the given context.
len([True, False, False, False]) == 4, it does not capture the fact that only one of the elements is True.

However, sum([True, False, False, False]) == 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty un-Pythonic (and unintuitive for Python devs) to sum booleans. You can add an if param is not False if you want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if param is not False does not work. It will need to be if refined_param is True for refined_param in [if param is not None for param in inputs_params] -- I can't get around this nested loop comprehension.

I came with a filter construct 0719107 -- this is easier to understand

xrpl/models/transactions/deposit_preauth.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_create.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_create.py Outdated Show resolved Hide resolved
Comment on lines 11 to 14
# Regular-Expression pertaining to Credentials Ledger Object transactions
# Note: This regex is not identical to the one used with DIDSet transaction. This regex
# mandates a minimum of length-1 strings.
HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]+")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the same hex regex for both DID strings and credential strings and just add an additional check to ensure the string isn't empty for credentials? Then it's more future-proof too (and much less confusing).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The restrictions on hexa-decimal format and non-zero are applicable to both uri and credential_type fields. If we split them into two separate checks (as you propose), an engineer might forget to include the second length check explicity.

I feel using separate regex-es is still a better solution. I can rename this regex to something more appropriate, CREDENTIAL_REGEX ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's less confusing to require the check than to only allow this regex to be usable in some hex situations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the rationale for the difference in the behavior between DID and Credentials strings?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In DID, an empty string in a transaction deletes the field. That's not relevant for credentials.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've incorporated your suggestion in e63b49d

@@ -182,3 +182,5 @@ PriceOracle
fixEmptyDID
fixXChainRewardRounding
fixPreviousTxnID
# Explicitly enable featureCredentials
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is unnecessary

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in f2651ca

.ci-config/rippled.cfg Show resolved Hide resolved
ckeshava and others added 3 commits October 21, 2024 14:35
… successfully deleted

Updates to Payment transaction model

Update AccountDelete transaction model with Credential ID array

Update EscrowFinish txn model with CredentialIDs

Updates to the PaymentChannelClaim txn model -- Include new unit test file
Co-authored-by: Mayukha Vadari <[email protected]>
@ckeshava
Copy link
Collaborator Author

@mvadari thanks for the early feedback

@ckeshava ckeshava requested a review from khancode October 22, 2024 20:40
@@ -57,7 +57,8 @@
"Child": -2,
"Nickname": 110,
"Contract": 99,
"GeneratorMap": 103
"GeneratorMap": 103,
"Credential": 129
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should go after Oracle

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I've updated in f476a51
It doesn't look like there is any rationale for the order of LedgerEntryTypes -- alphabetical or numeric.

Copy link
Contributor

coderabbitai bot commented Oct 24, 2024

Warning

Rate limit exceeded

@ckeshava has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between aeabe63 and cd7f860.

📒 Files selected for processing (3)
  • .ci-config/rippled.cfg (1 hunks)
  • CHANGELOG.md (1 hunks)
  • xrpl/models/transactions/deposit_preauth.py (2 hunks)

Walkthrough

This pull request introduces comprehensive support for Verifiable Credentials (XLS-70d) in the XRPL Python library. The changes span multiple files across the project, adding new transaction types (CredentialCreate, CredentialAccept, CredentialDelete), updating model definitions, enhancing test coverage, and modifying core library components to support credential-related functionality. The implementation includes validation logic, error handling, and integration tests to ensure robust credential management within the XRPL ecosystem.

Changes

File Change Summary
xrpl/models/transactions/ Added new transaction models for CredentialCreate, CredentialAccept, CredentialDelete with comprehensive validation.
tests/unit/models/transactions/ Added unit tests for new credential-related transaction models, validating input constraints and error handling.
tests/integration/transactions/test_credential.py Introduced integration tests for credential transaction lifecycle.
xrpl/core/binarycodec/definitions/definitions.json Added new transaction types, ledger entry types, and transaction results for credentials.
xrpl/models/utils.py Added utility functions for credential validation and hex encoding checks.
tests/unit/models/requests/test_ledger_entry.py Added tests for credential handling in ledger entries.
xrpl/models/requests/ledger_entry.py Introduced Credential class and updated LedgerEntry for credential queries.
xrpl/models/requests/deposit_authorized.py Added optional credentials field to DepositAuthorized class.
xrpl/models/transactions/deposit_preauth.py Enhanced credential management in DepositPreauth transaction.
xrpl/models/transactions/account_delete.py Added credential_ids field for credential management in AccountDelete.

Possibly related PRs

Suggested reviewers

  • mvadari
  • anissa-ripple
  • justinr1234
  • pdp2121

Poem

🐰 Hop, hop, credential dance!
In XRPL's blockchain expanse
Create, accept, and then delete
A rabbit's cryptographic feat!
Trust flows like carrots so sweet 🥕


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ckeshava ckeshava marked this pull request as ready for review October 24, 2024 20:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

🧹 Outside diff range and nitpick comments (27)
tests/unit/models/transactions/test_account_delete.py (2)

1-8: LGTM! Clean imports and well-defined constants.

The imports are appropriate, and the constants follow proper naming conventions. The account addresses appear to be valid XRPL addresses.

Remove the extra empty line at line 9 to maintain consistent spacing.


10-10: Add class docstring to improve test documentation.

Add a docstring describing the purpose of this test class and what aspects of AccountDelete it verifies.

 class TestAccountDelete(TestCase):
+    """Unit tests for the AccountDelete transaction model.
+
+    Tests credential_ids constraints and valid transaction creation scenarios.
+    """
xrpl/models/requests/account_objects.py (1)

Line range hint 1-7: Consider enhancing documentation for credential objects.

Since credentials are a new feature, it would be helpful to update the docstring to mention credential objects as one of the possible object types that can be returned. This would help users understand the full capabilities of the API.

Consider adding a note like:

"""
This request returns the raw ledger format for all objects owned by an account.

For a higher-level view of an account's trust lines and balances, see
AccountLinesRequest instead.
+
+The objects can include various types such as AMM, checks, credentials, escrows,
+and more. See the AccountObjectType enum for a complete list of supported types.

`See account_objects <https://xrpl.org/account_objects.html>`_
"""
tests/unit/models/transactions/test_credential_accept.py (2)

1-10: LGTM! Consider adding docstring comments.

The imports and test constants are well-structured. Consider adding docstring comments to explain the purpose of the test constants and their format requirements.

 _ACCOUNT_ISSUER = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
 _ACCOUNT_SUBJECT = "rNdY9XDnQ4Dr1EgefwU3CBRuAjt3sAutGg"
 _VALID_CREDENTIAL_TYPE = str_to_hex("Passport")
+
+# Constants used for testing CredentialAccept transaction
+# _ACCOUNT_ISSUER: The account that issues the credential
+# _ACCOUNT_SUBJECT: The account that receives the credential
+# _VALID_CREDENTIAL_TYPE: A valid credential type in hex format

12-12: Add class-level documentation.

Add a docstring to describe the purpose and scope of the test class.

 class TestCredentialAccept(TestCase):
+    """
+    Test suite for the CredentialAccept transaction model.
+    
+    This class verifies:
+    - Valid credential acceptance with correct parameters
+    - Validation of credential_type field constraints
+    - Error handling for invalid inputs
+    """
tests/unit/models/transactions/test_escrow_finish.py (2)

40-53: Consider using constants for magic numbers and improving string concatenation.

The test case effectively validates the maximum length constraint, but could be improved for maintainability.

+MAX_CREDENTIAL_IDS = 8  # Define at module level

 def test_creds_list_too_long(self):
     with self.assertRaises(XRPLModelException) as err:
         EscrowFinish(
             account=_ACCOUNT,
             owner=_ACCOUNT,
             offer_sequence=1,
-            credential_ids=["credential_index_" + str(i) for i in range(9)],
+            credential_ids=["credential_index_" + str(i) for i in range(MAX_CREDENTIAL_IDS + 1)],
         )

     self.assertEqual(
         err.exception.args[0],
-        "{'credential_ids': 'CredentialIDs list cannot have more than 8 "
-        + "elements.'}",
+        "{'credential_ids': 'CredentialIDs list cannot have more than 8 elements.'}"
     )

39-77: Document the relationship between EscrowFinish and credentials.

While the tests are well-structured, it would be helpful to add a docstring explaining why EscrowFinish transactions need credential IDs and how they relate to the XLS-0070d credentials standard mentioned in the PR objectives.

tests/integration/transactions/test_deposit_preauth.py (1)

17-37: Add docstring to clarify test purpose

While the test implementation is solid, consider adding a docstring to explain:

  • The purpose of testing both authorization and un-authorization in sequence
  • Expected behavior for each operation
  • Any prerequisites or assumptions

Example docstring:

"""
Test the authorization and un-authorization fields of DepositPreauth transaction.
Validates that:
1. An address can be successfully authorized
2. The same address can be subsequently unauthorized
Both operations should return tesSUCCESS.
"""
tests/unit/models/transactions/test_payment_channel_claim.py (1)

10-24: Add docstring and consider more realistic test data

While the test structure is good, consider:

  1. Adding a docstring to describe the test case
  2. Using a more realistic credential ID format (currently using a generic hex string)

Example docstring:

def test_valid(self):
    """Test successful creation of PaymentChannelClaim with valid credential ID."""
🧰 Tools
🪛 Gitleaks

18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

xrpl/models/transactions/escrow_finish.py (1)

58-61: Consider adding validation hints in docstring

While the docstring explains the basic functionality, it would be helpful to include the validation constraints (max 8 credentials, non-empty list requirement) to make the API more self-documenting.

     """Credentials associated with sender of this transaction. The credentials included
     must not be expired. If there are duplicates provided in the list, they will be
-    silently de-duped."""
+    silently de-duped. The list must not be empty and cannot contain more than 8
+    credential IDs."""
tests/unit/models/transactions/test_credential_delete.py (1)

12-28: Add docstrings and improve test method naming.

While the test implementation is correct and covers both valid parameter combinations, consider these improvements for better maintainability:

  1. Add a class docstring explaining the purpose of these tests
  2. Use more descriptive test method names (e.g., test_valid_with_issuer_account and test_valid_with_account_subject)
  3. Add assertion messages to provide context when tests fail

Example improvement:

 class TestCredentialDelete(TestCase):
+    """Unit tests for the CredentialDelete transaction model.
+    
+    Tests various combinations of parameters and validation rules for
+    credential deletion transactions.
+    """
-    def test_valid(self):
+    def test_valid_with_issuer_account(self):
         tx = CredentialDelete(
             issuer=_ACCOUNT_ISSUER,
             account=_ACCOUNT_SUBJECT,
             credential_type=_VALID_CREDENTIAL_TYPE,
         )
-        self.assertTrue(tx.is_valid())
+        self.assertTrue(tx.is_valid(), "Transaction should be valid with issuer and account")
tests/unit/models/transactions/test_credential_create.py (1)

1-11: Add module docstring and type hints for constants.

Consider adding a module docstring to describe the purpose of these tests and type hints for the constants to improve code maintainability.

+"""Unit tests for the CredentialCreate transaction model."""
 from unittest import TestCase

 from xrpl.models.exceptions import XRPLModelException
 from xrpl.models.transactions.credential_create import CredentialCreate
 from xrpl.utils import str_to_hex

-_ACCOUNT_ISSUER = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
-_ACCOUNT_SUBJECT = "rNdY9XDnQ4Dr1EgefwU3CBRuAjt3sAutGg"
-_VALID_CREDENTIAL_TYPE = str_to_hex("Passport")
-_VALID_URI = str_to_hex("www.my-id.com/username")
+_ACCOUNT_ISSUER: str = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
+_ACCOUNT_SUBJECT: str = "rNdY9XDnQ4Dr1EgefwU3CBRuAjt3sAutGg"
+_VALID_CREDENTIAL_TYPE: str = str_to_hex("Passport")
+_VALID_URI: str = str_to_hex("www.my-id.com/username")
tests/integration/transactions/test_credential.py (2)

1-13: Add module-level documentation.

Please add a module docstring describing the purpose of these integration tests and their relationship to the XLS-0070d credentials standard.

+"""
+Integration tests for XLS-0070d compliant credential transactions.
+
+These tests verify the complete lifecycle of a credential:
+1. Creation by an issuer
+2. Acceptance by a subject
+3. Deletion by the subject
+
+For more details on the credential standard, see:
+https://github.com/XRPLF/XRPL-Standards/discussions/70
+"""
 from tests.integration.integration_test_case import IntegrationTestCase

14-14: Use a standards-compliant URI format.

The URI should follow a more realistic format for credential identifiers as per XLS-0070d standard.

-_URI = "www.my-id.com/username"
+_URI = "did:example:123456/credentials/1234"
xrpl/models/transactions/payment_channel_claim.py (2)

111-114: Enhance docstring with validation constraints

While the docstring is informative, consider adding the validation constraints (max 8 elements) to make the requirements more visible to developers.

     """Credentials associated with sender of this transaction. The credentials included
     must not be expired. If there are duplicates provided in the list, they will be
-    silently de-duped."""
+    silently de-duped. The list must not be empty when provided and cannot contain
+    more than 8 elements."""

111-128: Consider runtime validation of credential expiration

While the docstring mentions that credentials must not be expired, there's no runtime validation for this constraint. Consider adding a validation mechanism or clarifying if this is handled at a different layer.

This could be implemented by:

  1. Adding an expiration check in _get_errors
  2. Documenting where the expiration validation occurs if it's handled elsewhere
  3. Adding a reference to the relevant section of the XLS-0070d specification
tests/unit/models/transactions/test_payment.py (3)

137-148: Consider improving test robustness and documentation.

The test correctly validates the empty credential_ids case, but could be enhanced:

  1. Add a docstring explaining the test's purpose
  2. Consider using a constant for the error message to avoid string duplication across tests
  3. Consider using assertIn instead of assertEqual for error message validation to make the test less brittle
     def test_credentials_array_empty(self):
+        """Test that Payment constructor raises XRPLModelException when credential_ids is empty."""
         with self.assertRaises(XRPLModelException) as err:
             Payment(
                 account=_ACCOUNT,
                 amount=_XRP_AMOUNT,
                 destination=_DESTINATION,
                 credential_ids=[],
             )
-        self.assertEqual(
+        self.assertIn(
+            "CredentialIDs list cannot be empty",
             err.exception.args[0],
-            "{'credential_ids': 'CredentialIDs list cannot be empty.'}"
         )

150-163: Enhance test clarity and maintainability.

The test effectively validates the maximum length constraint, but could be improved:

  1. Add a docstring explaining the test's purpose
  2. Define MAX_CREDENTIALS constant (8) at class/module level
  3. Use f-strings instead of string concatenation
  4. Consider using assertIn for more robust error message validation
+    MAX_CREDENTIALS = 8
+
     def test_credentials_array_too_long(self):
+        """Test that Payment constructor raises XRPLModelException when credential_ids exceeds maximum length."""
         with self.assertRaises(XRPLModelException) as err:
             Payment(
                 account=_ACCOUNT,
                 amount=_XRP_AMOUNT,
                 destination=_DESTINATION,
-                credential_ids=["credential_index_" + str(i) for i in range(9)],
+                credential_ids=[f"credential_index_{i}" for i in range(self.MAX_CREDENTIALS + 1)],
             )

-        self.assertEqual(
+        self.assertIn(
+            f"CredentialIDs list cannot have more than {self.MAX_CREDENTIALS} elements",
             err.exception.args[0],
-            "{'credential_ids': 'CredentialIDs list cannot have more than 8 "
-            + "elements.'}"
         )

137-163: Add test coverage for additional credential_ids scenarios.

Consider adding the following test cases to ensure comprehensive validation:

  1. Test with valid credential_ids (1-8 elements)
  2. Test with invalid credential_id format/type (if there are format requirements)

Example test structure:

def test_valid_credentials(self):
    """Test that Payment constructor accepts valid credential_ids."""
    tx = Payment(
        account=_ACCOUNT,
        amount=_XRP_AMOUNT,
        destination=_DESTINATION,
        credential_ids=["valid_credential_1", "valid_credential_2"],
    )
    self.assertTrue(tx.is_valid())
tests/unit/models/transactions/test_deposit_preauth.py (3)

19-25: Consider using named constants for better readability.

The bitmap logic uses magic numbers that could be more self-documenting with named constants.

Consider this improvement:

+    # Bitmap flags for input parameters
+    AUTHORIZE_BIT = 1        # 0001
+    UNAUTHORIZE_BIT = 2      # 0010
+    AUTH_CREDS_BIT = 4       # 0100
+    UNAUTH_CREDS_BIT = 8     # 1000
+    
+    VALID_SINGLE_INPUT_CASES = [AUTHORIZE_BIT, UNAUTHORIZE_BIT, 
+                               AUTH_CREDS_BIT, UNAUTH_CREDS_BIT]
+
     def test_all_input_combinations(self):
-        for val in range(0, 16):
+        for val in range(0, 16):  # Test all possible combinations (2^4)
             # bitmap
             # 0'th bit represents authorize field
             # 1'th bit represents unauthorize field
             # 2'nd bit represents authorized_credentials field
             # 3'rd bit represents unauthorized_credentials field

59-78: Simplify error message construction.

The error message concatenation is hard to maintain and prone to formatting issues.

Consider using f-strings or dedicated error message constants:

+    ERROR_NO_PARAMS = (
+        "Exactly one input parameter amongst authorize, unauthorize, "
+        "authorize_credentials or unauthorize_credentials must be set. "
+        "It is invalid if none of the params are specified."
+    )
+    
+    ERROR_MULTIPLE_PARAMS = (
+        "More than one input param cannot be specified for DepositPreauth "
+        "transaction. Please specify exactly one input parameter."
+    )
+
     self.assertEqual(
-        error.exception.args[0],
-        "{'DepositPreauth': '"
-        + "Exactly one input parameter amongst authorize, unauthorize, "
-        + "authorize_credentials or unauthorize_credentials must be set"
-        + "."
-        + " It is "
-        + "invalid if none of the params are specified."
-        + "'}",
+        error.exception.args[0],
+        f"{{'DepositPreauth': '{ERROR_NO_PARAMS}'}}",
     )

82-83: Clarify the deduplication note.

The note about credentials deduplication is vague. Consider adding more context about when and how deduplication might be implemented.

Add more detailed documentation:

-    # Note: If credentials de-duplication is implemented in the client library,
-    # additional tests need to be written
+    # Note: If credential deduplication is implemented in the client library,
+    # additional tests will be needed to verify:
+    # 1. Duplicate credentials are properly filtered
+    # 2. The 8-element limit applies after deduplication
+    # 3. The order of credentials is preserved after deduplication
xrpl/models/transactions/credential_accept.py (2)

17-20: Improve docstring formatting for clarity

The class docstring can be enhanced by following PEP 257 conventions, which recommend starting with a one-line summary followed by a blank line and a more detailed description. Consider reformatting it as:

"""Accepts a credential issued to the Account.

This transaction accepts a credential where the Account is the subject of the Credential object. The credential is not considered valid until it has been transferred or accepted.
"""

53-63: Optimize error message accumulation

Accumulating error messages using string concatenation can be less efficient and harder to read. Consider using a list to collect error messages and then join them at the end. Here's an improved version:

def _get_credential_type_error(self: Self) -> Optional[str]:
    errors = []
    if len(self.credential_type) == 0:
        errors.append("Length of credential_type field must be greater than 0.")
    if len(self.credential_type) > 64:
        errors.append("Length of credential_type field must not exceed 64 characters.")
    if not bool(HEX_REGEX.fullmatch(self.credential_type)):
        errors.append("credential_type field must be encoded in base-16 format.")
    return " ".join(errors) if errors else None
xrpl/models/transactions/credential_delete.py (1)

29-35: Ensure consistent docstring formatting for subject and issuer fields.

The docstrings for subject and issuer have inconsistent indentation and line breaks. Aligning them enhances readability.

Apply these diffs:

For the subject field:

-"""The person that the credential is for. If omitted, Account is assumed to be the
- subject."""
+"""The person that the credential is for. If omitted, Account is assumed to be the subject."""

For the issuer field:

-"""The issuer of the credential. If omitted, Account is assumed to be the issuer."""
+"""The issuer of the credential. If omitted, Account is assumed to be the issuer."""
xrpl/models/transactions/credential_create.py (1)

50-50: Avoid annotating self parameter in method definitions

Per PEP 484, the self parameter should not be annotated in method definitions. Removing the type annotation on self improves code readability and follows best practices.

Apply this diff to remove the self: Self annotations:

-def _get_errors(self: Self) -> Dict[str, str]:
+def _get_errors(self) -> Dict[str, str]:

-def _get_uri_error(self: Self) -> Optional[str]:
+def _get_uri_error(self) -> Optional[str]:

-def _get_credential_type_error(self: Self) -> Optional[str]:
+def _get_credential_type_error(self) -> Optional[str]:

Also applies to: 61-61, 73-73

xrpl/models/transactions/deposit_preauth.py (1)

67-78: Optimize sum usage by removing unnecessary list comprehension

You can use a generator expression instead of a list comprehension inside sum to avoid creating an intermediate list, which is more efficient.

Apply this diff:

 if (
-    sum(
-        [
-            param is not None
-            for param in (
-                self.authorize,
-                self.unauthorize,
-                self.authorize_credentials,
-                self.unauthorize_credentials,
-            )
-        ]
-    )
+    sum(
+        param is not None
+        for param in (
+            self.authorize,
+            self.unauthorize,
+            self.authorize_credentials,
+            self.unauthorize_credentials,
+        )
+    )
     > 1
 ):
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c288a5a and f476a51.

📒 Files selected for processing (26)
  • .ci-config/rippled.cfg (1 hunks)
  • tests/integration/transactions/test_credential.py (1 hunks)
  • tests/integration/transactions/test_deposit_preauth.py (1 hunks)
  • tests/unit/models/requests/test_deposit_authorized.py (1 hunks)
  • tests/unit/models/transactions/test_account_delete.py (1 hunks)
  • tests/unit/models/transactions/test_credential_accept.py (1 hunks)
  • tests/unit/models/transactions/test_credential_create.py (1 hunks)
  • tests/unit/models/transactions/test_credential_delete.py (1 hunks)
  • tests/unit/models/transactions/test_deposit_preauth.py (1 hunks)
  • tests/unit/models/transactions/test_escrow_finish.py (1 hunks)
  • tests/unit/models/transactions/test_payment.py (1 hunks)
  • tests/unit/models/transactions/test_payment_channel_claim.py (1 hunks)
  • xrpl/core/binarycodec/definitions/definitions.json (9 hunks)
  • xrpl/models/requests/account_objects.py (2 hunks)
  • xrpl/models/requests/deposit_authorized.py (2 hunks)
  • xrpl/models/transactions/init.py (2 hunks)
  • xrpl/models/transactions/account_delete.py (2 hunks)
  • xrpl/models/transactions/credential_accept.py (1 hunks)
  • xrpl/models/transactions/credential_create.py (1 hunks)
  • xrpl/models/transactions/credential_delete.py (1 hunks)
  • xrpl/models/transactions/deposit_preauth.py (2 hunks)
  • xrpl/models/transactions/escrow_finish.py (3 hunks)
  • xrpl/models/transactions/payment.py (2 hunks)
  • xrpl/models/transactions/payment_channel_claim.py (2 hunks)
  • xrpl/models/transactions/types/transaction_type.py (1 hunks)
  • xrpl/models/utils.py (1 hunks)
🧰 Additional context used
🪛 Gitleaks
tests/unit/models/transactions/test_payment_channel_claim.py

18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


36-36: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


57-57: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (47)
tests/unit/models/requests/test_deposit_authorized.py (1)

11-13: Verify credential ID format against XLS-0070d specification.

The credential ID appears to be a 32-byte hex string. Let's verify this format matches the XLS-0070d specification.

xrpl/models/requests/deposit_authorized.py (2)

9-9: LGTM!

The addition of Set from typing is appropriate for the new credentials field type annotation.


42-46: Consider adding validation and enhancing documentation.

The implementation looks good but could benefit from some enhancements:

  1. The docstring could specify:

    • Expected format of credential IDs
    • Any size limitations on the set
    • Impact on authorization decision when credentials are provided
  2. Consider adding validation for credential ID format if there are specific requirements.

Example enhancement:

     credentials: Optional[Set[str]] = None
     """The object IDs of Credential objects. If this field is included, then the
     credential will be taken into account when analyzing whether the sender can send
-    funds to the destination.
+    funds to the destination.
+
+    :param credentials: A set of credential IDs in canonical format.
+        Each ID must be a valid credential identifier as per XLS-0070d.
+        The authorization decision will consider both direct authorization
+        and any valid credentials provided in this set.
     """

Let's verify the credential ID format requirements:

tests/unit/models/transactions/test_account_delete.py (1)

41-43: Verify credential ID format requirements.

The test uses a 32-byte hex string as credential ID. Let's verify if this format is consistently required across the codebase.

✅ Verification successful

Credential ID format appears to be flexible with basic validation

Based on the code analysis:

  • The AccountDelete model only validates the number of credential IDs (must be between 1-8)
  • No specific format validation is enforced for the credential ID strings
  • The test case uses a 64-character hex string, but this appears to be an example rather than a strict requirement

The 32-byte hex string format used in the test is valid but not explicitly required by the model validation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for credential ID format validation or usage patterns

# Look for credential ID validation logic
rg -l "credential.*id" --type py

# Search for credential ID format documentation
rg -i "credential.*id.*format" --type md

Length of output: 559


Script:

#!/bin/bash
# Look for credential ID validation or usage in model files
rg -A 5 "credential.*id" xrpl/models/transactions/credential_accept.py
rg -A 5 "credential.*id" xrpl/models/transactions/account_delete.py

# Look for credential ID test cases to understand format patterns
rg -A 5 "credential.*id" tests/integration/transactions/test_credential.py

# Search for any hex string validation related to credentials
ast-grep --pattern 'def validate_hex_string($$$)'

Length of output: 1483


Script:

#!/bin/bash
# Look for hex string patterns in credential-related test files
rg -A 3 '[A-F0-9]{64}' tests/unit/models/transactions/test_account_delete.py tests/integration/transactions/test_credential.py

# Look for credential ID validation in model files
ast-grep --pattern 'class AccountDelete($$$)'

# Check for any credential format constants or validation patterns
rg -i "hex|format|pattern" xrpl/models/transactions/account_delete.py

Length of output: 611

xrpl/models/transactions/types/transaction_type.py (2)

21-23: LGTM! Verify related implementation files.

The new credential transaction types are properly implemented and follow the existing conventions.

Let's verify the presence of corresponding transaction models and tests:

✅ Verification successful

Transaction types are properly implemented with corresponding models and tests

The verification confirms that all necessary implementation files are present:

  • Transaction models:
    • xrpl/models/transactions/credential_accept.py
    • xrpl/models/transactions/credential_create.py
    • xrpl/models/transactions/credential_delete.py
  • Test coverage:
    • Integration: tests/integration/transactions/test_credential.py
    • Unit tests for each model:
      • tests/unit/models/transactions/test_credential_accept.py
      • tests/unit/models/transactions/test_credential_create.py
      • tests/unit/models/transactions/test_credential_delete.py
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for related credential implementation files

# Test 1: Look for transaction model implementations
echo "Checking for transaction models..."
fd -t f "credential_(accept|create|delete).py" xrpl/models/transactions/

# Test 2: Look for test files
echo "Checking for test files..."
fd -t f "test_credential" tests/

Length of output: 608


21-23: Consider updating documentation for new transaction types.

Since these transaction types implement the XLS-0070d credentials standard, ensure they are properly documented.

Let's check for documentation updates:

xrpl/models/requests/account_objects.py (2)

25-25: LGTM! Enum value correctly added.

The CREDENTIAL enum value follows the established pattern and maintains alphabetical ordering.


25-25: Verify credential object type usage.

Let's ensure the new credential object type is properly integrated with the rest of the credential management features.

✅ Verification successful

The credential object type is properly integrated with the credential management features

The verification shows comprehensive integration:

  • The CREDENTIAL account object type is used in the full credential lifecycle:
    • Creation via CredentialCreate transaction
    • Acceptance via CredentialAccept transaction
    • Deletion via CredentialDelete transaction
  • Integration tests validate the complete credential workflow including:
    • Object creation and storage in account directories
    • Proper authorization flows between issuer and subject
    • Cleanup and deletion verification
  • The implementation includes proper validation for:
    • Credential type format and length constraints
    • Authorization rules and permissions
    • Directory storage and cleanup
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the CREDENTIAL account object type
# and its integration with credential-related features

# Check for credential-related transaction types and their usage of account objects
echo "Checking credential transaction implementations..."
rg -l "AccountObjectType.CREDENTIAL" --type py

# Check for credential-related tests
echo "Checking credential-related tests..."
rg -l "test.*credential" --type py tests/

# Check for any potential documentation about credentials
echo "Checking for credential documentation..."
fd -e md -e rst | xargs rg -l "credential"

Length of output: 570


Script:

#!/bin/bash
# Let's check the actual content of the credential test file and related code

# Check the content of the credential test file
echo "=== Content of credential test file ==="
cat tests/integration/transactions/test_credential.py

# Search for credential-related code in the codebase
echo -e "\n=== Searching for credential-related code ==="
rg "credential" -i --type py -C 2

# Look for any credential-related transaction types
echo -e "\n=== Searching for credential transaction types ==="
rg "class.*Credential" --type py -C 2

Length of output: 86145

xrpl/models/transactions/account_delete.py (3)

4-6: LGTM: Import statements are appropriate

The new imports are correctly added to support type hints for the credential management feature.


53-65: 🛠️ Refactor suggestion

Improve credential validation logic

The current validation has some potential improvements:

  1. The empty set check might be redundant since you're using Set which already handles duplicates
  2. Consider adding validation for individual credential ID format
  3. Error messages could be more specific about requirements

Consider this enhanced validation:

     def _get_errors(self: Self) -> Dict[str, str]:
         errors = super()._get_errors()
 
         # Validation checks on the credential_ids field
         if self.credential_ids is not None:
-            if len(self.credential_ids) == 0:
-                errors["credential_ids"] = "CredentialIDs list cannot be empty."
             if len(self.credential_ids) > 8:
                 errors[
                     "credential_ids"
-                ] = "CredentialIDs list cannot have more than 8 elements."
+                ] = "Maximum of 8 unique credentials are allowed, but got {}.".format(
+                    len(self.credential_ids)
+                )
+            # Add validation for individual credential ID format if specified in XLS-0070d
+            for cred_id in self.credential_ids:
+                if not self._is_valid_credential_id(cred_id):
+                    errors["credential_ids"] = f"Invalid credential ID format: {cred_id}"
+                    break
 
         return errors

Let's check the credential ID format requirements:

#!/bin/bash
# Search for credential ID format specifications or examples in the codebase
rg -A 3 "credential.*id.*format|credential.*example" 

Line range hint 1-65: Consider transaction ordering and deletion constraints

Since AccountDelete is a critical transaction that removes an account from the ledger, we should carefully consider the interaction between credentials and account deletion:

  1. Should all associated credentials be automatically invalidated upon account deletion?
  2. Should account deletion be prevented if there are active credentials?
  3. How does this interact with the account deletion requirements mentioned in the class docstring?

Consider documenting these behavioral implications in the class docstring and ensuring they align with the XLS-0070d standard requirements.

Let's check for related documentation:

tests/unit/models/transactions/test_escrow_finish.py (1)

55-66: LGTM! Good edge case coverage.

The test effectively validates the empty list constraint with clear error message verification.

tests/integration/transactions/test_deposit_preauth.py (2)

6-6: LGTM! Clean import organization

The new imports are well-organized and the use of DESTINATION from reusable values promotes better maintainability over hardcoded addresses.

Also applies to: 9-10


Line range hint 1-67: Verify test execution in CI environment

According to the PR objectives, integration tests may not pass in CI due to missing Docker container for the custom branch.

Let's check for any CI configuration:

Consider:

  1. Adding clear documentation about test environment requirements
  2. Providing instructions for running tests with custom rippled builds
  3. Adding CI skip markers or conditional test execution based on environment
tests/unit/models/transactions/test_payment_channel_claim.py (3)

1-7: LGTM: Clean imports and well-structured constants

The imports are minimal and appropriate for the test suite, and the account constant helps maintain DRY principles.


18-19: Note: Static analysis warnings for public keys are false positives

The public key strings flagged by static analysis are valid XRPL public keys in hex format, not API keys. This is the correct format for XRPL transaction test data.

Also applies to: 36-37, 57-58

🧰 Tools
🪛 Gitleaks

18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


20-22: Verify credential ID format against XLS-0070d standard

Let's verify that the credential ID format in the test matches the specification.

✅ Verification successful

Based on the search results, I can now provide a final response:

Credential ID format is valid and consistent with implementation

The credential ID format used in the test (EA85602C1B41F6F1F5E83C0E6B87142FB8957BD209469E4CC347BA2D0C26F66A) is valid according to the codebase implementation which shows:

  • The format is consistently used across multiple test files (PaymentChannelClaim, AccountDelete, EscrowFinish)
  • The implementation enforces validation rules for credential IDs:
    • Cannot be empty
    • Maximum of 8 credentials allowed
    • Must be unique (silently de-duped)

The 64-character hexadecimal format matches the standard cryptographic identifier pattern used in XRPL.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for credential ID format documentation in the codebase
rg -l "credential.*id.*format" --type md

# Search for credential-related constants or validation patterns
ast-grep --pattern 'CREDENTIAL_ID_PATTERN = $_'

Length of output: 92


Script:

#!/bin/bash
# Search for credential ID validation or format checks in Python files
rg -A 5 "credential.*id" --type py

# Search for any credential-related constants or patterns
rg -A 5 "CREDENTIAL" --type py

# Look for test files related to credentials
fd "test.*credential.*\.py$"

Length of output: 24150

xrpl/models/transactions/escrow_finish.py (3)

6-6: LGTM: Clean import addition

The Set import is appropriately added to support the new credential_ids field type.


74-82: Consider adding credential format validation

The current validation only checks for list constraints but doesn't validate the format of the credential IDs themselves. Consider adding validation for the expected format of credential IDs if specified in the XLS-0070d standard.

#!/bin/bash
# Search for credential ID format specifications or validations
rg -i "credential.*format|credential.*pattern|credential.*validate"

74-82: 🛠️ Refactor suggestion

Verify credential limit alignment with XLS-0070d standard

The validation logic looks correct, but we should verify that the 8-credential limit aligns with the XLS-0070d specification requirements.

Additionally, consider extracting the magic number 8 into a class constant for better maintainability:

 @dataclass(frozen=True, **KW_ONLY_DATACLASS)
 class EscrowFinish(Transaction):
+    MAX_CREDENTIALS = 8
     ...
     def _get_errors(self: Self) -> Dict[str, str]:
         ...
         if self.credential_ids is not None:
             if len(self.credential_ids) == 0:
                 errors["credential_ids"] = "CredentialIDs list cannot be empty."
-            if len(self.credential_ids) > 8:
+            if len(self.credential_ids) > self.MAX_CREDENTIALS:
                 errors[
                     "credential_ids"
-                ] = "CredentialIDs list cannot have more than 8 elements."
+                ] = f"CredentialIDs list cannot have more than {self.MAX_CREDENTIALS} elements."
tests/unit/models/transactions/test_credential_delete.py (2)

1-10: LGTM! Well-structured test setup.

The imports are appropriate, and the test constants are well-defined at the module level. Good practice using str_to_hex utility for credential type encoding.


1-79: Verify compliance with XLS-0070d standard and integration points.

While the unit tests are well-structured, we should verify:

  1. The error messages and validation rules align with the XLS-0070d standard
  2. The test cases cover all required scenarios from the standard
  3. The integration points with the C++ implementation are consistent

Let's check for any related test files and validation rules:

✅ Verification successful

Integration and validation rules are properly implemented

The review concern can be resolved based on the comprehensive analysis of the implementation:

  1. The validation rules are consistent across all credential transactions (Create, Accept, Delete):

    • Hex-encoded credential_type with length limits (0-64 bytes)
    • Required issuer/subject validation
    • Consistent error messages
  2. The integration test demonstrates proper lifecycle:

    • Full create-accept-delete flow
    • Directory page verification
    • Proper cleanup validation
  3. The implementation aligns with credential management best practices:

    • Proper permission checks (subject/issuer relationships)
    • Atomic operations
    • Directory consistency checks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other credential-related test files
echo "=== Related test files ==="
fd test_credential

# Search for credential validation rules
echo -e "\n=== Validation rules ==="
rg -A 5 "class CredentialDelete"

# Search for standard references
echo -e "\n=== Standard references ==="
rg -i "XLS-0070d"

Length of output: 978


Script:

#!/bin/bash
# Let's check the implementation and integration tests for more context

# Check credential delete implementation
echo "=== CredentialDelete Implementation ==="
cat xrpl/models/transactions/credential_delete.py

# Check integration tests
echo -e "\n=== Integration Tests ==="
cat tests/integration/transactions/test_credential.py

# Check other credential transaction tests for consistency
echo -e "\n=== Other Credential Tests ==="
cat tests/unit/models/transactions/test_credential_create.py
cat tests/unit/models/transactions/test_credential_accept.py

Length of output: 13462

.ci-config/rippled.cfg (1)

185-186: LGTM! Verify rippled implementation.

The amendments are correctly added:

  • fixAMMv1_1: Addresses the missing amendment as discussed.
  • Credentials: Aligns with the XLS-0070d credentials standard implementation.

Let's verify the amendments are properly defined in rippled:

xrpl/models/utils.py (2)

3-7: LGTM! Import changes are well-structured.

The addition of Pattern and Final types enhances type safety and clearly indicates the immutability of the regex constant.


11-14: 🛠️ Refactor suggestion

Consider enhancing the regex pattern and documentation.

While the implementation is functional, there are a few suggestions for improvement:

  1. The comment could be more specific about the exact length requirements and format expectations.
  2. Consider adding a maximum length constraint to prevent potential abuse.
  3. The regex could be more restrictive if there are additional format requirements for credentials.

Let's check if there are any length constraints or format requirements in the XLS-0070d spec or related code:

Consider updating the pattern to include length constraints if applicable:

-HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]+")
+# Maximum length based on XLS-0070d spec (update the value as per spec)
+HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]{1,64}")
✅ Verification successful

Let me gather more context about the validation logic to understand the length constraints better.


Let me check one more thing to understand the validation implementation and length constraints:


Based on the gathered evidence, I can now provide the final response:

The regex pattern and validation are correctly implemented

The current implementation of HEX_REGEX is appropriate for its use case:

  1. The length constraints are already enforced in the transaction validation logic, as evidenced by the error messages: "Length of credential_type field must not be greater" and "Length of credential_type field must be greater than 0"
  2. The format requirement is correctly enforced - credentials must be in base-16 format
  3. Test cases confirm the validation works with practical examples like "Passport" converted to hex

The comment could be more specific, but the implementation itself is sound and follows the codebase's validation requirements.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for credential-related length or format constraints
rg -i "credential.*length|credential.*format" 

# Search for usage of HEX_REGEX to understand validation context
ast-grep --pattern 'HEX_REGEX.match($_)'

Length of output: 1755


Script:

#!/bin/bash
# Search for validation logic in credential-related files
rg -A 5 "validate_credential" 

# Look for actual credential type validation implementation
ast-grep --pattern 'def validate_credential_type(self, $_):
  $$$
'

# Check test files for example credential types
rg "credential_type.*=" tests/

Length of output: 2912


Script:

#!/bin/bash
# Look for the actual validation implementation in credential transaction files
rg -B 2 -A 10 "def validate_credential_type" xrpl/models/transactions/

# Check for _VALID_CREDENTIAL_TYPE constant definition
rg "_VALID_CREDENTIAL_TYPE.*=" tests/

Length of output: 427

tests/unit/models/transactions/test_credential_create.py (1)

64-106: LGTM! Comprehensive validation tests for credential_type field.

The tests thoroughly cover the validation requirements for the credential_type field, including length limits, empty values, and format validation.

tests/integration/transactions/test_credential.py (1)

49-54: Validate credential data and transaction fees.

The test should verify:

  1. The hex-encoded URI in the created credential object
  2. The transaction fees for each operation
  3. The complete credential object data after creation
xrpl/models/transactions/payment_channel_claim.py (1)

5-7: LGTM: Import changes are appropriate

The addition of Set from typing and Self from typing_extensions provides proper type hints for the new functionality while maintaining backward compatibility.

xrpl/models/transactions/__init__.py (2)

31-33: LGTM! New credential transaction imports are well-organized.

The new imports for credential-related transactions (CredentialAccept, CredentialCreate, CredentialDelete) are properly organized and follow the existing import pattern.


119-121: LGTM! New credential transactions properly exposed in public API.

The credential transaction classes are correctly added to the __all__ list, maintaining alphabetical order and following the module's public API conventions.

Let's verify the complete integration of these new transaction types:

✅ Verification successful

Credential transaction types are properly integrated across the codebase

The verification confirms complete integration:

  • Transaction type enums are defined in transaction_type.py
  • Implementation files exist for all three transactions: credential_accept.py, credential_create.py, and credential_delete.py
  • Test coverage is comprehensive with both unit tests and integration tests
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the complete integration of credential transaction types

# Test 1: Check if the corresponding transaction type definitions exist
echo "Checking transaction type definitions..."
rg "CREDENTIAL_(ACCEPT|CREATE|DELETE)" -A 1

# Test 2: Verify the existence of model implementation files
echo "Checking model implementation files..."
fd "credential_(accept|create|delete).py" "xrpl/models/transactions"

# Test 3: Look for corresponding test files
echo "Checking test coverage..."
fd "test_credential" "tests"

Length of output: 1606

xrpl/models/transactions/payment.py (3)

7-7: LGTM: Import addition is correct

The Set import is appropriately placed with other typing imports and is required for the new credential_ids field type.


132-148: Verify integration with credential system

Since this is part of a larger credential management feature, we should verify:

  1. That the Payment transaction properly integrates with the credential validation system
  2. That credential verification occurs before payment processing

Let's check the integration points:

✅ Verification successful

Based on the search results, I can now generate the final response:

Integration with credential system is properly implemented

The Payment transaction correctly integrates with the credential system through:

  1. Proper validation of credential_ids field with consistent checks across all transaction types (empty list and max 8 elements)
  2. Credential validation happens at the model level before any payment processing
  3. The credential system is consistently implemented across related transaction types (Payment, PaymentChannelClaim, AccountDelete, etc.)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for credential validation code
rg -A 5 "verify.*credential|validate.*credential"

# Search for payment processing order
ast-grep --pattern $'def process_payment($$$) {
  $$$
}'

Length of output: 258


Script:

#!/bin/bash
# Search for credential validation code with broader patterns
rg -A 5 "credential"

# Search for payment processing with correct Python syntax
ast-grep --pattern 'def process_payment($_):
    $$$'

# Look for credential-related imports and class definitions
rg -A 3 "class.*Payment|from.*credential|import.*credential"

Length of output: 72643


140-148: 🛠️ Refactor suggestion

Consider adding credential ID format validation

While the size validation is good, consider also validating the format of credential IDs. This would help catch issues earlier in the validation chain.

Here's a suggested enhancement:

 if self.credential_ids is not None:
     if len(self.credential_ids) == 0:
         errors["credential_ids"] = "CredentialIDs list cannot be empty."
     if len(self.credential_ids) > 8:
         errors[
             "credential_ids"
         ] = "CredentialIDs list cannot have more than 8 elements."
+    for cred_id in self.credential_ids:
+        if not is_valid_credential_id(cred_id):  # Add this helper function
+            errors["credential_ids"] = f"Invalid credential ID format: {cred_id}"

Let's verify if other transaction types have consistent validation:

xrpl/core/binarycodec/definitions/definitions.json (10)

56-56: LGTM: Credential ledger entry type addition

The new Credential ledger entry type is correctly added with ID 129, following the existing pattern and maintaining proper numeric ordering after Oracle (128).


1074-1093: LGTM: IssuerNode and SubjectNode field definitions

The UInt64 fields for credential node references are properly defined with:

  • Correct sequential nth values (24, 25)
  • Consistent serialization flags
  • Appropriate type assignment

2004-2013: LGTM: CredentialType field definition

The CredentialType blob field is correctly defined with:

  • Proper nth value (30)
  • VL encoding enabled for variable-length data
  • Consistent serialization flags

2174-2183: LGTM: Subject AccountID field definition

The Subject field is properly defined as an AccountID type with:

  • Correct nth value (24)
  • VL encoding enabled for account addresses
  • Consistent serialization flags

2224-2233: LGTM: CredentialIDs Vector256 field definition

The CredentialIDs field is correctly defined with:

  • Proper nth value (5)
  • VL encoding enabled for array data
  • Consistent serialization flags

2604-2613: LGTM: Credential STObject field definition

The Credential object field is properly defined with:

  • Correct nth value (33)
  • Appropriate type assignment
  • Consistent serialization flags

2803-2822: LGTM: Credential authorization array field definitions

The AuthorizeCredentials and UnauthorizeCredentials array fields are properly defined with:

  • Sequential nth values (26, 27)
  • Consistent serialization flags
  • Appropriate type assignments

3008-3008: LGTM: Credential transaction result code

The tecBAD_CREDENTIALS result code is properly defined with value 192, following the existing pattern for technical failure codes.


3059-3061: LGTM: Credential transaction types

The credential-related transaction types are properly defined with sequential values:

  • CredentialCreate: 54
  • CredentialAccept: 55
  • CredentialDelete: 56

Line range hint 1-3061: Verify field references in the codebase

The changes look good overall. Let's verify that all new fields are properly referenced in the codebase.

✅ Verification successful

The search results show that the new credential-related fields, transaction types, and result codes are properly referenced across the codebase:

  1. The credential fields are referenced in multiple transaction models and their corresponding test files
  2. The credential transaction types (Create/Accept/Delete) are defined in the transaction types and have corresponding model and test files
  3. The tecBAD_CREDENTIALS result code is referenced in the definitions file

Let's verify the actual implementation of these credential-related files to ensure completeness:


Based on the implementation files and integration tests, I can verify that the new credential-related fields in the definitions.json are properly implemented and used in the codebase:

All credential-related changes are properly integrated

  • The transaction models (CredentialCreate/Accept/Delete) correctly use the new fields
  • Field validations are implemented for credential_type and URI
  • Integration tests verify the full credential lifecycle including creation, acceptance, and deletion
  • The tests validate the presence/absence of credential objects in the ledger
  • All new fields from definitions.json (IssuerNode, SubjectNode, CredentialType, etc.) are referenced and used appropriately
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of new credential fields in the codebase

# Search for credential field references
echo "Searching for credential field references..."
rg -l "IssuerNode|SubjectNode|CredentialType|CredentialIDs|AuthorizeCredentials|UnauthorizeCredentials"

# Search for credential transaction type references
echo "Searching for credential transaction type references..."
rg -l "CredentialCreate|CredentialAccept|CredentialDelete"

# Search for credential result code references
echo "Searching for credential result code references..."
rg -l "tecBAD_CREDENTIALS"

Length of output: 1659


Script:

#!/bin/bash
# Check implementation of credential transaction files
echo "=== Credential Create Implementation ==="
cat xrpl/models/transactions/credential_create.py

echo -e "\n=== Credential Accept Implementation ==="
cat xrpl/models/transactions/credential_accept.py

echo -e "\n=== Credential Delete Implementation ==="
cat xrpl/models/transactions/credential_delete.py

echo -e "\n=== Integration Test ==="
cat tests/integration/transactions/test_credential.py

Length of output: 12545

xrpl/models/transactions/credential_delete.py (2)

16-17: Well-defined CredentialDelete transaction class.

The class correctly defines the CredentialDelete transaction, inheriting from Transaction, and uses dataclass for immutable field definitions.


39-56: Efficient error collection in _get_errors method.

The _get_errors method effectively aggregates errors from the superclass and validates the credential_type. The check ensuring at least one of subject or issuer is provided is appropriate.

xrpl/models/transactions/credential_create.py (1)

6-6: Verify compatibility of Self from typing_extensions with supported Python versions

Ensure that the use of Self from typing_extensions is compatible with the minimum Python version supported by the project. If the project supports Python versions earlier than 3.10 (where Self was introduced in typing_extensions), importing Self may cause compatibility issues.

Run the following script to identify usages of Self in the codebase:

✅ Verification successful

Let me gather more information about the Python version requirements.


Using Self from typing_extensions is compatible with project requirements

The project requires Python ^3.8 and typing-extensions ^4.2.0 as dependencies. The Self type was added to typing-extensions in version 3.11, which is well within the project's requirements. The widespread usage across 50+ files in the codebase also indicates this is an established pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of `Self` in the codebase to verify compatibility.

# Expected: List of files using `Self`.
rg --type=py --word-regexp 'from typing_extensions import Self' --files-with-matches

Length of output: 2509


Script:

#!/bin/bash
# Check Python version requirements in setup.py, pyproject.toml, or similar files
fd "setup.py|pyproject.toml|requirements.txt" --type f --exec cat {}

Length of output: 2201

xrpl/models/transactions/deposit_preauth.py (1)

124-127: Review necessity of # type: ignore comments on required fields

The # type: ignore comments on lines 124 and 127 suppress type checking for the issuer and credential_type fields. If possible, adjust the type annotations or the REQUIRED usage to eliminate the need for these ignores, ensuring that type checking remains effective.

Would you like assistance in resolving the type checking issues to remove the # type: ignore comments?

tests/unit/models/transactions/test_account_delete.py Outdated Show resolved Hide resolved
Comment on lines 48 to 51
credential_ids: Optional[Set[str]] = None
"""Credentials associated with sender of this transaction. The credentials included
must not be expired. If there are duplicates provided in the list, they will be
silently de-duped."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Documentation update needed for credential_ids field

The review comment is partially correct. The code shows that there are validation checks in place for the credential_ids field that enforce:

  1. Non-empty list (error: "CredentialIDs list cannot be empty")
  2. Maximum of 8 credentials (error: "CredentialIDs list cannot have more than 8 elements")
  3. Automatic de-duplication through the use of Set[str]

The docstring should be updated to reflect these constraints:

     credential_ids: Optional[Set[str]] = None
     """Credentials associated with sender of this transaction. The credentials included
-    must not be expired. If there are duplicates provided in the list, they will be
-    silently de-duped."""
+    must not be expired and must follow these constraints:
+    - Maximum of 8 unique credentials allowed
+    - Cannot be an empty list when specified
+    - Duplicates will be automatically removed through Set data structure
+    """
🔗 Analysis chain

Enhance credential_ids documentation and validation

The docstring should be more specific about constraints:

  1. Maximum limit of 8 credentials should be documented
  2. Silent de-duplication behavior might be surprising to users
  3. Consider adding validation for credential ID format

Apply this enhancement to the docstring:

     credential_ids: Optional[Set[str]] = None
     """Credentials associated with sender of this transaction. The credentials included
     must not be expired. If there are duplicates provided in the list, they will be
-    silently de-duped."""
+    silently de-duped. Maximum of 8 unique credentials are allowed.
+    
+    Note: Duplicate credentials will be automatically removed without warning."""

Let's check if there's a specific format for credential IDs:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for credential ID format validation in related files
rg -l "credential.*id.*format|credential.*validation" 

Length of output: 148


Script:

#!/bin/bash
# Search for credential-related validation or format checks
rg -A 3 "credential.*id|credential.*format" --type python

# Search for any credential-related constants or limits
rg -A 3 "MAX.*CREDENTIAL|CREDENTIAL.*LIMIT" --type python

# Look for credential validation in test files
rg -A 3 "test.*credential" --type python

# Search for credential-related error messages or exceptions
rg "credential.*error|invalid.*credential" --type python

Length of output: 345


Script:

#!/bin/bash
# Search for credential-related validation or format checks
rg -A 3 "credential.*id|credential.*format"

# Search for any credential-related constants or limits
rg -A 3 "MAX.*CREDENTIAL|CREDENTIAL.*LIMIT"

# Look for credential validation in test files
rg -A 3 "test.*credential"

# Search for credential-related error messages or exceptions
rg "credential.*error|invalid.*credential"

# Look for credential-related code in Python files
fd -e py -x cat {} | rg -A 3 "credential"

Length of output: 39713

Comment on lines 21 to 59
# invalid inputs to the credential_type field
def test_cred_type_field_too_long(self):
with self.assertRaises(XRPLModelException) as error:
CredentialAccept(
issuer=_ACCOUNT_ISSUER,
account=_ACCOUNT_SUBJECT,
credential_type=str_to_hex("A" * 65),
)
self.assertEqual(
error.exception.args[0],
"{'credential_type': 'Length of credential_type field must not be greater "
+ "than 64 bytes. '}",
)

def test_cred_type_field_empty(self):
with self.assertRaises(XRPLModelException) as error:
CredentialAccept(
issuer=_ACCOUNT_ISSUER,
account=_ACCOUNT_SUBJECT,
credential_type="",
)
self.assertEqual(
error.exception.args[0],
"{'credential_type': 'Length of credential_type field must be greater than "
+ "0. credential_type field must be encoded in base-16 format. '}",
)

def test_cred_type_field_not_hex(self):
with self.assertRaises(XRPLModelException) as error:
CredentialAccept(
issuer=_ACCOUNT_ISSUER,
account=_ACCOUNT_SUBJECT,
credential_type="Passport",
)
self.assertEqual(
error.exception.args[0],
"{'credential_type': 'credential_type field must be encoded in base-16 "
+ "format. '}",
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding boundary test cases.

The current tests cover important invalid scenarios, but consider adding these additional test cases:

  1. Test with exactly 64 bytes (boundary condition)
  2. Test with special characters in hex string
  3. Test with mixed case hex string
def test_cred_type_field_exact_length(self):
    # Test with exactly 64 bytes
    tx = CredentialAccept(
        issuer=_ACCOUNT_ISSUER,
        account=_ACCOUNT_SUBJECT,
        credential_type=str_to_hex("A" * 32),  # 32 chars = 64 bytes in hex
    )
    self.assertTrue(tx.is_valid())

def test_cred_type_field_special_hex(self):
    # Test with valid hex containing special characters
    with self.assertRaises(XRPLModelException) as error:
        CredentialAccept(
            issuer=_ACCOUNT_ISSUER,
            account=_ACCOUNT_SUBJECT,
            credential_type="!@#$%^",
        )

def test_cred_type_field_mixed_case(self):
    # Test with mixed case hex string
    tx = CredentialAccept(
        issuer=_ACCOUNT_ISSUER,
        account=_ACCOUNT_SUBJECT,
        credential_type="aAbBcCdD",
    )
    self.assertTrue(tx.is_valid())

tests/unit/models/transactions/test_credential_accept.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_delete.py Show resolved Hide resolved
xrpl/models/transactions/credential_delete.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_delete.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_create.py Outdated Show resolved Hide resolved
xrpl/models/transactions/deposit_preauth.py Outdated Show resolved Hide resolved
@ckeshava ckeshava requested a review from mvadari October 29, 2024 22:46
ckeshava and others added 5 commits October 30, 2024 12:57
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
xrpl/models/transactions/deposit_preauth.py (1)

62-87: Consider simplifying the parameter validation logic.

While the current implementation is correct, it could be more concise by using list comprehension and sum:

-        if (
-            self.authorize is None
-            and self.unauthorize is None
-            and self.authorize_credentials is None
-            and self.unauthorize_credentials is None
-        ):
+        params = [
+            self.authorize,
+            self.unauthorize,
+            self.authorize_credentials,
+            self.unauthorize_credentials,
+        ]
+        if not any(params):
tests/unit/models/transactions/test_deposit_preauth.py (3)

32-54: Improve test maintainability and setup.

  1. Move sample credentials to test-level setup to avoid potential test interdependency.
  2. Consider using test helper methods for credential creation.
def setUp(self):
    self.sample_credential = Credential(
        issuer="SampleIssuer",
        credential_type="SampleCredType"
    )

def _create_sample_credentials(self, count=1):
    return [
        Credential(
            issuer=f"SampleIssuer_{i}",
            credential_type="SampleCredType"
        )
        for i in range(count)
    ]

56-138: Reduce test duplication and improve error message construction.

  1. Consider using subTest to parameterize the array validation tests.
  2. Use f-strings instead of string concatenation for error messages.
def test_credentials_array_validation(self):
    test_cases = [
        ("authorize_credentials", "AuthorizeCredentials"),
        ("unauthorize_credentials", "UnauthorizeCredentials"),
    ]
    for field, prefix in test_cases:
        with self.subTest(f"{field}_validation"):
            # Test exceeding length
            credentials = self._create_sample_credentials(MAX_CREDENTIAL_ARRAY_LENGTH + 1)
            with self.assertRaises(XRPLModelException) as error:
                kwargs = {
                    "account": _ACCOUNT,
                    "fee": _FEE,
                    "sequence": _SEQUENCE,
                    field: credentials,
                }
                DepositPreauth(**kwargs)
            self.assertEqual(
                error.exception.args[0],
                f"{{'DepositPreauth': '{prefix} list cannot exceed {MAX_CREDENTIAL_ARRAY_LENGTH} elements.'}}"
            )

140-154: Consolidate empty array tests with array validation tests.

The empty array tests could be included in the parameterized test structure suggested above:

# Add to test_credentials_array_validation
with self.subTest(f"{field}_empty_array"):
    with self.assertRaises(XRPLModelException) as error:
        kwargs = {
            "account": _ACCOUNT,
            "fee": _FEE,
            "sequence": _SEQUENCE,
            field: [],
        }
        DepositPreauth(**kwargs)
    self.assertEqual(
        error.exception.args[0],
        f"{{'DepositPreauth': '{prefix} list cannot be empty.'}}"
    )
tests/unit/models/transactions/test_payment.py (2)

150-162: Consider extracting the magic number for better maintainability.

While the test is well-structured, consider extracting the magic number '9' as a constant to improve maintainability and make the relationship with the maximum limit of 8 more explicit.

+ MAX_CREDENTIAL_IDS = 8
+ EXCEED_MAX_CREDENTIAL_IDS = MAX_CREDENTIAL_IDS + 1

  def test_credentials_array_too_long(self):
      with self.assertRaises(XRPLModelException) as err:
          Payment(
              account=_ACCOUNT,
              amount=_XRP_AMOUNT,
              destination=_DESTINATION,
-             credential_ids=["credential_index_" + str(i) for i in range(9)],
+             credential_ids=["credential_index_" + str(i) for i in range(EXCEED_MAX_CREDENTIAL_IDS)],
          )

164-177: Enhance readability and clarity of the test.

Two suggestions to improve this test:

  1. Use an f-string instead of string concatenation for the error message assertion
  2. Make the test data more explicit about demonstrating duplicates
  def test_credentials_array_duplicates(self):
      with self.assertRaises(XRPLModelException) as err:
          Payment(
              account=_ACCOUNT,
              amount=_XRP_AMOUNT,
              destination=_DESTINATION,
-             credential_ids=["credential_index" for _ in range(5)],
+             credential_ids=["cred_1", "cred_2", "cred_1", "cred_3"],  # explicit duplicate of "cred_1"
          )

      self.assertEqual(
          err.exception.args[0],
-         "{'credential_ids_duplicates': 'CredentialIDs list cannot contain duplicate"
-         + " values.'}",
+         "{'credential_ids_duplicates': 'CredentialIDs list cannot contain duplicate values.'}"
      )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98e0f79 and e168dcb.

📒 Files selected for processing (6)
  • tests/unit/models/transactions/test_deposit_preauth.py (2 hunks)
  • tests/unit/models/transactions/test_payment.py (1 hunks)
  • xrpl/models/transactions/credential_accept.py (1 hunks)
  • xrpl/models/transactions/credential_create.py (1 hunks)
  • xrpl/models/transactions/credential_delete.py (1 hunks)
  • xrpl/models/transactions/deposit_preauth.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • xrpl/models/transactions/credential_accept.py
  • xrpl/models/transactions/credential_delete.py
  • xrpl/models/transactions/credential_create.py
🧰 Additional context used
📓 Learnings (2)
xrpl/models/transactions/deposit_preauth.py (2)
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/deposit_preauth.py:54-64
Timestamp: 2024-11-12T12:37:49.114Z
Learning: In the `DepositPreauth` class in `xrpl/models/transactions/deposit_preauth.py`, when validating that exactly one of the parameters `authorize`, `unauthorize`, `authorize_credentials`, or `unauthorize_credentials` is set, ensure that any refactoring maintains logical equivalence with the original condition to prevent altering the intended functionality.
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/deposit_preauth.py:89-93
Timestamp: 2024-11-12T12:37:49.114Z
Learning: In the Python file `xrpl/models/transactions/deposit_preauth.py`, within the `_validate_credentials_length` function, when validating `authorize_credentials` and `unauthorize_credentials`, it's acceptable to assign error messages to the same key in the `errors` dictionary if the conditions are mutually exclusive, as the error messages won't overwrite each other.
tests/unit/models/transactions/test_deposit_preauth.py (1)
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/deposit_preauth.py:54-64
Timestamp: 2024-11-12T12:37:49.114Z
Learning: In the `DepositPreauth` class in `xrpl/models/transactions/deposit_preauth.py`, when validating that exactly one of the parameters `authorize`, `unauthorize`, `authorize_credentials`, or `unauthorize_credentials` is set, ensure that any refactoring maintains logical equivalence with the original condition to prevent altering the intended functionality.
🔇 Additional comments (6)
xrpl/models/transactions/deposit_preauth.py (3)

6-7: LGTM! Clear and well-documented field additions.

The new imports and field declarations are appropriate for the credential management functionality. The docstrings clearly describe the purpose of each field.

Also applies to: 14-18, 48-53


89-104: LGTM! Well-structured validation logic.

The validation function effectively checks for:

  1. Empty arrays
  2. Arrays exceeding maximum length
  3. Duplicate credentials

The error messages are clear and descriptive.


119-131: 🛠️ Refactor suggestion

Add validation for Credential fields.

Consider adding validation to ensure:

  1. issuer is a valid account address
  2. credential_type is a properly formatted hex string
 @require_kwargs_on_init
 @dataclass(frozen=True, **KW_ONLY_DATACLASS)
 class Credential(NestedModel):
+    def _get_errors(self: Self) -> Dict[str, str]:
+        errors = {}
+        if not is_valid_classic_address(self.issuer):
+            errors["issuer"] = "Invalid XRPL account address"
+        if not is_hex_string(self.credential_type):
+            errors["credential_type"] = "Must be a hex-encoded string"
+        return errors

Likely invalid or redundant comment.

tests/unit/models/transactions/test_payment.py (3)

137-148: LGTM! Well-structured test for empty credential_ids validation.

The test effectively validates that an empty credential_ids list raises the appropriate exception with a clear error message.


137-177: Add test case for successful credential_ids validation.

While the error cases are well covered, consider adding a test case for successful validation with valid credential_ids.

Example test to add:

def test_valid_payment_with_credentials(self):
    transaction_dict = {
        "account": _ACCOUNT,
        "amount": _XRP_AMOUNT,
        "destination": _DESTINATION,
        "credential_ids": ["cred1", "cred2", "cred3"],  # adjust format as per spec
    }
    tx = Payment(**transaction_dict)
    self.assertTrue(tx.is_valid())

137-177: Verify consistent credential_ids validation across transaction types.

Since these tests validate credential_ids constraints for the Payment transaction, let's verify that the same validation rules are consistently implemented across other transaction types mentioned in the PR (AccountDelete, PaymentChannelClaim, EscrowFinish).

✅ Verification successful

Based on all the gathered information, I can now provide the final response:

Credential_ids validation is consistently implemented across transaction types

The verification confirms that credential_ids validation is properly and consistently implemented:

  • All four transaction types (Payment, AccountDelete, PaymentChannelClaim, and EscrowFinish) import and use the same validate_credential_ids function from utils.py
  • The validation function in utils.py enforces consistent rules:
    • Non-empty list check
    • Maximum 8 elements check
    • No duplicates check
  • Each transaction type properly integrates the validation by calling errors.update(validate_credential_ids(self.credential_ids)) in their validation logic
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent credential_ids validation across transaction types

# Search for credential_ids validation patterns
echo "Searching for credential_ids validation patterns..."
rg -A 5 "credential_ids.*=.*Field" xrpl/models/transactions/

# Search for validation error messages to ensure consistency
echo "Searching for credential_ids validation error messages..."
rg "CredentialIDs.*cannot" xrpl/models/

Length of output: 622


Script:

#!/bin/bash
# Let's check how credential_ids validation is implemented and used

# First, check the implementation in utils.py
echo "=== Checking credential_ids validation implementation ==="
rg -B 2 -A 10 "def validate_credential_ids" xrpl/models/utils.py

# Then, check which transaction types use this validation
echo -e "\n=== Checking transaction types using credential_ids ==="
ast-grep --pattern 'class $_ extends Transaction:
  $$$
  credential_ids: $$$
  $$$'

# Also check for direct Field usage
echo -e "\n=== Checking direct credential_ids field definitions ==="
rg "credential_ids.*=.*Field" xrpl/models/transactions/

Length of output: 918


Script:

#!/bin/bash
# Let's try a different approach to find credential_ids usage

# Search for credential_ids in transaction models
echo "=== Searching for credential_ids in transaction models ==="
rg -l "credential_ids" xrpl/models/transactions/

# Check for imports of validate_credential_ids
echo -e "\n=== Checking imports of validate_credential_ids ==="
rg "from.*utils.*validate_credential_ids" xrpl/models/transactions/

# Get more context around credential_ids validation implementation
echo -e "\n=== Getting full credential_ids validation implementation ==="
rg -B 5 -A 15 "validate_credential_ids" xrpl/models/utils.py

# Look for any class definitions containing credential_ids
echo -e "\n=== Looking for classes with credential_ids ==="
ast-grep --pattern 'class $CLASS {
  $$$
  credential_ids$$$
  $$$
}'

Length of output: 1627


Script:

#!/bin/bash
# Let's check how credential_ids is defined in each transaction type

echo "=== Checking Payment.py credential_ids implementation ==="
rg -B 2 -A 5 "credential_ids.*=.*Field" xrpl/models/transactions/payment.py

echo -e "\n=== Checking AccountDelete.py credential_ids implementation ==="
rg -B 2 -A 5 "credential_ids.*=.*Field" xrpl/models/transactions/account_delete.py

echo -e "\n=== Checking PaymentChannelClaim.py credential_ids implementation ==="
rg -B 2 -A 5 "credential_ids.*=.*Field" xrpl/models/transactions/payment_channel_claim.py

echo -e "\n=== Checking EscrowFinish.py credential_ids implementation ==="
rg -B 2 -A 5 "credential_ids.*=.*Field" xrpl/models/transactions/escrow_finish.py

# Check for validation calls in these files
echo -e "\n=== Checking validation calls in transaction files ==="
rg "validate_credential_ids" xrpl/models/transactions/{payment,account_delete,payment_channel_claim,escrow_finish}.py

Length of output: 1957


Script:

#!/bin/bash
# Let's check the class definitions to see how credential_ids is defined

echo "=== Checking class definitions in Payment.py ==="
rg -B 5 -A 15 "class Payment" xrpl/models/transactions/payment.py

echo -e "\n=== Checking class definitions in AccountDelete.py ==="
rg -B 5 -A 15 "class AccountDelete" xrpl/models/transactions/account_delete.py

echo -e "\n=== Checking class definitions in PaymentChannelClaim.py ==="
rg -B 5 -A 15 "class PaymentChannelClaim" xrpl/models/transactions/payment_channel_claim.py

echo -e "\n=== Checking class definitions in EscrowFinish.py ==="
rg -B 5 -A 15 "class EscrowFinish" xrpl/models/transactions/escrow_finish.py

Length of output: 5682

@ckeshava
Copy link
Collaborator Author

@khancode I have responded to/incorporated all your comments as of 387f72b

@ckeshava ckeshava requested a review from khancode December 20, 2024 03:36
xrpl/models/transactions/credential_create.py Outdated Show resolved Hide resolved
xrpl/models/transactions/credential_create.py Outdated Show resolved Hide resolved

from xrpl.models.exceptions import XRPLModelException

HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]*")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the hex significantly different than what's in xrpl/constants.py? It looks like yours allows for empty string which doesn't look correct.

@khancode
Copy link
Collaborator

@ckeshava You'll need to make the same rippled.cfg changes - XRPLF/xrpl.js#2829 (comment)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
xrpl/models/utils.py (2)

13-13: Mark constant as Final.

Use Final type annotation to ensure immutability:

-MAX_CREDENTIAL_ARRAY_LENGTH = 8
+MAX_CREDENTIAL_ARRAY_LENGTH: Final[int] = 8

34-39: Standardize error messages.

Consider standardizing error messages for consistency:

-        errors.append("cannot be an empty string.")
+        errors.append("Cannot be empty.")
-        errors.append(f"Length cannot exceed {_MAX_CREDENTIAL_LENGTH}.")
+        errors.append(f"Length must not exceed {_MAX_CREDENTIAL_LENGTH} characters.")
-        errors.append("credential_type field must be encoded in hex.")
+        errors.append("Must be a valid hexadecimal string.")
xrpl/models/transactions/credential_create.py (2)

18-18: Add documentation for the _MAX_URI_LENGTH constant

Consider adding a docstring to explain the purpose and constraints of this constant.

 _MAX_URI_LENGTH = 256
+"""Maximum allowed length in characters for the URI field in a credential."""

70-82: Improve error message consistency

Consider defining constant error messages and making them more descriptive:

+    # Error message constants
+    _ERR_EMPTY = "URI cannot be empty"
+    _ERR_TOO_LONG = f"URI length cannot exceed {_MAX_URI_LENGTH} characters"
+    _ERR_NOT_HEX = "URI must be hex-encoded (required for Blob serialization)"
+
     def _get_uri_error(self: Self) -> Optional[str]:
         if self.uri is None:
             return None
 
         errors = []
         if len(self.uri) == 0:
-            errors.append("cannot be an empty string.")
+            errors.append(self._ERR_EMPTY)
         elif len(self.uri) > _MAX_URI_LENGTH:
-            errors.append(f"Length cannot exceed {_MAX_URI_LENGTH} characters.")
+            errors.append(self._ERR_TOO_LONG)
             
         if not HEX_REGEX.fullmatch(self.uri):
-            errors.append("Must be encoded in hex.")
+            errors.append(self._ERR_NOT_HEX)
         return " ".join(errors) if errors else None
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e168dcb and 8a93ed7.

📒 Files selected for processing (4)
  • xrpl/models/transactions/credential_accept.py (1 hunks)
  • xrpl/models/transactions/credential_create.py (1 hunks)
  • xrpl/models/transactions/credential_delete.py (1 hunks)
  • xrpl/models/utils.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • xrpl/models/transactions/credential_delete.py
  • xrpl/models/transactions/credential_accept.py
🧰 Additional context used
📓 Learnings (1)
xrpl/models/transactions/credential_create.py (2)
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/credential_create.py:61-71
Timestamp: 2024-11-12T12:37:49.114Z
Learning: In `xrpl/models/transactions/credential_create.py`, the `uri` field in `CredentialCreate` is stored as a `Blob` in `rippled` and must be base-16 encoded as per the serialization requirements.
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/credential_delete.py:57-68
Timestamp: 2024-11-12T12:37:49.114Z
Learning: Consistent implementation patterns are preferred in the `xrpl-py` codebase, especially in transaction models under `xrpl/models/transactions/`. When suggesting refactoring that affects multiple transactions, consider the impact on overall consistency and propose comprehensive changes when appropriate.
🔇 Additional comments (5)
xrpl/models/utils.py (2)

11-11: 🛠️ Refactor suggestion

Use stricter hex validation pattern.

The current pattern allows empty strings. Consider using [a-fA-F0-9]+ to require at least one character.

-HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]*")
+HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]+")

Likely invalid or redundant comment.


43-68: ⚠️ Potential issue

Add individual credential validations.

The function should validate each credential in the list:

  1. Format using HEX_REGEX
  2. Length against _MAX_CREDENTIAL_LENGTH
     if len(credential_list) != len(set(credential_list)):
         errors["credential_ids_duplicates"] = (
             "CredentialIDs list cannot contain duplicate values."
         )
 
+    for i, cred in enumerate(credential_list):
+        if not HEX_REGEX.fullmatch(cred):
+            errors[f"credential_ids[{i}]"] = "Must be a valid hexadecimal string."
+        elif len(cred) > _MAX_CREDENTIAL_LENGTH:
+            errors[f"credential_ids[{i}]"] = f"Length must not exceed {_MAX_CREDENTIAL_LENGTH} characters."
+
     return errors

Likely invalid or redundant comment.

xrpl/models/transactions/credential_create.py (3)

21-56: Well-structured class implementation!

The class definition follows best practices with:

  • Proper use of dataclass decorators
  • Clear field documentation
  • Correct type annotations
  • Appropriate use of REQUIRED sentinel

57-69: Clean error aggregation implementation!

The error handling logic is well-structured and properly extends the parent class functionality.


1-82: Verify integration with other credential transactions

The implementation looks solid, but let's verify the consistency of error handling across all credential-related transactions.

✅ Verification successful

Error handling and hex validation are consistent across credential transactions

The verification shows that:

  1. The error handling pattern in CredentialCreate follows the SDK's standard approach, using _get_errors() and specific error methods like _get_uri_error().
  2. The hex validation using HEX_REGEX is consistently implemented across the codebase, including in credential-related transactions.
  3. The credential type validation is centralized in utils.py and reused across credential transactions.
  4. The error handling structure matches other transaction types in the SDK (e.g., NFToken, AMM transactions).
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check consistency of error handling across credential transactions
# Expected: Similar error handling patterns in related files

# Find all credential-related transaction files
echo "Checking credential transaction files..."
fd "credential.*\.py$" --type f

# Check error handling patterns
echo -e "\nChecking error handling patterns..."
rg -A 3 "_get_.*_error" --type py

# Check hex validation consistency
echo -e "\nChecking hex validation patterns..."
rg "HEX_REGEX" --type py

Length of output: 14313

@ckeshava
Copy link
Collaborator Author

@khancode the said amendment is already present in the rippled.cfg file: https://github.com/ckeshava/xrpl-py/blob/cred/.ci-config/rippled.cfg#L186

In fact, it is present in the main branch of xprl-py repository.

@ckeshava ckeshava requested a review from khancode December 20, 2024 18:06
@khancode
Copy link
Collaborator

@khancode the said amendment is already present in the rippled.cfg file: https://github.com/ckeshava/xrpl-py/blob/cred/.ci-config/rippled.cfg#L186

In fact, it is present in the main branch of xprl-py repository.

That's fixAMMv1_1 which should be part of v2.2.0. fixAMMv1_2 is the newest one for v2.3.0.

Copy link
Collaborator

@khancode khancode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving so I don't block the merge and release. If you can't address my comments now then just create a follow-up PR afterwards.

@ckeshava
Copy link
Collaborator Author

@khancode the said amendment is already present in the rippled.cfg file: https://github.com/ckeshava/xrpl-py/blob/cred/.ci-config/rippled.cfg#L186
In fact, it is present in the main branch of xprl-py repository.

That's fixAMMv1_1 which should be part of v2.2.0. fixAMMv1_2 is the newest one for v2.3.0.

Thanks. I've included the recent amendments in aeabe63

.ci-config/rippled.cfg Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
xrpl/models/transactions/credential_create.py (3)

18-18: Add documentation for the _MAX_URI_LENGTH constant

Consider adding a docstring to explain the purpose and source of this limit.

 _MAX_URI_LENGTH = 256
+"""Maximum allowed length for the URI field in characters, as defined by the XLS-0070d standard."""

74-82: Improve error message consistency and add constant for minimum length

  1. Error messages should follow a consistent style pattern
  2. Consider extracting the minimum length requirement to a constant
+_MIN_URI_LENGTH = 1
+"""Minimum allowed length for the URI field in characters."""
+
 def _get_uri_error(self: Self) -> Optional[str]:
     if self.uri is None:
         return None
 
     errors = []
-    if len(self.uri) == 0:
-        errors.append("cannot be an empty string.")
-    elif len(self.uri) > _MAX_URI_LENGTH:
-        errors.append(f"Length cannot exceed {_MAX_URI_LENGTH} characters.")
+    if len(self.uri) < _MIN_URI_LENGTH:
+        errors.append(f"Length must be at least {_MIN_URI_LENGTH} character(s).")
+    elif len(self.uri) > _MAX_URI_LENGTH:
+        errors.append(f"Length must not exceed {_MAX_URI_LENGTH} characters.")
 
     if not HEX_REGEX.fullmatch(self.uri):
-        errors.append("Must be encoded in hex.")
+        errors.append("Value must be encoded in hexadecimal format.")
     return " ".join(errors) if errors else None

46-50: Enhance URI field documentation to explain hex encoding requirement

The URI field documentation should explain that the value must be hex-encoded because it's stored as a Blob type in rippled.

     uri: Optional[str] = None
     """
     Additional data about the credential (such as a link to the Verifiable
-    Credential document).
+    Credential document). The value must be hex-encoded as it is stored
+    as a Blob type in the XRPL ledger.
     """
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a93ed7 and aeabe63.

📒 Files selected for processing (2)
  • .ci-config/rippled.cfg (1 hunks)
  • xrpl/models/transactions/credential_create.py (1 hunks)
🧰 Additional context used
📓 Learnings (1)
xrpl/models/transactions/credential_create.py (2)
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/credential_create.py:61-71
Timestamp: 2024-11-12T12:37:49.114Z
Learning: In `xrpl/models/transactions/credential_create.py`, the `uri` field in `CredentialCreate` is stored as a `Blob` in `rippled` and must be base-16 encoded as per the serialization requirements.
Learnt from: ckeshava
PR: XRPLF/xrpl-py#759
File: xrpl/models/transactions/credential_delete.py:57-68
Timestamp: 2024-11-12T12:37:49.114Z
Learning: Consistent implementation patterns are preferred in the `xrpl-py` codebase, especially in transaction models under `xrpl/models/transactions/`. When suggesting refactoring that affects multiple transactions, consider the impact on overall consistency and propose comprehensive changes when appropriate.
🔇 Additional comments (4)
.ci-config/rippled.cfg (2)

Line range hint 1-191: LGTM! Configuration is properly set up for testing

The configuration file is well-structured with appropriate settings for testing the Credentials feature:

  • Network ports and protocols are correctly configured
  • Required amendments including Credentials are enabled
  • Database and logging settings are suitable for testing environment

187-189: Verify the necessity of AMM-related amendments

The PR objectives focus on implementing the Credentials standard, but these changes add AMM-related amendments. While having additional amendments won't break anything, we should verify if they are required for the integration tests.

✅ Verification successful

AMM-related amendments are necessary for the test suite

The presence of extensive AMM-related test files (both unit and integration) in the codebase indicates that these amendments are required for proper testing functionality. The test suite includes AMM-specific features like AMM Create, Deposit, Withdraw, Vote, and Bid operations, which likely depend on these amendments being enabled in the rippled configuration.

  • Found multiple AMM test files:
    • tests/integration/transactions/test_amm_*.py
    • tests/unit/models/transactions/test_amm_*.py
  • The amendments are correctly configured in .ci-config/rippled.cfg
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the new amendments are referenced in the test files

# Search for references to the new amendments in test files
echo "Searching for references to new amendments in test files..."
rg -i "fixAMMv1_2|AMMClawback|InvariantsV1_1" "*test*"

# Check if these amendments are enabled in the rippled version used for testing
echo "Checking rippled version compatibility..."
curl -s -X POST -H "Content-Type: application/json" -d '{"method": "feature", "params": [{"feature": "fixAMMv1_2"}]}' http://0.0.0.0:5005/

Length of output: 466


Script:

#!/bin/bash
# Search for test files and check for amendment references
echo "Searching for test files containing amendment references..."
fd -t f "test" | xargs rg -i "fixAMMv1_2|AMMClawback|InvariantsV1_1"

# Check if these amendments are used in AMM-related test files
echo "Searching for AMM-related test files..."
fd -t f "test|spec" | xargs rg -i "amm.*test"

# Check the rippled configuration for other amendment references
echo "Checking rippled configuration..."
rg -i "fixAMMv1_2|AMMClawback|InvariantsV1_1" .ci-config/

Length of output: 1802

xrpl/models/transactions/credential_create.py (2)

21-56: LGTM! Well-structured class definition with comprehensive documentation

The class follows SDK patterns with appropriate use of decorators, field types, and documentation.


1-82: Verify integration with credential system components

The implementation looks solid, but let's verify the integration with other credential-related components.

✅ Verification successful

Integration with credential system components verified successfully

The implementation is properly integrated with the credential system:

  • All three credential transaction types (Create/Accept/Delete) consistently inherit from the Transaction base class
  • Credential type validation is uniformly implemented across all credential transactions using the shared get_credential_type_error utility
  • Transaction types are properly defined in the TransactionType enum
  • URI validation is unique to CredentialCreate and has appropriate length constraints
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent implementation across credential transaction types
# and check for potential integration issues.

# Check for consistent error handling patterns across credential transactions
echo "Checking error handling patterns..."
rg -A 3 "_get_.*_error" "xrpl/models/transactions/credential_*.py"

# Verify credential type validation consistency
echo "Checking credential type validation..."
rg -A 5 "get_credential_type_error" "xrpl/models/transactions/"

# Look for potential URI validation inconsistencies
echo "Checking URI validation patterns..."
rg -A 3 "_MAX_URI_LENGTH" "xrpl/models/transactions/"

Length of output: 4182


Script:

#!/bin/bash
# Let's verify the complete credential system integration

# Check for all credential-related transaction types
echo "Checking all credential transaction types..."
fd "credential_.*\.py$" xrpl/models/transactions/

# Verify consistent imports and base class usage
echo "Checking base class and imports..."
rg -l "class.*\(Transaction\)" xrpl/models/transactions/credential_*.py

# Check transaction type enum values
echo "Checking transaction type definitions..."
rg -A 5 "TransactionType.*CREDENTIAL" xrpl/models/

# Verify credential type validation utility
echo "Checking credential type validation implementation..."
fd "utils\.py$" xrpl/models/ --exec rg -A 5 "get_credential_type_error"

Length of output: 2536

def _validate_credentials_length(
credentials: List[Credential], field_name: str
) -> None:
if len(credentials) == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add a check like this at the top, you can remove the ifs below when you call this helper function

if credentials is None:
    return

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I've incorporated in cd7f860

Copy link
Collaborator

@mvadari mvadari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor changes but nothing to block merge. Nice work!

@ckeshava ckeshava merged commit aaa00ea into XRPLF:main Dec 21, 2024
22 checks passed
@ckeshava
Copy link
Collaborator Author

thank you all for reviewing this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants