Skip to content

feat: add Credentials support (XLS-70)#154

Open
Copilot wants to merge 56 commits into
mainfrom
copilot/xls-70-support-credentials
Open

feat: add Credentials support (XLS-70)#154
Copilot wants to merge 56 commits into
mainfrom
copilot/xls-70-support-credentials

Conversation

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown

High Level Overview of Change

This PR adds initial XLS-70 Credentials support to the model layer and includes follow-up fixes from review to ensure protocol compatibility, validation correctness, and no_std build compatibility.

  • Credential transaction models

    • Added CredentialCreate, CredentialAccept, and CredentialDelete.
    • Added model validation for:
      • credential_type (non-empty, max 128 hex chars) in all three credential transactions.
      • uri max length in CredentialCreate.
    • Added CredentialDelete validation requiring the submitter Account to match either subject or issuer.
    • Updated CredentialDelete tests to align with the new submitter validation logic (account == subject || account == issuer).
  • Credential ledger object

    • Added Credential ledger object model.
    • Added typed CredentialFlag enum with LsfAccepted = 0x00010000.
    • Updated object to use typed flags instead of NoFlags.
  • DepositPreauth XLS-70 extensions

    • Transaction model supports:
      • AuthorizeCredentials
      • UnauthorizeCredentials
    • Ledger object supports credential-based authorization shape.
    • Enforced XOR-style validation for authorization selectors.
    • Added validation that credential authorization arrays contain 1..=8 entries.
    • Added no_std test-module imports for vec! macro usage.
  • Shared credential authorization types

    • Deduplicated CredentialAuthorizationFields / CredentialAuthorization into a shared model:
      • src/models/credential_authorization.rs
    • Reused from both transaction and ledger DepositPreauth models.
  • Related request/model surface

    • account_objects request supports type=credential.
    • ledger_entry request supports credential selector fields.
    • deposit_authorized request supports credentials.
    • Added credential_ids on affected tx models: Payment, EscrowFinish, PaymentChannelClaim, AccountDelete.
    • Added explicit #[serde(rename = "CredentialIDs")] on all four to match rippled casing.
  • Clippy / formatting / compatibility fixes

    • Boxed XRPLRequest::LedgerEntry to resolve large_enum_variant.
    • Added use alloc::boxed::Box; in request models for no_std compatibility.
    • Ran cargo fmt fixes, including import ordering.
    • Added targeted serde tests to verify CredentialIDs field naming.
  • Compatibility + docs

    • Kept account-based DepositPreauth::new(...) constructor semantics and added credential-based constructor path for ledger objects.
    • Updated changelog entry for XLS-70 support.

Context of Change

XLS-70 requires first-class support for Credential objects and credential-aware authorization flows. The initial pass added missing model coverage, but review identified protocol-facing gaps (notably CredentialIDs casing, missing lsfAccepted, and missing validations) that could cause silent incompatibility with rippled or allow invalid model states. A later review also identified no_std regressions introduced during follow-up fixes (missing Box/vec! imports and a mismatched test fixture). This update closes those gaps while preserving the original architecture and scope.

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

Before / After

Before:

  • SDK lacked XLS-70 credential transaction/object coverage.
  • credential_ids serialized incorrectly as CredentialIds instead of CredentialIDs.
  • Credential object had no typed lsfAccepted flag support.
  • DepositPreauth credential arrays were not size-validated.
  • Credential authorization types were duplicated in two modules.
  • Clippy failed on XRPLRequest large enum variant.
  • Follow-up changes introduced no_std build regressions from missing imports and one invalid test fixture.

After:

  • Credential primitives are modeled end-to-end with protocol-correct field naming and stronger validation.
  • CredentialIDs serializes correctly on all four affected transaction types.
  • Credential object exposes typed CredentialFlag::LsfAccepted.
  • DepositPreauth credential arrays are validated at 1..=8.
  • Shared credential authorization types are centralized and reused.
  • XRPLRequest::LedgerEntry is boxed and no_std-compatible (alloc::boxed::Box in scope).
  • no_std test modules compile with explicit alloc::vec import for vec!.
  • CredentialDelete test fixtures now match the enforced submitter relationship rules.

Test Plan

  • Ran targeted unit tests for updated behavior:
    • models::transactions::payment::tests::test_credential_ids_serde_name
    • models::transactions::account_delete::tests::test_credential_ids_serde_name
    • models::transactions::escrow_finish::tests::test_credential_ids_serde_name
    • models::transactions::payment_channel_claim::tests::test_credential_ids_serde_name
    • models::transactions::credential_create::tests::test_credential_type_length_validation
    • models::transactions::credential_accept::tests::test_credential_type_length_validation
    • models::transactions::credential_delete::tests::test_account_must_match_subject_or_issuer
    • models::transactions::credential_delete::tests::test_valid_with_subject
    • models::transactions::deposit_preauth::tests::test_authorize_credentials_array_size_validation
    • models::transactions::deposit_preauth::tests::test_valid_with_authorize_credentials
    • models::ledger::objects::deposit_preauth::tests::test_serde_with_authorize_credentials
  • Ran no_std reproduction command from review:
    • cargo test --release --no-default-features --features embassy-rt,core,utils,wallet,models,helpers,websocket,json-rpc
  • Ran formatting:
    • cargo fmt --all
  • Ran linting:
    • cargo clippy --lib -- -D warnings

Copilot AI linked an issue Mar 30, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add support for Credentials (XLS-70) Add XLS-70 Credentials models and credential-based DepositPreauth support Mar 30, 2026
Copilot AI requested a review from e-desouza March 30, 2026 23:03

@e-desouza e-desouza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Took a close look at this. The structure follows existing patterns well and the scope is right for an initial XLS-70 pass, but there are a couple of data-breaking issues that need fixing before merge.

Blockers:

  1. credential_ids serializes as CredentialIds instead of CredentialIDs. The struct-level rename_all = "PascalCase" gets the casing wrong. Needs explicit #[serde(rename = "CredentialIDs")] on all four transaction types that gained this field. This would silently break serialization against rippled.

  2. The Credential ledger object is missing the lsfAccepted flag (0x00010000). Without it the SDK can't tell whether a credential has been accepted by the subject, which is the whole point of the accept/unaccept lifecycle in XLS-70. Should use a CredentialFlag enum instead of NoFlags.

  3. Clippy fails with large_enum_variant on XRPLRequest after the LedgerEntry struct grew. Needs boxing.

  4. cargo fmt fails on two files.

Should fix:

  • No credential_type length validation (spec: non-empty, max 64 bytes / 128 hex chars). The codebase already does this for domain and uri.
  • No uri length validation in CredentialCreate (spec: max 256 bytes).
  • CredentialAuthorizationFields / CredentialAuthorization are duplicated identically in both transactions/deposit_preauth.rs and ledger/objects/deposit_preauth.rs. Define once, re-export.
  • No validation on AuthorizeCredentials array size (spec: 1-8 items).
  • No tests exercise credential_ids on the four modified transactions. A serde roundtrip would have caught the rename bug.

Comment thread src/models/transactions/payment.rs
Comment thread src/models/ledger/objects/credential.rs Outdated
Comment thread src/models/transactions/credential_create.rs
Comment thread src/models/transactions/deposit_preauth.rs Outdated
Comment thread src/models/transactions/credential_delete.rs
Comment thread src/models/transactions/deposit_preauth.rs

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

Took a close look at this. The structure follows existing patterns well and the scope is right for an initial XLS-70 pass, but there are a couple of data-breaking issues that need fixing before merge....

Implemented all blocker items and the listed should-fix items in commit 85430fe (with main fixes in dede078). This includes CredentialIDs serde casing fixes + tests, CredentialFlag::LsfAccepted, boxed XRPLRequest::LedgerEntry for clippy, credential_type/uri validations, shared credential authorization types, and DepositPreauth credential list size validation.

Copilot AI changed the title Add XLS-70 Credentials models and credential-based DepositPreauth support Add XLS-70 Credentials models and credential-based DepositPreauth support (with protocol-compatibility follow-up fixes) Mar 30, 2026
Copilot AI requested a review from e-desouza March 30, 2026 23:43

@e-desouza e-desouza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good progress. 9 of 10 original issues are fixed. But the fix commits introduced three new problems.

1. Test failure: credential_delete::test_valid_with_subject

The test sets account = "rSubmitter..." and subject = "rSubject..." (different addresses). The new validation correctly enforces account == subject || account == issuer, but this test wasn't updated to match. Fix: use the same address for both account and subject.

2. no_std build is broken

Three missing imports:

  • vec! macro not in scope in transactions/deposit_preauth.rs (lines 378, 400) and ledger/objects/deposit_preauth.rs (line 177). Needs use alloc::vec; in the test modules.
  • Box not in scope in requests/mod.rs (lines 150, 325). The Box<LedgerEntry> fix for large_enum_variant needs use alloc::boxed::Box; for no_std builds.

Reproduce with: cargo test --release --no-default-features --features embassy-rt,core,utils,wallet,models,helpers,websocket,json-rpc

3. cargo fmt still fails

Import ordering in src/models/ledger/objects/deposit_preauth.rs. Just needs a cargo fmt --all pass.

Comment thread src/models/transactions/credential_delete.rs
Comment thread src/models/requests/mod.rs
Comment thread src/models/ledger/objects/deposit_preauth.rs

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

Good progress. 9 of 10 original issues are fixed. But the fix commits introduced three new problems.

1. Test failure: credential_delete::test_valid_with_subject...

Fixed in ba48c88. I updated the failing credential_delete::test_valid_with_subject fixture, added missing no_std imports (alloc::boxed::Box in requests/mod.rs, alloc::vec in both DepositPreauth test modules), and reran cargo fmt --all plus the no-default-features test command.

Copilot AI changed the title Add XLS-70 Credentials models and credential-based DepositPreauth support (with protocol-compatibility follow-up fixes) Add XLS-70 Credentials models and credential-based DepositPreauth support (with protocol-compatibility and no_std follow-up fixes) Mar 30, 2026
Copilot AI requested a review from e-desouza March 30, 2026 23:59

@e-desouza e-desouza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed across three rounds. All issues from both review passes are fixed: serde CredentialIDs rename, typed lsfAccepted flag, credential_type validation, shared authorization types, CredentialDelete submitter checks, array bounds, no_std imports, and test fixture alignment. Ran the full test suite locally -- fmt, clippy, 614 default tests, 485 no_std tests, and 41 integration tests against a standalone rippled node. All green.

One thing for next time: the agent's sandbox needs firewall rules allowing outbound access to testnet.xrpl-labs.com, faucet.altnet.rippletest.net, and xrpl.org so it can run integration tests against the testnet faucet directly.

@e-desouza e-desouza marked this pull request as ready for review April 1, 2026 01:42
@pdp2121

pdp2121 commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator

/ai-review

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Several correctness issues flagged inline: a logic bug in CredentialDelete authorization that incorrectly rejects valid implicit-issuer cases (line 120), an unreachable else branch (line 140), doc/code contradiction on both-None (line 118), missing length validation for authorize_credentials in the ledger object, unvalidated credential_ids on Payment/AccountDelete/EscrowFinish/PaymentChannelClaim, and Cow<[T]> serde reliability concerns across multiple types. The authorize field change in DepositPreauth is also a semver-breaking API change that needs changelog/version attention.

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/models/transactions/credential_delete.rs
Comment thread src/models/transactions/credential_delete.rs Outdated
Comment thread src/models/transactions/credential_delete.rs Outdated
Comment thread src/models/transactions/credential_delete.rs Outdated
Comment thread src/models/ledger/objects/deposit_preauth.rs
Comment thread src/models/ledger/objects/deposit_preauth.rs
Comment thread src/models/requests/deposit_authorize.rs Outdated
Comment thread src/models/transactions/account_delete.rs Outdated
Comment thread src/models/transactions/payment.rs Outdated
e-desouza

This comment was marked as low quality.

Comment thread src/models/transactions/payment.rs Outdated
Comment on lines +50 to +52
pub previous_txn_id: Cow<'a, str>,
/// The index of the ledger containing the transaction that most recently modified this object.
pub previous_txn_lgr_seq: u32,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Given that the rust library is yet to be equipped with many ledger-objects in the future, this is a good time to refactor common fields such as these to prevent code duplication.

previous_txn_lgr_seq and previous_txn_id is found in a huge majority of the ledger objects. These fields should be refactored.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I left this unresolved because moving PreviousTxnID/PreviousTxnLgrSeq into ledger-object common fields would be a broader public-model refactor across many existing ledger objects, not just the XLS-70 Credential object. I addressed the concrete XLS-70 validation/test feedback in af71a30 and can handle this as a separate refactor if you want it in this PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tracked separately in #325.

This is broader than PR #154's XLS-70 credential changes because it involves cross-model public API/request-shape refactors:

  • ledger-object common metadata fields like PreviousTxnID / PreviousTxnLgrSeq
  • request ledger lookup fields via LookupByLedgerRequest

Leaving this PR focused on the XLS-70 compatibility fixes and handling the CommonFields refactor in the follow-up issue.

Comment thread src/models/ledger/objects/deposit_preauth.rs Outdated
pub credentials: Option<Vec<Cow<'a, str>>>,
/// The unique identifier of a ledger.
#[serde(flatten)]
pub ledger_lookup: Option<LookupByLedgerRequest<'a>>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This model can be further simplified by refactoring LookupByLedgerRequest inside CommonFields.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tracked separately in #325.

This is broader than PR #154's XLS-70 credential changes because it involves cross-model public API/request-shape refactors:

  • ledger-object common metadata fields like PreviousTxnID / PreviousTxnLgrSeq
  • request ledger lookup fields via LookupByLedgerRequest

Leaving this PR focused on the XLS-70 compatibility fixes and handling the CommonFields refactor in the follow-up issue.

Comment thread src/models/requests/ledger_entry.rs Outdated
Comment thread src/models/transactions/credential_accept.rs Outdated
Comment thread src/models/transactions/credential_accept.rs
Comment thread src/models/transactions/credential_accept.rs
Comment thread tests/proptest_credentials.rs Outdated
e-desouza added 14 commits June 25, 2026 18:47
…egration tests

Each credential_ids integration test now verifies both sides of the authorization gate:
- Submit WITHOUT credential_ids → assert tecNO_PERMISSION (gate is enforced)
- Submit WITH credential_ids → assert tesSUCCESS (gate is satisfied)

Previously tests only checked the happy path, which passed regardless of whether
the DepositAuth gate was actually enforced on the ledger.
Move hardcoded "4B5943" (hex for "KYC") into a single exported constant
in tests/common/constants.rs and reference it from all credential integration
tests. Eliminates 7 independent copies of the same magic string.
Distribute proptest blocks from the integration crate (proptest_credentials.rs)
into #[cfg(test)] mod tests in each model file. Unit tests belong with the
code under test, not in a separate integration binary that requires a running
node and adds noise to cargo test --release.

Removes tests/proptest_credentials.rs entirely; no behavioral change.
…dential_ids test

Replace raw sign_and_submit + ledger_accept with the test_transaction helper,
which asserts tesSUCCESS and advances the ledger atomically. Consistent with
how every other transaction submission is written in the integration suite.
…d and ledger_entry tests

Replace inline CredentialCreate + CredentialAccept + account_objects sequences
with the shared provision_credential helper. Removes ~50 lines of duplicated
setup code and makes the test intent clearer.
… serde roundtrips

Using ? with map_err gives proptest proper failure messages on serialize/deserialize
errors instead of a panic that can mask shrinking.
…xrpl-labs.com

xrpl-labs testnet is rate-limiting / timing out. Switch to the canonical
altnet endpoints:
- JSON-RPC: https://s.altnet.rippletest.net:51234
- WebSocket: wss://s.altnet.rippletest.net:51233

Faucet URL derivation in get_faucet_url already matches on "altnet", so
faucet routing works automatically with the new host.
…ests

`amount` (without `signature`) does not deliver XRP — it is the cumulative
claim amount for signature-validated off-chain claims. To deliver XRP directly
from the source account, `balance` is the correct field.

Negative gate test was getting tesSUCCESS (no delivery → DepositAuth not
triggered) instead of tecNO_PERMISSION. Using `balance` causes actual fund
delivery and the DepositAuth gate fires correctly.
Tests the full DepositAuth gate for direct XRP payments: negative assertion
(tecNO_PERMISSION without credentials) then positive (tesSUCCESS with
credential_ids). This is the highest-value gap vs rippled's DepositAuth_test.cpp.
…on tests

Scenarios added (from rippled Credentials_test.cpp):
  6. duplicate CredentialCreate → tecDUPLICATE
  7. double CredentialAccept → tecDUPLICATE
  8. CredentialCreate with uri field — uri stored on ledger object
  9. expired credential → tecEXPIRED on create; CredentialAccept after expiry → tecEXPIRED
- payment.rs: add missing ledger_accept() after negative submit (prevented
  positive test from seeing committed sequence number → tefPAST_SEQ risk)
- payment.rs: replace Payment::new() positional soup with struct literal
  (CLAUDE.md: prefer ..Default::default() over positional None args)
- credential.rs: saturating_sub(2) instead of - 2 to avoid u64 underflow
  on early-genesis standalone nodes
- credential.rs: use provision_credential() in test_credential_accept_duplicate
  instead of inlining create+accept manually
- credential.rs: remove redundant inline use imports (already at file scope)
- credential_create.rs / credential_accept.rs proptests: use "AB".repeat(len)
  with len in 1..=64 instead of single-char repeat — ensures even-length hex
  (valid byte-encodable strings) rather than odd-length strings that are
  technically undecodable as complete bytes
…tion

validate_credential_type: reject odd-length hex strings (cannot represent
whole bytes; rippled binary codec rejects them at serialization time).

validate_credential_ids: enforce exactly 64 hex chars per ID (credential IDs
are SHA-512Half ledger-object hashes: 256-bit = 32 bytes = 64 hex chars).

Update tests to use even-length non-hex strings ("NOTHEX") for non-hex error
assertions. Fix credential_delete proptest regex to produce even-length strings
only: ([0-9A-F]{2}){1,64} instead of [0-9A-F]{2,64}.

Add test_credential_type_odd_length_error test to explicitly cover the new
parity guard.
@e-desouza e-desouza force-pushed the copilot/xls-70-support-credentials branch from a7c2952 to f08feb5 Compare June 25, 2026 22:53
…redential_ids tests

Convert EscrowCreate/EscrowFinish, AccountDelete, PaymentChannelCreate/
PaymentChannelClaim positional constructors to struct-literal form with
..Default::default() in the new *_with_credential_ids test functions.
Comment thread src/models/transactions/credential_create.rs
Odd-length hex URIs cannot represent whole bytes and would be rejected
by rippled's binary codec. Added parity guard matching validate_credential_type.
Updated test fixture to use even-length non-hex string for the non-hex path.
Patel-Raj11
Patel-Raj11 previously approved these changes Jun 26, 2026
Comment on lines +1 to +7
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc fbc4b34a079b2bde9fb6af79a2d16814ab42060ff7d2987f8c150f8ddc132cde # shrinks to ct = "AAAA000A00A0A00A0A0A000", has_subject = false, has_issuer = false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did you intend to commit this file into the git worktree? Where is this test-file used?

As per my understanding, the rust SDK only has unit, integration and CLI tests

ckeshava
ckeshava previously approved these changes Jun 29, 2026
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.

Support Credentials (XLS-70)

5 participants