Skip to content

Commit 2bcb9f6

Browse files
committed
Update with SME review comments
1 parent 0f9338c commit 2bcb9f6

File tree

6 files changed

+33
-44
lines changed

6 files changed

+33
-44
lines changed

_code-samples/issue-credentials/js/accept_credential.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import dotenv from "dotenv";
44
import inquirer from "inquirer";
55
import { Client, Wallet } from "xrpl";
66
import { lookUpCredentials } from "./look_up_credentials.js";
7-
import { decodeHex } from "./decode_hex.js";
7+
import { hexToString } from "@xrplf/isomorphic/dist/utils/index.js";
88

99
const XRPL_SERVER = "wss://s.devnet.rippletest.net:51233"
1010

@@ -41,7 +41,7 @@ async function main() {
4141
);
4242

4343
const choices = pendingCredentials.map((cred, i) => ({
44-
name: `${i+1}) '${decodeHex(cred.CredentialType)}' issued by ${cred.Issuer}`,
44+
name: `${i+1}) '${hexToString(cred.CredentialType)}' issued by ${cred.Issuer}`,
4545
value: i,
4646
}));
4747
choices.unshift({ name: "0) No, quit.", value: -1 });

_code-samples/issue-credentials/js/credential.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import {
33
rippleTimeToISOTime,
44
isValidClassicAddress,
55
} from "xrpl";
6-
import { stringToHex } from "@xrplf/isomorphic/dist/utils/index.js";
6+
import { stringToHex, hexToString } from "@xrplf/isomorphic/dist/utils/index.js";
77

8-
import { decodeHex } from "./decode_hex.js";
98
import { ValueError } from "./errors.js";
109

1110
// Regex constants
12-
const CREDENTIAL_REGEX = /^[A-Za-z0-9_.-]{1,64}$/;
11+
const CREDENTIAL_REGEX = /^[A-Za-z0-9_.-]{1,128}$/;
1312
const URI_REGEX = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]{1,256}$/;
1413

1514
/**
@@ -93,8 +92,8 @@ export function credentialFromXrpl(entry) {
9392
const { Subject, CredentialType, URI, Expiration, Flags } = entry;
9493
return {
9594
subject: Subject,
96-
credential: decodeHex(CredentialType),
97-
uri: URI ? decodeHex(URI) : undefined,
95+
credential: hexToString(CredentialType),
96+
uri: URI ? hexToString(URI) : undefined,
9897
expiration: Expiration ? rippleTimeToISOTime(Expiration) : undefined,
9998
accepted: Boolean(Flags & 0x00010000), // lsfAccepted
10099
};

_code-samples/issue-credentials/js/decode_hex.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

_code-samples/issue-credentials/js/issuer_service.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ dotenv.config();
1818
async function initWallet() {
1919
let seed = process.env.ISSUER_ACCOUNT_SEED;
2020

21-
if (!seed) {
21+
if (!seed || seed.startsWith("<")) {
2222
const { seedInput } = await inquirer.prompt([
2323
{
2424
type: "password",
@@ -89,11 +89,11 @@ async function main() {
8989
URI: credXrpl.uri,
9090
Expiration: credXrpl.expiration,
9191
};
92-
const ccResponse = await client.submit(tx, { autofill: true, wallet });
92+
const ccResponse = await client.submitAndWait(tx, { autofill: true, wallet });
9393

94-
if (ccResponse.result.engine_result === "tecDUPLICATE") {
94+
if (ccResponse.result.meta.TransactionResult === "tecDUPLICATE") {
9595
throw new XRPLTxError(ccResponse, 409);
96-
} else if (ccResponse.result.engine_result !== "tesSUCCESS") {
96+
} else if (ccResponse.result.meta.TransactionResult !== "tesSUCCESS") {
9797
throw new XRPLTxError(ccResponse);
9898
}
9999

@@ -137,6 +137,7 @@ async function main() {
137137
issuer: wallet.address,
138138
credential_type: credential,
139139
},
140+
ledger_index: "validated",
140141
});
141142

142143
const tx = {
@@ -146,12 +147,12 @@ async function main() {
146147
CredentialType: credential,
147148
};
148149

149-
const cdResponse = await client.submit(tx, { autofill: true, wallet });
150-
if (cdResponse.result.engine_result === "tecNO_ENTRY") {
150+
const cdResponse = await client.submitAndWait(tx, { autofill: true, wallet });
151+
if (cdResponse.result.meta.TransactionResult === "tecNO_ENTRY") {
151152
// Usually this won't happen since we just checked for the credential,
152153
// but it's possible it got deleted since then.
153154
throw new XRPLTxError(cdResponse, 404);
154-
} else if (cdResponse.result.engine_result !== "tesSUCCESS") {
155+
} else if (cdResponse.result.meta.TransactionResult !== "tesSUCCESS") {
155156
throw new XRPLTxError(cdResponse);
156157
}
157158

_code-samples/issue-credentials/js/look_up_credentials.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export async function lookUpCredentials(client, issuer, subject, accepted = "bot
2222
command: "account_objects",
2323
account,
2424
type: "credential",
25+
ledger_index: "validated",
2526
};
2627

2728
// Fetch first page

docs/tutorials/javascript/build-apps/credential-issuing-service.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ _(Requires the Credentials amendment. {% not-enabled /%})_
1010
This tutorial demonstrates how to build and use a microservice that issues [Credentials](../../../concepts/decentralized-storage/credentials.md) on the XRP Ledger, in the form of a RESTlike API, using the [Express](https://expressjs.com/) framework for Node.js.
1111

1212

13+
## Prerequisites
14+
15+
To complete this tutorial, you should meet the following guidelines:
16+
17+
- You have [Node.js](https://nodejs.org/en/download/) v18 or higher installed.
18+
- You are somewhat familiar with modern JavaScript programming and have completed the [Get Started Using JavaScript tutorial](./get-started.md).
19+
- You have some understanding of the XRP Ledger, its capabilities, and of cryptocurrency in general. Ideally you have completed the [Basic XRPL guide](https://learn.xrpl.org/).
20+
21+
1322
## Setup
1423

1524
First, download the complete sample code for this tutorial from GitHub:
@@ -78,7 +87,7 @@ To request a credential, make a request such as the following:
7887

7988
{% tab label="Summary" %}
8089
* HTTP method: `POST`
81-
* URL: `http://localhost:3000/credential`
90+
* URL: `http://localhost:3005/credential`
8291
* Headers:
8392
* `Content-Type: application/json`
8493
* Request Body:
@@ -95,7 +104,7 @@ To request a credential, make a request such as the following:
95104

96105
{% tab label="cURL" %}
97106
```sh
98-
curl -H "Content-Type: application/json" -X POST -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential", "documents": {"reason": "please"}}' http://localhost:3000/credential
107+
curl -H "Content-Type: application/json" -X POST -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential", "documents": {"reason": "please"}}' http://localhost:3005/credential
99108
```
100109
{% /tab %}
101110

@@ -111,7 +120,7 @@ The parameters of the JSON request body should be as follows:
111120
| `expiration` | String - ISO8601 Datetime | No | The time after which the credential expires, such as `2025-12-31T00:00:00Z`. |
112121
| `uri` | String | No | Optional URI data to store with the credential. This data will become public on the XRP Ledger. If provided, this must be a string with minimum length 1 and max length 256, consisting of only characters that are valid in URIs, which are numbers, letters, and the following special characters: `-._~:/?#[]@!$&'()*+,;=%`. Conventionally, it should link to a Verifiable Credential document as defined by the W3C. |
113122

114-
This microservice immediately issues any credential that the user requests. A successful response from the API uses the HTTP status code `201 Created` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` or `ctid` value from the response to look up the transaction using an explorer such as [https://devnet.xrpl.org/](https://devnet.xrpl.org/).
123+
This microservice immediately issues any credential that the user requests. A successful response from the API uses the HTTP status code `201 Created` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` value from the response to look up the transaction using an explorer such as [https://devnet.xrpl.org/](https://devnet.xrpl.org/).
115124

116125
{% admonition type="warning" name="Differences from Production" %}For a real credential issuer, you would probably check the credential type and only issue specific types of credentials, or maybe even just one type. <br><br> If checking the user's documents requires human intervention or takes longer than the amount of time an API request should wait to respond, you would need to store credential requests to some kind of storage, like a SQL database. You might also want to add a separate method for admins (or automated processes) to reject or issue the credential after checking the documents.{% /admonition %}
117126

@@ -123,13 +132,13 @@ To show a list of credentials issued by the issuing account, make the following
123132

124133
{% tab label="Summary" %}
125134
* HTTP method: `GET`
126-
* URL: `http://localhost:3000/admin/credential`
135+
* URL: `http://localhost:3005/admin/credential`
127136
* Query parameters (optional): Use `?accepted=yes` to filter results to only credentials that the subject has accepted, or `?accepted=no` for credentials the user has not accepted.
128137
{% /tab %}
129138

130139
{% tab label="cURL" %}
131140
```sh
132-
curl http://localhost:3000/admin/credential
141+
curl http://localhost:3005/admin/credential
133142
```
134143
{% /tab %}
135144

@@ -183,7 +192,7 @@ To revoke an issued credential, make a request such as the following:
183192

184193
{% tab label="Summary" %}
185194
* HTTP method: `DELETE`
186-
* URL: `http://localhost:3000/admin/credential`
195+
* URL: `http://localhost:3005/admin/credential`
187196
* Headers:
188197
* `Content-Type: application/json`
189198
* Request Body:
@@ -197,7 +206,7 @@ To revoke an issued credential, make a request such as the following:
197206

198207
{% tab label="cURL" %}
199208
```sh
200-
curl -H "Content-Type: application/json" -X DELETE -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential"}' http://localhost:3000/admin/credential
209+
curl -H "Content-Type: application/json" -X DELETE -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential"}' http://localhost:3005/admin/credential
201210
```
202211
{% /tab %}
203212

@@ -210,7 +219,7 @@ The parameters of the JSON request body should be as follows:
210219
| `subject` | String - Address | Yes | The XRPL classic address of the subject of the credential to revoke. |
211220
| `credential` | String | Yes | The type of credential to revoke. This must match a credential type previously issued. |
212221

213-
A successful response from the API uses the HTTP status code `200 OK` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` or `ctid` value from the response to look up the transaction using an explorer.
222+
A successful response from the API uses the HTTP status code `200 OK` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` value from the response to look up the transaction using an explorer.
214223

215224
## Code Walkthrough
216225

@@ -223,7 +232,6 @@ The code for this tutorial is divided among the following files:
223232
| `errors.js` | Custom error classes that standardize how the server reports validation errors and XRPL transaction failures. |
224233
| `issuer_service.js` | Defines the microservice as an Express app, including API methods and error handling. |
225234
| `look_up_credentials.js` | A helper function for looking up Credentials tied to an account, including pagination and filtering, used by both the credential issuer and holder. |
226-
| `decode_hex.js` | A helper function for decoding hexadecimal into human-readable strings, used by both the credential issuer and holder. |
227235

228236
### accept_credential.js
229237

@@ -305,12 +313,6 @@ This code performs [pagination using markers](../../../references/http-websocket
305313

306314
{% code-snippet file="/_code-samples/issue-credentials/js/look_up_credentials.js" language="js" /%}
307315

308-
### decode_hex.js
309-
310-
This file implements conversion of hex strings to human-readable text using ASCII, where possible. If the hex can't be decoded, it returns the original text prefaced with `(BIN) ` as a graceful fallback instead of throwing an error. This is important when reading data from the XRP Ledger because other users and tools can create Credentials with arbitrary binary data which might not decode to actual text at all. Even though the microservice from this tutorial only creates Credentials that use a restricted subset of ASCII characters, it might need to read ledger data that was created with different tools and different rules. You might even want to put more restrictions or checks in place depending on how you use the data; for example, if you output the results to a webpage you should make sure to escape or strip HTML tags to avoid visual glitches or cross-site-scripting attacks.
311-
312-
{% code-snippet file="/_code-samples/issue-credentials/js/decode_hex.js" language="js" /%}
313-
314316
### credential.js
315317

316318
This file defines a set of helper functions that validate credential related input, verify request data, and convert between the issuer microservice's simplified Credential format and the XRP Ledger object representation. It throws typed errors on invalid input.
@@ -329,7 +331,7 @@ The function `validateCredentialRequest(...)` checks that the user input meets v
329331

330332
The `credentialFromXrpl(...)` function converts an XRPL ledger entry into a usable credential object (for example, converting the credential field from hexadecimal to a native string). The API methods that read data from the XRP Ledger use this function so that their output is formatted the same way as user input in the other API methods.
331333

332-
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="// Convert an XRPL ledger" before="Convert to an object" language="js" /%}
334+
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="// Convert an XRPL ledger" before="// Convert to an object" language="js" /%}
333335

334336
The `credentialToXrpl(...)` function returns an object which is formatted for submitting to the XRP Ledger:
335337

0 commit comments

Comments
 (0)