0070 XLS-70d: On-Chain Credentials #202
Replies: 6 comments 2 replies
-
Great Proposal. Just some thoughts in no order I would keep this all under the issuer. I don't own my drivers license or passport, thats exclusive property of the goverment. Same with a Financial Advisor approving accreditation status. That user cant take it or do anything with it. Intuitively I know the issuer and the user. I would look under the issuer, for the user's VC. But thats just me maybe there are examples where its reversed. I also think you should think about this in actual real world application. Take a DMV example. The issuer is the CA DMV. The verifier is the clerk, and the subject is me. The DMV needs to be able to add and remove clerks. That clerk is who is authorizing the VC. I need to know who authorized it. Under this each clerk is the issuer. Thats not really how I would structure this. Same for DEFI accreditation. The issuer is the organization, the verifier is the financial advisor, the subject is the client. I made an example how I would do this using smart contracts. https://github.com/Transia-RnD/xhs-library/blob/main/test/integration/did/did.test.ts I also wonder if URI is even necessary and the fact that it is not updatable makes me think it shouldn't be an actual URI. In the DMV example, I'm not going to store that PII information publicly. So it would most likely be an auth url. Which means that I can change the data anytime I want for the user, but also, sometimes url's change, maybe v1/v2 or maybe I just made a mistake and need to change them. I would store the Drivers License ID as the URI, then in whatever application I know where to look (the full url) if I need the details. But thats just me. I know there are others out there using these same principles. |
Beta Was this translation helpful? Give feedback.
-
How would a user know a |
Beta Was this translation helpful? Give feedback.
-
A minor update to this spec was published today, 2024-07-12. |
Beta Was this translation helpful? Give feedback.
-
A minor update to this spec was published today, 2024-07-23. The only parts updated were the |
Beta Was this translation helpful? Give feedback.
-
Per the new XLS Contributing process, it is my opinion that we have reached a "well-refined standard." As such, I propose that we move this discussion to a file (via #211) and work on final changes using additional PRs, for better change-tracking. Please comment here if you would like to object to moving this spec/discussion forward in the process into a DRAFT spec. (Note that per the Contributing guidelines, moving a spec into the DRAFT state does not mean any kind of endorsement, nor does it mean that this specification will become adopted. It is solely meant as a mechanism to enable better change tracking using PRs.) |
Beta Was this translation helpful? Give feedback.
-
Closing this discussion since the spec has been initially merged via #211. For further discussion or changes, please open a PR in this repo on XLS-70d. |
Beta Was this translation helpful? Give feedback.
-
An updated version of this spec can be found here: XLS-70d: On-Chain Credentials.
The earlier version follows:
On-Chain Credentials
Abstract
The XRPL DID (Digital Identifier) amendment (XLS-40) empowers users to manage their digital identities on the XRP Ledger. While this amendment adds support for on-chain identity management and simplifies off-chain credential usage, maximizing blockchain technology's full potential for credential handling requires on-chain solutions.
This document proposes a design to bridge this gap. It outlines the issuance, storage, and verification of credentials directly on the XRP Ledger, while still supporting the privacy needs of users.
On-chain credentialing can streamline various processes within the XRPL ecosystem. For example, financial institutions can issue credentials on the XRP Ledger, attesting to a user's identity and compliance. This eliminates the need for repeated KYC (Know Your Customer) checks across different platforms, fostering a smoother user experience. By enabling secure credential management, this proposal unlocks the potential for a new wave of trust-based applications within the XRPL ecosystem.
1. Overview
This design adds support for creating, accepting, and deleting credentials. It also extends the existing Deposit Authorization feature, allowing accounts to not only whitelist specific accounts, but also whitelist accounts with specific credentials.
This proposal only supports blocking the interactions that Deposit Authorization supports, such as direct payments. However, future proposals could add support for credential-gating in other parts of the XRPL, such as AMMs or lending pools.
We propose:
Credential
ledger objectDepositPreauth
ledger objectDepositPreauth
transactionCredential
ledger objectCredentialCreate
transaction typeCredentialAccept
transaction typeCredentialDelete
transaction typeThis feature will require an amendment, tentatively titled
featureCredentialAuth
.1.1. Background: DIDs and Verifiable Credentials (VCs)
A verifiable credential (VC), as defined by the W3C specification, is a secure and tamper-evident way to represent information about a subject, such as an individual, organization, or even an IoT device. These credentials are issued by a trusted entity and can be verified by third parties without directly involving the issuer at all.
For a clearer understanding of the relationship between DIDs (Decentralized Identifiers) and VCs, a real-world example is considered. A DID serves as a unique identifier for an entity, similar to a fingerprint or a photo of a person (their physical characteristics). In contrast, a VC acts as a verifiable piece of information associated with that DID, much like a driver's license or passport. When a third party needs to verify an identity claim, they can examine the VC presented by the entity and ensure it aligns with the corresponding DID (such as comparing the photo on a driver's license to the physical characteristics of the person to whom it supposedly belongs).
1.2. Terminology
Note: These definitions do not directly match the definitions listed in the W3C Verifiable Credentials spec. These terms are used in a slightly different way in the context of this spec.
1.3. Basic Flow
In this scenario, an authorizer/verifier account, Verity, is a regulated entity wants to ensure that she is only interacting with properly KYC'd accounts.
2. On-Ledger Object:
Credential
This object will be an on-chain representation of a credential, while ensuring that any sensitive details are kept off-chain, to protect the subject's privacy.
2.1. Fields
LedgerEntryType
string
UInt16
Credential
).Flags
number
UInt32
Subject
string
AccountID
Issuer
string
AccountID
CredentialType
string
Blob
Expiration
number
UInt32
URI
string
Blob
OwnerNode
string
UInt64
Account.PreviousTxnID
.PreviousTxnID
string
Hash256
PreviousTxnLgrSeqNumber
number
UInt32
2.1.1. Object ID
The ID of this object will be a hash that incorporates the
Subject
,Issuer
, andCredentialType
fields, combined with a unique space key forCredential
objects, which will be defined during implementation.2.1.2.
Flags
lsfAccepted
0x00010000
lsfAccepted
is on if the subject of the credential has accepted the credential. If it is off, the issuer has the reserve burden; if it is on, the burden moves to the subject.2.1.3.
CredentialType
This value is similar to the NFT
Taxon
value, where the value's meaning will be decided by the issuer. It may be the same as a claim in a VC, but could also represent a subset of such a claim.2.2. Account Deletion
The
Credential
object is not a deletion blocker.3. On-Ledger Object:
DepositPreauth
The
DepositPreauth
object tracks a preauthorization from one account to another. This object already exists on the XRPL, but is being extended as a part of this spec to also support credential preauthorization.3.1. Fields
As a reference, here are the existing fields for the
DepositPreauth
object.Account
string
AccountID
Authorize
string
AccountID
LedgerEntryType
string
UInt16
0x0070
, mapped to the string"DepositPreauth"
, indicates that this is aDepositPreauth
object.OwnerNode
string
UInt64
Account.PreviousTxnID
.PreviousTxnID
string
Hash256
PreviousTxnLgrSeqNumber
number
UInt32
We propose these modifications:
Authorize
string
AccountID
AuthorizeCredentials
array
STArray
3.1.1. Object ID
The ID of this object will be either a hash of the
Account
andAuthorize
fields (as it currently is), or a hash of theAccount
and the contents ofAuthorizeCredentials
fields, combined with the unique space key forDepositAuth
objects:0x0070
.3.1.2.
Authorize
This field is now optional, and a valid
DepositPreauth
object must either have theAuthorize
field or theAuthorizeCredentials
field.3.1.3.
AuthorizeCredentials
This field is an array of inner objects. The contents of these inner objects determine the credential(s) that are accepted.
If more than one credential is included in the list, all of those credentials must be included (effectively ANDing them together).
The list has a maximum size of 8 credentials.
Issuer
string
AccountID
CredentialType
string
Blob
4. Transaction:
DepositPreauth
This transaction currently creates and deletes
DepositPreauth
objects, thereby whitelisting and unwhitelisting accounts.This spec extends that functionality to also support whitelisting and unwhitelisting credentials.
4.1. Fields
As a reference, here are the existing fields for the
DepositPreauth
transaction:Authorize
string
AccountID
Unauthorize
string
AccountID
This proposal adds two new fields:
AuthorizeCredentials
array
STArray
UnauthorizeCredentials
array
STArray
4.1.1.
AuthorizeCredentials
andUnauthorizeCredentials
These fields follow the same rules outlined in section 3.1.3 for the
Credential
object'sAuthorizeCredentials
field.4.2. Failure Conditions
DepositPreauth
will still be obeyed.Authorize
,Unauthorize
,AuthorizeCredentials
, andUnauthorizeCredentials
are included (i.e. there must be exactly one of these fields included).UnauthorizeCredentials
is included in the transaction, the credential(s) are not currently authorized.AuthorizeCredentials
is included in the transaction:4.3. State Changes
If the transaction is successful:
DepositPreauth
object is created or deleted.5. Transaction:
CredentialCreate
This transaction creates a
Credential
object.There are two possible methods of doing so:
CredentialAccept
is used for the subject to accept the transaction.5.1. Fields
TransactionType
string
UInt16
CredentialCreate
).Account
string
AccountID
Subject
string
AccountID
Issuer
string
AccountID
CredentialType
string
Blob
Signature
string
Blob
Expiration
number
UInt32
URI
string
Blob
5.1.1.
Subject
andIssuer
Only one of these two fields must be specified. If
Subject
is specified,Account
is the issuer. IfIssuer
is specified,Account
is the subject.5.1.2.
Signature
If the subject is submitting the account, a signature must be included from the issuer attesting to the credential. This signature will sign a simple JSON that looks something like this:
5.2. Failure Conditions
Subject
andIssuer
are specified (i.e. there must be only one of the two fields).Subject
/Issuer
doesn't exist.Issuer
is included butSignature
is not.Expiration
is in the past.URI
field is too long (limit 256 bytes).5.3. State Changes
If the transaction is successful:
Credential
object is created.Issuer
==Account
, then thelsfAccepted
flag is enabled.6. Transaction:
CredentialAccept
This transaction transfers a credential from the
Issuer
to theAccount
. It is not a valid credential until it has been transferred.6.1. Fields
TransactionType
string
UInt16
CredentialAccept
).Account
string
AccountID
Issuer
string
AccountID
CredentialType
string
Blob
6.2. Failure Conditions
Account
orIssuer
doesn't exist.Account
doesn't have enough reserve for the object.6.3. State Changes
If the transaction is successful:
lsfAccepted
flag is turned on in the credential.Credential
object is moved from the issuer's owner directory to the subject's.7. Transaction:
CredentialDelete
This transaction deletes a
Credential
object.It can be executed by:
7.1. Fields
TransactionType
string
UInt16
CredentialDelete
).Account
string
AccountID
Subject
string
AccountID
Account
is assumed to be the subject.Issuer
string
AccountID
Account
is assumed to be the issuer.CredentialType
string
Blob
7.2. Failure Conditions
Subject
,Issuer
, andCredentialType
fields doesn't exist.Account
isn't the issuer or account, and the expiration hasn't passed.7.3. State Changes
If the transaction is successful:
Credential
object is deleted.8. Transactions: Common Fields
8.1. Fields
As a reference, here are the fields that all transactions currently have.
CredentialIDs
array
Vector256
Transactions that accept this field:
Destination
field (like a payment) or something that has to be accepted by the destination (like an escrow/check).8.2. Failure Conditions
CredentialIDs
isn't an object that exists.CredentialIDs
isn't aCredential
object.CredentialIDs
is an expiredCredential
object.CredentialIDs
is included, but the destination doesn't have Deposit Authorization set up, or the transaction doesn't have a destination (e.g.AccountSet
).CredentialIDs
are not authorized by the destination.Note: the transaction will still fail if too many credentials are included. The exact list must be provided.
8.3. State Changes
9. RPC:
deposit_authorized
The
deposit_authorized
RPC method already exists on the XRPL. This proposal suggests some modifications to also support permissioned DEX domains.9.1. Request Fields
source_account
string
destination_account
string
ledger_hash
string
ledger_index
string
ornumber
This proposal puts forward the following addition:
credentials
array
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.9.2. Response Fields
deposit_authorized
boolean
destination_account
string
source_account
string
ledger_hash
string
ledger_index
number
ledger_current_index
number
validated
boolean
10. Compliance with W3C Spec
This proposal prioritizes interoperability with the W3C Verifiable Credential (VC) spec. The existing verification flow for XRPL-based Verifiable Credentials remains the primary method:
did:xrpl:r....
). This document is stored in the account's DID object.However, this proposal introduces an optional enhancement. Within the
Credential
object, theURI
field can also point to the VC. It's important to note that this pointer is entirely optional, and the core verification process using DID resolution will still be the primary method for XRPL credential verification.It is recommended that anyone who holds a credential also have a DID object, to increase compliance with the W3C VC spec.
Note that the issuer of the on-chain
Credential
does not have to be the same as the issuer of the VC, and the on-chain object can instead essentially serve as an on-chain attestation from the issuer for the VC.11. Invariants
11.1. Reserves
The burden of reserve should be with the issuer, and the credential should be in the issuer's owner directory, if the
lsfAccepted
flag is off. The burden of reserve should be with the subject and the credential should be in the subject's owner directory, if thelsfAccepted
flag is on.11.2. The
DepositPreauth
ObjectA
DepositPreauth
ledger object always has exactly one of theAuthorize
field and theAuthorizeCredentials
field.If the
AuthorizeCredentials
field is included, the array contains between 1 and 8 credentials.12. Examples
In this example, a trusted issuer, Isabel, is issuing a credential that indicates that Alice is KYC'd. Verity is setting up her account to only interact with accounts that Isabel has attested to being properly KYC'd.
For ease of reading, some of the common transaction fields, such as signatures and public keys, have been left out of this example.
12.1.
CredentialCreate
Isabel creates the credential for Alice, after confirming her KYC status off-chain.
12.2. The
Credential
ObjectThis is the object created by the
CredentialCreate
transaction.12.3.
CredentialAccept
Alice accepts the credential, thereby making it valid.
12.4.
DepositPreauth
Verity sets up her account to only interact with accounts that Isabel has KYC'd.
12.5. Payments
This transaction will succeed, since Alice has attached the authorized credential from Isabel.
This transaction will fail, since Alice has not attached the authorized credential from Isabel. This is akin to trying to go through airport security without a form of ID.
This transaction will fail, since the attached credential isn't Bob's.
This transaction will fail, since Bob doesn't have a valid credential from Isabel.
This transaction will fail, since Bob doesn't have Deposit Authorization set up.
13. Security
13.1. Trust Assumptions
You need to trust the issuer to only be issuing valid credentials, and to not delete a credential without a legitimate reason.
13.2. Data Privacy
No private data needs to be stored on-chain (e.g. all the KYC data). The actual VC can still be stored off-chain. It could be stored at a link in the
URI
field.Appendix
Appendix A: FAQ
A.1: Do I have to only use credentials with the Deposit Authorization feature? Can I also use them for off-chain purposes?
You can use credentials for any purpose you'd like, not just for Deposit Authorization.
A.2: Do I need to use credentials for VCs or can I use them for other things?
You can use credentials for any purpose you'd like. The recommended usecase is VCs, but there are plenty of other usecases for them.
A.3: Why not use NFTs instead, with a certain issuer-taxon combo and the
tfTransferable
flag unset?Using NFTs instead wouldn't be as elegant, since it means that the NFT has to follow a specific format. It would make more sense for an off-ledger use-case, not directly on-ledger. In addition, an NFT serves different needs - e.g. NFTs don't have an expiration.
A.4: Can an issuer issue a credential for themselves?
Yes.
A.5: Can an issuer edit the details of a credential once it's created?
No, it would have to be deleted and recreated. This is similar to needing to get a new card if your license/passport expires. If the
Subject
,Issuer
, andCredentialType
stay the same, though, the object will still have the same ID as before.A.6: Why do I need to include the
CredentialIDs
field in my transaction? Shouldn't the XRPL be able to figure out whether I have a valid credential automatically?There is no way for the XRPL to iterate through just the list of credentials an account has; it can only iterate through the entire list of account objects. This is an unbounded list of objects (it could be millions of objects). Even just the list of accepted credentials could theoretically be millions of objects long. It could cause serious performance issues if the system had to iterate through the whole list.
It's much faster to have the credential ID included - it's easy to make sure that that's a valid credential, and check that it's an authorized credential.
A.7: Can a credential issuer delete their account?
Yes, even if they still have issued credentials in existence.
A.8: Does a credential issue have to have an on-chain account?
No, though they must have a valid keypair. If the second method of creating a credential is used (via a signature from the issuer), the issuer can still attest to a credential without having to have an account on-chain. This is to ensure closer compliance with the W3C Verifiable Credentials spec (see section 9).
A.9: How do I get a list of credentials that an issuer has issued?
The answer to that question is still being investigated. One option would be a Clio API.
A.10: Can I edit the list of credentials stored in
AuthorizeCredentials
?No, you have to delete the
DepositPreauth
object and recreate it with the new list.A.11: Does the list of credentials in
AuthorizeCredentials
have to be AND-ed together? Can I instead use it as an OR list (i.e. only provide one of the credentials instead of all)? Or some complex combination?No. You can OR credential(s) by putting them in separate
DepositPreauth
objects. For performance reasons, it is much easier to do a credential lookup if you need to have all of the credentials. Otherwise, you'd have to search the whole list. In addition, people who need to use this feature will likely not find the object reserve cost-prohibitive.A.12: Why are
CredentialCreate
andCredentialDelete
separate transactions?It's easier and clearer to have those be separate operations.
Beta Was this translation helpful? Give feedback.
All reactions