diff --git a/@l10n/ja/resources/contribute-documentation/tutorial-structure.md b/@l10n/ja/resources/contribute-documentation/tutorial-structure.md deleted file mode 100644 index 2125c2069e..0000000000 --- a/@l10n/ja/resources/contribute-documentation/tutorial-structure.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -html: tutorial-structure.html -parent: contribute-documentation.html -seo: - description: 一般的なチュートリアルの構成要素の要約です。 ---- -# チュートリアルの構成 - -各XRP Ledgerチュートリアルは、同一のフォーマットで構成されています。 - -1. チュートリアルで説明する機能の簡単な説明。 -2. コードを実行するための前提条件(必要な場合)、またはサンプルコードへのリンク。 -3. チュートリアルの機能の使用例。 -4. サンプルコードの解説と、そのスクリプトの特徴的な要素の紹介。 -5. 次のステップとして試すべき概念的な情報や優れたチュートリアルへのリンク。 - -セットアップ(前提条件)と使用方法とコード開発は分けて考えましょう。これらはそれぞれ異なる活動であり、それぞれ脳の異なる領域を動かします。この3つの要素を一度に考えようとすると、混乱につながります。 - -## 説明 - -![説明](/docs/img/tut-struct1.png) - -そのサンプルが何を示しているかを記載してください。可能であれば、各サンプルには関連する特定のタスクを達成するための手順を記述してください。(NFTの売却オファーの作成、売却オファーの受け入れ、売却オファーの削除など)。チュートリアルで説明されている内容を理解するのに十分なコンセプトに関する情報を記載し、必要であれば、追加情報へのリンクも記載します。 - -## 前提条件 - -![前提条件](/docs/img/tut-struct2.png) - -必要なソフトウェアと、チュートリアルを実行するために必要なすべてのサンプルコードへのリンクを提供します。必要であれば、サードパーティのツールの使い方を簡単に説明しますが、ユーザが自由に深く掘り下げることができるように、ソースとなるウェブサイトへのリンクを提供します。 - -## 使用例 - -![使用例](/docs/img/tut-struct3.png) - -チュートリアルのアプリケーションの完成した動作例を提供することから始めましょう。これは、ソフトウェアを使って問題を解決するチャンスです。 -  -チュートリアルの各ステップにはスクリーンショットを使用してください。これによって、ユーザは自分でコードを実行しなくてもチュートリアルを理解することができます。もちろん、コードを実行することが _望ましい_ ですが、これにりユーザに選択肢を与えることができます。 - -適切な条件におけるシナリオを記述してください。インターネットへの接続が途切れなければ、アプリケーションは問題なく動作するはずです。チュートリアルに関連しないトラブルシューティングの情報を提供しないでください。 - -## コード解説 - -![コード解説](/docs/img/tut-struct4.png) - -コードを1ブロックずつ見ていきましょう。既に説明したトピックを繰り返さないでください。サンプルコードには、HTML構文のような基本的な部分のプログラミング方法については、その実装に独自なものがない限り、詳細な説明はしないでください。 - -強調すべき重要なことは、XRPLとのやりとりはすべてトランザクションかリクエストであり、すべてのトランザクションとリクエストは本質的に同じであるということです。私たちが提供するサンプルコードは、トランザクションやリクエストを準備する方法と、返された結果を処理する方法を示しています。1つのトランザクションやリクエストをどのように送信しどのようなレスポンスを返すかを知ることは、他のトランザクションやリクエストの処理について非常に良いヒントとなります。 - -(技術的には、リクエストに似た第3のカテゴリがあります。[Subscriptionメソッド](../../docs/references/http-websocket-apis/public-api-methods/subscription-methods/index.md)をご覧ください)。 - -## 関連項目 - -![関連項目](/docs/img/tut-struct5.png) - -チュートリアルの最後には、追加の資料、概念的な情報、学習のにおいて有益な次のステップとなるチュートリアルへのリンクを提供します。 diff --git a/_code-samples/verify-credential/js/README.md b/_code-samples/verify-credential/js/README.md index e447a0ba2d..ab620d3dc3 100644 --- a/_code-samples/verify-credential/js/README.md +++ b/_code-samples/verify-credential/js/README.md @@ -6,27 +6,34 @@ Quick install & usage: ```sh npm install +node ./verify_credential.js ``` -`verify_credential.js` can also be used as a commandline utility. Full usage statement: +The output should look something like this: -```sh -$ ./verify_credential.js -h - -Usage: verify-credential [options] [issuer] [subject] [credential_type] - -Verify an XRPL credential - -Arguments: - issuer Credential issuer address as base58 (default: - "rEzikzbnH6FQJ2cCr4Bqmf6c3jyWLzkonS") - subject Credential subject (holder) address as base58 (default: - "rsYhHbanGpnYe3M6bsaMeJT5jnLTfDEzoA") - credential_type Credential type as string. (default: "my_credential") - -Options: - -b, --binary Use binary (hexadecimal) for credential_type - -n, --network {devnet,testnet,mainnet} Use the specified network for lookup (default: "devnet") - -q, --quiet Don't print log messages - -h, --help display help for command +```text +Looking up credential... +{ + "command": "ledger_entry", + "credential": { + "subject": "rsYhHbanGpnYe3M6bsaMeJT5jnLTfDEzoA", + "issuer": "rEzikzbnH6FQJ2cCr4Bqmf6c3jyWLzkonS", + "credential_type": "6D795F63726564656E7469616C" + }, + "ledger_index": "validated" +} +Found credential: +{ + "CredentialType": "6D795F63726564656E7469616C", + "Flags": 65536, + "Issuer": "rEzikzbnH6FQJ2cCr4Bqmf6c3jyWLzkonS", + "IssuerNode": "0", + "LedgerEntryType": "Credential", + "PreviousTxnID": "7D1257779E2D298C07C7E0C73CD446534B143FBD1F13DB268A878E40FD153B9A", + "PreviousTxnLgrSeq": 803275, + "Subject": "rsYhHbanGpnYe3M6bsaMeJT5jnLTfDEzoA", + "SubjectNode": "0", + "index": "9603F0E204A8B1C61823625682EB0ECE98A4ECF22FF46CD4845FA9BFA3606B24" +} +Credential is valid. ``` diff --git a/_code-samples/verify-credential/js/package.json b/_code-samples/verify-credential/js/package.json index 7072c02536..1282784be0 100644 --- a/_code-samples/verify-credential/js/package.json +++ b/_code-samples/verify-credential/js/package.json @@ -1,18 +1,9 @@ { - "name": "issuer_service", - "version": "1.0.0", - "description": "", - "main": "verify_credential.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", + "name": "verify_credentials", + "version": "2.0.0", + "description": "Sample code showing how to check if a credential on the XRPL exists and is valid.", "type": "module", "dependencies": { - "commander": "^13.1.0", - "winston": "^3.17.0", "xrpl": "^4.2.5" } } diff --git a/_code-samples/verify-credential/js/verify_credential.js b/_code-samples/verify-credential/js/verify_credential.js index cbb251f6c6..55e4f66a82 100755 --- a/_code-samples/verify-credential/js/verify_credential.js +++ b/_code-samples/verify-credential/js/verify_credential.js @@ -1,184 +1,68 @@ -#!/usr/bin/env node +import { Client, rippleTimeToISOTime, convertStringToHex } from "xrpl" -import { Command } from "commander"; -import { Client, rippleTimeToISOTime, convertStringToHex } from "xrpl"; -import winston from "winston"; +const client = new Client("wss://s.devnet.rippletest.net:51233") +await client.connect() -// Set up logging -------------------------------------------------------------- -// Use WARNING by default in case verify_credential is called from elsewhere. -const logger = winston.createLogger({ - level: "warn", - transports: [new winston.transports.Console()], - format: winston.format.simple(), -}); +const SUBJECT_ADDRESS = "rsYhHbanGpnYe3M6bsaMeJT5jnLTfDEzoA" +const ISSUER_ADDRESS = "rEzikzbnH6FQJ2cCr4Bqmf6c3jyWLzkonS" +const CREDENTIAL_TYPE = convertStringToHex("my_credential").toUpperCase() -// Define an error to throw when XRPL lookup fails unexpectedly -class XRPLLookupError extends Error { - constructor(error) { - super("XRPL look up error"); - this.name = "XRPLLookupError"; - this.body = error; - } +// Look up Credential ledger entry -------------------------------------------- +const ledgerEntryRequest = { + command: "ledger_entry", + credential: { + subject: SUBJECT_ADDRESS, + issuer: ISSUER_ADDRESS, + credential_type: CREDENTIAL_TYPE, + }, + ledger_index: "validated", } +console.log("Looking up credential...") +console.log(JSON.stringify(ledgerEntryRequest, null, 2)) -const CREDENTIAL_REGEX = /^[0-9A-F]{2,128}$/; -// See https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/credential#credential-flags -// to learn more about the lsfAccepted flag. -const LSF_ACCEPTED = 0x00010000; - -async function verifyCredential(client, issuer, subject, credentialType, binary=false) { - /** - * Check whether an XRPL account holds a specified credential, - * as of the most recently validated ledger. - * Parameters: - * client - Client for interacting with rippled servers. - * issuer - Address of the credential issuer, in base58. - * subject - Address of the credential holder/subject, in base58. - * credentialType - Credential type to check for as a string, - * which will be encoded as UTF-8 (1-128 characters long). - * binary - Specifies that the credential type is provided in hexadecimal format. - * You must provide the credential_type as input. - * Returns True if the account holds the specified, valid credential. - * Returns False if the credential is missing, expired, or not accepted. - */ - - // Encode credentialType as uppercase hex, if needed - let credentialTypeHex = ""; - if (binary) { - credentialTypeHex = credentialType.toUpperCase(); +let xrplResponse +try { + xrplResponse = await client.request(ledgerEntryRequest) +} catch (err) { + if (err.data?.error === "entryNotFound") { + console.error("Credential was not found") } else { - credentialTypeHex = convertStringToHex(credentialType).toUpperCase(); - logger.info(`Encoded credential_type as hex: ${credentialTypeHex}`); + console.error(err) } + process.exit(1) +} - if (credentialTypeHex.length % 2 !== 0 || !CREDENTIAL_REGEX.test(credentialTypeHex)) { - // Hexadecimal is always 2 chars per byte, so an odd length is invalid. - throw new Error("Credential type must be 128 characters as hexadecimal."); - } +const credential = xrplResponse.result.node +console.log("Found credential:") +console.log(JSON.stringify(credential, null, 2)) - // Perform XRPL lookup of Credential ledger entry -------------------------- - const ledgerEntryRequest = { - command: "ledger_entry", - credential: { - subject: subject, - issuer: issuer, - credential_type: credentialTypeHex, - }, +// Check if the credential has been accepted ---------------------------------- +const lsfAccepted = 0x00010000 +if (!(credential.Flags & lsfAccepted)) { + console.log("Credential is not accepted.") + process.exit(2) +} + +// Confirm that the credential is not expired --------------------------------- +if (credential.Expiration) { + const expirationTime = rippleTimeToISOTime(credential.Expiration) + console.log(`Credential has expiration: ${expirationTime}`) + console.log("Looking up validated ledger to check for expiration.") + + const ledgerResponse = await client.request({ + command: "ledger", ledger_index: "validated", - }; - logger.info("Looking up credential..."); - logger.info(JSON.stringify(ledgerEntryRequest, null, 2)); + }) - let xrplResponse; - try { - xrplResponse = await client.request(ledgerEntryRequest); - } catch (err) { - if (err.data?.error === "entryNotFound") { - logger.info("Credential was not found"); - return false; - } else { - // Other errors, for example invalidly specified addresses. - throw new XRPLLookupError(err?.data || err); - } - } + const closeTime = rippleTimeToISOTime(ledgerResponse.result.ledger.close_time) + console.log(`Most recent validated ledger was at: ${closeTime}`) - const credential = xrplResponse.result.node; - logger.info("Found credential:"); - logger.info(JSON.stringify(credential, null, 2)); - - // Check if the credential has been accepted --------------------------- - if (!(credential.Flags & LSF_ACCEPTED)) { - logger.info("Credential is not accepted."); - return false - } - - // Confirm that the credential is not expired ------------------------------ - if (credential.Expiration) { - const expirationTime = rippleTimeToISOTime(credential.Expiration); - logger.info(`Credential has expiration: ${expirationTime}`); - logger.info("Looking up validated ledger to check for expiration."); - - let ledgerResponse; - try { - ledgerResponse = await client.request({ - command: "ledger", - ledger_index: "validated", - }); - } catch (err) { - throw new XRPLLookupError(err?.data || err); - } - - const closeTime = rippleTimeToISOTime(ledgerResponse.result.ledger.close_time); - logger.info(`Most recent validated ledger is: ${closeTime}`); - - if (new Date(closeTime) > new Date(expirationTime)) { - logger.info("Credential is expired."); - return false; - } - } - - // Credential has passed all checks --------------------------------------- - logger.info("Credential is valid."); - return true; -} - - -// Commandline usage ----------------------------------------------------------- -async function main() { - // Websocket URLs of public servers - const NETWORKS = { - devnet: "wss://s.devnet.rippletest.net:51233", - testnet: "wss://s.altnet.rippletest.net:51233", - mainnet: "wss://xrplcluster.com/", - }; - - - // Parse arguments --------------------------------------------------------- - let result = false - const program = new Command(); - program - .name("verify-credential") - .description("Verify an XRPL credential") - .argument("[issuer]", "Credential issuer address as base58", "rEzikzbnH6FQJ2cCr4Bqmf6c3jyWLzkonS") - .argument("[subject]", "Credential subject (holder) address as base58", "rsYhHbanGpnYe3M6bsaMeJT5jnLTfDEzoA") - .argument("[credential_type]", "Credential type as string.", "my_credential") - .option("-b, --binary", "Use binary (hexadecimal) for credential_type") - .option( - `-n, --network {${Object.keys(NETWORKS)}}`, - "Use the specified network for lookup", - (value) => { - if (!Object.keys(NETWORKS).includes(value)) { - throw new Error(`Must be one of: ${Object.keys(NETWORKS)}`); - } - return value; - }, - "devnet" - ) - .option("-q, --quiet", "Don't print log messages") - // Call verify_credential with appropriate args ---------------------------- - .action(async (issuer, subject, credentialType, options) => { - const client = new Client(NETWORKS[options.network]); - await client.connect(); - - // Use INFO level by default when called from the commandline. - if (!options.quiet) { logger.level = "info" } - - // Commander.js automatically sets options.binary to a boolean: - // - If you provide -b or --binary on the command line then options.binary = true - // - If you do not provide it then options.binary = false - result = await verifyCredential(client, issuer, subject, credentialType, options.binary); - - await client.disconnect(); - }); - await program.parseAsync(process.argv); - - // Return a nonzero exit code if credential verification failed ----------- - if (!result) { - process.exit(1); + if (new Date(closeTime) > new Date(expirationTime)) { + console.log("Credential is expired.") + process.exit(3) } } -main().catch((err) => { - console.error("Fatal startup error:", err); - process.exit(1); -}); +// Credential has passed all checks ------------------------------------------- +console.log("Credential is valid.") +client.disconnect() diff --git a/docs/img/tut-struct1.png b/docs/img/tut-struct1.png deleted file mode 100644 index 0cff262096..0000000000 Binary files a/docs/img/tut-struct1.png and /dev/null differ diff --git a/docs/img/tut-struct2.png b/docs/img/tut-struct2.png deleted file mode 100644 index 21a57aecd9..0000000000 Binary files a/docs/img/tut-struct2.png and /dev/null differ diff --git a/docs/img/tut-struct3.png b/docs/img/tut-struct3.png deleted file mode 100644 index 9773b3d86c..0000000000 Binary files a/docs/img/tut-struct3.png and /dev/null differ diff --git a/docs/img/tut-struct4.png b/docs/img/tut-struct4.png deleted file mode 100644 index 0d6da07b99..0000000000 Binary files a/docs/img/tut-struct4.png and /dev/null differ diff --git a/docs/img/tut-struct5.png b/docs/img/tut-struct5.png deleted file mode 100644 index 07e8f22072..0000000000 Binary files a/docs/img/tut-struct5.png and /dev/null differ diff --git a/docs/tutorials/javascript/compliance/verify-credential.md b/docs/tutorials/javascript/compliance/verify-credential.md index b08be392f0..b5bea83651 100644 --- a/docs/tutorials/javascript/compliance/verify-credential.md +++ b/docs/tutorials/javascript/compliance/verify-credential.md @@ -4,8 +4,7 @@ seo: labels: - Credentials --- - -# Verify Credentials in Javascript +# Verify Credentials This tutorial describes how to verify that an account holds a valid [credential](/docs/concepts/decentralized-storage/credentials) on the XRP Ledger, which has different use cases depending on the type of credential and the meaning behind it. A few possible reasons to verify a credential include: @@ -13,297 +12,107 @@ This tutorial describes how to verify that an account holds a valid [credential] - Checking a person's professional certifications, after verifying their identity with a [DID](/docs/concepts/decentralized-storage/decentralized-identifiers). - Displaying a player's achievements in a blockchain-connected game. -This tutorial uses sample code in Javascript using the [xrpl-js library](../index.md). +## Goals + +By following this tutorial, you should learn how to: + +- Fetch a Credential entry from the ledger. +- Recognize if a credential has been accepted and when it has expired. ## Prerequisites -- You must have Node.js installed and know how to run Javascript code from the command line. Node.js v18 is required for xrpl.js. -- You should have a basic understanding of the XRP Ledger. -- The credential you want to verify should exist in the ledger already, and you should know the addresses of both the issuer and the holder, as well as the official credential type you want to check. - - For sample code showing how to create credentials, see [Build a Credential Issuing Service](../build-apps/credential-issuing-service.md). +To complete this tutorial, you should: +- Have a basic understanding of the XRP Ledger. +- Have an XRP Ledger client library, such as **xrpl.js**, installed. +- Know the issuer, subject, and credential type of the credential you want to verify. For purposes of this tutorial, you can use sample values of data that exists in the public network. + - For information on how to create your own credentials, see the [Build a Credential Issuing Service](../build-apps/credential-issuing-service.md) tutorial. -## Setup +## Source Code -First, download the complete sample code for this tutorial from GitHub: +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/verify-credential/" %}code samples section of this website's repository{% /repo-link %}. -- {% repo-link path="_code-samples/verify-credential/js/" %}Verify Credential sample code{% /repo-link %} +## Steps -Then, in the appropriate directory, install dependencies: - -```sh -npm install -``` - -This installs the appropriate version of the `xrpl.js` library and a few other dependencies. You can view all dependencies in the {% repo-link path="_code-samples/verify-credentials/js/package.json" %}`package.json`{% /repo-link %} file. - -## Overview - -The Verify Credential sample code consists of one file, `verify_credential.js`, and contains two main parts: - -1. A function, `verifyCredential(...)` which can be called with appropriate arguments to verify that a credential exists and is valid. This function can be imported into other code to be used as part of a larger application. -2. A commandline utility that can be used to verify any credential. - -## Usage - -To test that you have the code installed and working properly, you can run the commandline utility with no arguments to check the existence of a default credential on Devnet, such as: - -```sh -node verify_credential.js -``` - -If all goes well, you should see output such as the following: - -```text -info: Encoded credential_type as hex: 5465737443726564656E7469616C -info: Looking up credential... -info: { - "command": "ledger_entry", - "credential": { - "subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "credential_type": "5465737443726564656E7469616C" - }, - "ledger_index": "validated" -} -info: Found credential: -info: { - "CredentialType": "5465737443726564656E7469616C", - "Flags": 65536, - "Issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "IssuerNode": "0", - "LedgerEntryType": "Credential", - "PreviousTxnID": "B078C70D17820069BDF913146F9908A209B4E10794857A3E474F4C9C5A35CA6A", - "PreviousTxnLgrSeq": 1768183, - "Subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "SubjectNode": "0", - "index": "F2ACB7292C4F4ACB18010251F1653934DC17F06AA5BDE7F484F65B5A648D70CB" -} -info: Credential is valid. -``` - -If the code reports that the credential was not found when called with no arguments, it's possible that the example credential has been deleted or the Devnet has been reset. If you have another credential you can verify, you can provide the details as commandline arguments. For example: - -```sh -node verify_credential.js rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3 rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG TestCredential -``` - -A full usage statement is available with the `-h` flag. - -### Other Examples - -The following examples show other possible scenarios. The data for these examples may or may not still be present in Devnet. For example, anyone can delete an expired credential. +### 1. Install dependencies {% tabs %} -{% tab label="Valid with Expiration" %} -```text -$ ./verify_credential.js rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3 rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG TCredential777 +{% tab label="JavaScript" %} +From the code sample folder, use `npm` to install dependencies: -info: Encoded credential_type as hex: 5443726564656E7469616C373737 -info: Looking up credential... -info: { - "command": "ledger_entry", - "credential": { - "subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "credential_type": "5443726564656E7469616C373737" - }, - "ledger_index": "validated" -} -info: Found credential: -info: { - "CredentialType": "5443726564656E7469616C373737", - "Expiration": 798647105, - "Flags": 65536, - "Issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "IssuerNode": "0", - "LedgerEntryType": "Credential", - "PreviousTxnID": "D32F66D1446C810BF4E6310E21111C0CE027140292347F0C7A73322F08C07D7E", - "PreviousTxnLgrSeq": 2163057, - "Subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "SubjectNode": "0", - "URI": "746573745F757269", - "index": "6E2AF1756C22BF7DC3AA47AD303C985026585B54425E7FACFAD6CD1867DD39C2" -} -info: Credential has expiration: 2025-04-22T14:25:05.000Z -info: Looking up validated ledger to check for expiration. -info: Most recent validated ledger is: 2025-04-22T13:47:30.000Z -info: Credential is valid. -``` -{% /tab %} -{% tab label="Expired" %} -```text -$ ./verify_credential.js rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3 rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG TCredential777 - -info: Encoded credential_type as hex: 5443726564656E7469616C373737 -info: Looking up credential... -info: { - "command": "ledger_entry", - "credential": { - "subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "credential_type": "5443726564656E7469616C373737" - }, - "ledger_index": "validated" -} -info: Found credential: -info: { - "CredentialType": "5443726564656E7469616C373737", - "Expiration": 798647105, - "Flags": 65536, - "Issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "IssuerNode": "0", - "LedgerEntryType": "Credential", - "PreviousTxnID": "D32F66D1446C810BF4E6310E21111C0CE027140292347F0C7A73322F08C07D7E", - "PreviousTxnLgrSeq": 2163057, - "Subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "SubjectNode": "0", - "URI": "746573745F757269", - "index": "6E2AF1756C22BF7DC3AA47AD303C985026585B54425E7FACFAD6CD1867DD39C2" -} -info: Credential has expiration: 2025-04-22T14:25:05.000Z -info: Looking up validated ledger to check for expiration. -info: Most recent validated ledger is: 2025-04-22T14:40:00.000Z -info: Credential is expired. +```sh +npm i ``` {% /tab %} -{% tab label="Unaccepted" %} -```text -$ ./verify_credential.js rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3 rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG Tst9Credential +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies: -info: Encoded credential_type as hex: 5473743943726564656E7469616C -info: Looking up credential... -info: { - "command": "ledger_entry", - "credential": { - "subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "credential_type": "5473743943726564656E7469616C" - }, - "ledger_index": "validated" -} -info: Found credential: -info: { - "CredentialType": "5473743943726564656E7469616C", - "Expiration": 797007605, - "Flags": 0, - "Issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "IssuerNode": "0", - "LedgerEntryType": "Credential", - "PreviousTxnID": "A7A5F2AF66222B7ECDBC005477BDDCE35E1460FC53339A7800CBDE79DBBB6FE4", - "PreviousTxnLgrSeq": 1633091, - "Subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "SubjectNode": "0", - "URI": "746573745F757269", - "index": "4282469903F9046C8E559447CB1B17A18362E2AFC04399BB7516EFB0B1413EAB" -} -info: Credential is not accepted. -``` -{% /tab %} - -{% tab label="Hexadecimal Credential Type" %} -```text -$ ./verify_credential.js rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3 rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG 5473743343726564656E7469616C -b - -info: Looking up credential... -info: { - "command": "ledger_entry", - "credential": { - "subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "credential_type": "5473743343726564656E7469616C" - }, - "ledger_index": "validated" -} -info: Found credential: -info: { - "CredentialType": "5473743343726564656E7469616C", - "Flags": 65536, - "Issuer": "rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3", - "IssuerNode": "0", - "LedgerEntryType": "Credential", - "PreviousTxnID": "062DA0586A57A32220785159D98F5206A14C4B98F5A7D8A9BCDB6836E33C45FE", - "PreviousTxnLgrSeq": 1768019, - "Subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", - "SubjectNode": "0", - "URI": "746573745F757269", - "index": "8548D8DC544153044D17E38499F8CB4E00E40A93085FD979AB8B949806668843" -} -info: Credential is valid. +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt ``` {% /tab %} {% /tabs %} +### 2. Set up client and define constants -## Code Walkthrough +To get started, import the client library and instantiate an API client. You also need to specify the details of the credential you want to verify. -### 1. Initial setup +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" before="// Look up Credential" /%} +{% /tab %} +{% /tabs %} -The `verify_credential.js` file implements the code for this tutorial. -This file can be run as a commandline tool, so it starts with a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)). Then it imports the relevant dependencies, including the specific parts of the `xrpl.js` library: +### 3. Look up the credential -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" before="// Set up logging" /%} +Use the [ledger_entry method][] to request the credential, using the latest validated ledger version. The response includes the [Credential entry][] as it is stored in the ledger. -The next section of the code sets the default log level for messages that might be written to the console through the utility: +If the request fails with an `entryNotFound` error, then the specified credential doesn't exist in the ledger—maybe you got one of the values wrong or maybe the credential has been deleted. -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Set up logging" before="// Define an error to throw" /%} +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Look up Credential" before="// Check if the credential has been accepted" /%} +{% /tab %} +{% /tabs %} -Then it defines a type of exception to throw if something goes wrong when connecting to the XRP Ledger: +### 4. Check if the credential has been accepted -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Define an error to throw" before="const CREDENTIAL" /%} +Since a credential isn't valid until the subject has accepted it, you need to check if the credential has been accepted to know if it's valid. The accepted status of a credential is stored as a flag in the `Flags` field, so you use the bitwise-AND operator to see if that particular flag is enabled. -Finally, a regular expression to validate the credential format and the [lsfAccepted](../../../references/protocol/ledger-data/ledger-entry-types/credential.md#credential-flags) flag are defined as constants for use further on in the code. +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Check if the credential has been accepted" before="// Confirm that the credential is not expired" /%} +{% /tab %} +{% /tabs %} -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="const CREDENTIAL" before="async function verifyCredential" /%} +### 5. Check credential expiration -### 2. verifyCredential function +If the credential has an expiration time, you need to confirm that it has not passed, causing the credential to expire. As with all expirations in the XRP Ledger, expiration is compared with the official close time of the previous ledger, so use the [ledger method][] to fetch the ledger header and compare against the close time. -The `verifyCredential(...)` function performs the main work for this tutorial. The function definition and comments define the parameters: +If the credential does not have an expiration time, then it remains valid indefinitely. -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="async function verifyCredential" before="// Encode credentialType as uppercase hex" /%} +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Confirm that the credential is not expired" before="// Credential has passed" /%} +{% /tab %} +{% /tabs %} -The XRP Ledger APIs require the credential type to be hexadecimal, so it converts the user input if necessary: +### 6. Declare credential valid -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Encode credentialType as uppercase hex" before="// Perform XRPL lookup" /%} +If the credential has passed all checks to this point, it is valid. In summary, the checks were: -Next, it calls the [ledger_entry method](/docs/references/http-websocket-apis/public-api-methods/ledger-methods/ledger_entry#get-credential-entry) to look up the requested Credential ledger entry: +- The credential exists in the latest validated ledger. +- It has been accepted by the subject. +- It has not expired. -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Perform XRPL lookup" before="// Check if the credential has been accepted" /%} - -If it succeeds in finding the credential, the function continues by checking that the credential has been accepted by its holder. Since anyone can issue a credential to anyone else, a credential is only considered valid if its subject has accepted it. - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="Check if the credential has been accepted" before="// Confirm that the credential is not expired" /%} - -Then, if the credential has an expiration time, the function checks that the credential is not expired. If the credential has no expiration, this step can be skipped. A credential is officially considered expired if its expiration time is before the [official close time](/docs/concepts/ledgers/ledger-close-times) of the most recently validated ledger. This is more universal than comparing the expiration to your own local clock. Thus, the code uses the [ledger method][] to look up the most recently validated ledger: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Confirm that the credential is not expired" before="// Credential has passed all checks" /%} - -If none of the checks up to this point have returned a `false` value, then the credential must be valid. This concludes the `verifyCredential(...)` function: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Credential has passed all checks" before="// Commandline usage" /%} - -### 3. Commandline interface - -This file also implements a commandline utility which runs when the file is executed directly as a Node.js script. Some variables, such as the set of available networks, are only needed for this mode: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Commandline usage" before="// Parse arguments" /%} - -Then it uses the [commander package](https://www.npmjs.com/package/commander) to define and parse the arguments that the user can pass from the commandline: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Parse arguments" before="// Call verify_credential" /%} - -After parsing the commandline args, it sets the appropriate values and passes them to `verifyCredential(...)` to perform the credential verification: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Call verify_credential" before="// Return a nonzero exit code" /%} - -It returns a nonzero exit code if this credential was not verified. This can be useful for shell scripts: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Return a nonzero exit code" before="main().catch" /%} - -Finally, the code runs the `main()` function: - -{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="main().catch" /%} +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Credential has passed" /%} +{% /tab %} +{% /tabs %} ## Next Steps @@ -316,5 +125,11 @@ Now that you know how to use `xrpl.js` to verify credentials, you can try buildi ## See Also - [Verify Credentials in Python](../../python/compliance/verify-credential.md) +- **References:** + - API methods: + - [ledger_entry method][] + - [ledger method][] + - Ledger entries + - [Credential entry][] {% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/redirects.yaml b/redirects.yaml index dffb961e6e..0ec2eaab96 100644 --- a/redirects.yaml +++ b/redirects.yaml @@ -1,3 +1,6 @@ +/resources/contribute-documentation/tutorial-structure/: + to: /resources/contribute-documentation/tutorial-guidelines/ + type: 301 /docs/references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types/: to: /docs/references/protocol/transactions/pseudo-transaction-types/ type: 301 @@ -1667,7 +1670,7 @@ tutorial-guidelines.html: to: /resources/contribute-documentation/tutorial-guidelines type: 301 tutorial-structure.html: - to: /resources/contribute-documentation/tutorial-structure + to: /resources/contribute-documentation/tutorial-guidelines type: 301 report-a-scam.html: to: /contributing/report-a-scam @@ -2356,6 +2359,9 @@ code_of_conduct.ja: type: 301 # Japanese +/ja/resources/contribute-documentation/tutorial-structure/: + to: /ja/resources/contribute-documentation/tutorial-guidelines/ + type: 301 /ja/docs/references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types/: to: /ja/docs/references/protocol/transactions/pseudo-transaction-types/ type: 301 @@ -4016,7 +4022,7 @@ code_of_conduct.ja: to: /ja/resources/contribute-documentation/tutorial-guidelines type: 301 /ja/tutorial-structure.html: - to: /ja/resources/contribute-documentation/tutorial-structure + to: /ja/resources/contribute-documentation/tutorial-guidelines type: 301 /ja/report-a-scam.html: to: /ja/contributing/report-a-scam diff --git a/redocly.yaml b/redocly.yaml index 56213d10da..7ad4789b83 100644 --- a/redocly.yaml +++ b/redocly.yaml @@ -5,6 +5,7 @@ ignore: - _code-samples/**/README.md - _code-samples/create-amm/ts/tsconfig.json - resources/contribute-blog/_blog-template.md + - resources/contribute-documentation/_tutorial-template.md l10n: defaultLocale: en-US locales: diff --git a/resources/contribute-documentation/_tutorial-template.md b/resources/contribute-documentation/_tutorial-template.md new file mode 100644 index 0000000000..8adcff5cce --- /dev/null +++ b/resources/contribute-documentation/_tutorial-template.md @@ -0,0 +1,97 @@ +--- +seo: + description: TODO +--- +# Tutorial Template + +This tutorial shows how to ***TODO: add summary and contextual information. See [Tutorial Guidelines](./tutorial-guidelines.md) for explanation. Parts that you should modify or remove are in bold-italic with the letters TODO.*** + + +## Goals + +By following this tutorial, you should learn how to: + +- ***TODO: goals of the tutorial.*** +- ***TODO: If the tutorial includes a graphical interface, include a screenshot of the final product here.*** + +## Prerequisites + +To complete this tutorial, you should: + +- Have a basic understanding of the XRP Ledger. +- Have an XRP Ledger client library, such as **xrpl.js**, installed. +- ***TODO: add and adjust prerequisites as required.*** + + +## Source Code + +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/" %}code samples section of this website's repository{% /repo-link %}. + + +## Usage + +***TODO: For single-file scripts that perform a linear set of steps without user input, omit the Usage section. For sample apps, provide some instructions on how to use the app.*** + + +## Steps + +### 1️1. Install dependencies + +{% tabs %} +{% tab label="JavaScript" %} +From the code sample folder, use `npm` to install dependencies: + +```sh +npm i +``` +{% /tab %} + +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies: + +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` +{% /tab %} +{% /tabs %} + +### 2. Connect and get account(s) + +To get started, import the client library and instantiate an API client. ***TODO: mention other details in this section, such as getting wallets/test accounts.*** + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/TODO/js/TODO.js" language="js" before="// TODO" /%} +{% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/TODO/py/TODO.py" language="py" before="# TODO" /%} +{% /tab %} +{% /tabs %} + + +### 3. ***TODO: more steps, as necessary*** + +***TODO: a brief description of what happens in this step*** + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/TODO/js/TODO.js" language="js" from="// TODO" before="// TODO next" /%} +{% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/TODO/py/TODO.py" language="py" from="# TODO" before="# TODO next" /%} +{% /tab %} +{% /tabs %} + +***TODO: optional text following the code to describe expected outputs or details you should note for later.*** + + +## See Also + +***TODO: link related tutorials, references, and use cases.*** + + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/resources/contribute-documentation/tutorial-guidelines.md b/resources/contribute-documentation/tutorial-guidelines.md index f98f6b16e3..4dcf972483 100644 --- a/resources/contribute-documentation/tutorial-guidelines.md +++ b/resources/contribute-documentation/tutorial-guidelines.md @@ -1,84 +1,208 @@ --- -html: tutorial-guidelines.html -parent: contribute-documentation.html seo: description: Learn how this site's tutorials are structured and guidelines for contributing quality tutorials. --- # Tutorial Guidelines -We are creating a modular tutorial framework that allows developers to learn how transactions and requests work on the XRP Ledger. Developers can review the modules to learn about business solutions, and potentially repurpose the scripts in their own applications. +We are creating a repository of tutorials and functional code samples that show how various features of the XRP Ledger work. Developers and large language models (LLMs) can use these tutorials and their associated code samples to learn about business solutions, and copy or adapt the scripts for use in their own applications. + +The guidelines on this page don't need to be strictly enforced. It's OK to diverge from them in cases where you have good reason to. + +## Rationale + +The main purpose of tutorials is to provide **sample code** alongside natural-language text to further explain what the code does. This serves multiple purposes: + +- Developers can copy and paste the sample code into their own applications. +- Large language models (LLMs) can use this as training data to generate high-quality code for use with the XRP Ledger. + +Conceptual information is sometimes necessary, but tutorials are not the place to discuss concepts at length. A tutorial should include a few sentences throughout to help provide context for the action occurring in the code, and should link out to concept and reference pages to provide background reading. + +LLMs are increasingly being used in software development. To assist users of these tools, we would like to provide many working code samples that demonstrate best practices and follow consistent structure and style. Tutorials that have descriptions of what the code does alongside matching code snippets help LLMs develop the correct associations between terms used in natural language and programming language, hopefully leading to more accurate results from code generation. + +## Recommended Tutorial Structure + +Use the {% repo-link path="/resources/contribute-documentation/_tutorial-template.md" %}`_tutorial-template.md` file{% /repo-link %} as a starting point with typical headers and contents expected for a new tutorial. See below for recommendations of what type of content to include in each section. + +### Introduction + +Immediately after the title, introduce the tutorial with a brief introduction. Using 2-3 sentences, provide contextual information of _what_, _why_, or _when_ you would need to use the tutorial. Do not provide too much information—leave those details for concepts or use case articles—instead, "backlink" to conceptual documentation for core concepts and features demonstrated by the tutorial. + +### Prerequisites + +The prerequisites help readers recognize when they are in the right place and provide background reading in case there are other topics they need to understand first. Example pre-requisites may include: + +- Knowledge and learning background, especially tutorials that this one builds on top of. +- Dev environment setup, especially basic depedencies such as your xrpl client library. + - Do not include dependencies that are specific to this tutorial here, because people tend to skim/gloss over this section. For dependencies specific to this tutorial, include them in the steps later. +- Specific on-chain structures that need to be in place in advance. For example, to trade against an AMM, the AMM must exist in the ledger. +- Amendments that need to be enabled for this tutorial. Use an amendment disclaimer component to show the Mainnet status of the amendment. + +### Source Code + +Update the path in the `{% repo-link ... %}` component to link to the source files for this tutorial specifically. This component is designed to adjust based on the environment, so for example previews link to the code on the preview branch... although it doesn't fully work as of this writing (2025-12-15). + +### Usage + +Include a Usage section only for **sample app tutorials**. Provide a "Usage" section if this tutorial's sample code: + +- Has a GUI with multiple buttons/inputs +- Is a commandline utility with multiple options/parameters +- Consists of multiple scripts that need to be run in a specific order + +If there's a user interface, you can also embed a video demonstrating usage of the sample app. + +For single-file scripts that perform a linear set of steps without user input, omit the Usage section. + +### Steps + +Using numbered steps, walk through the entire code sample. Numbered steps help to orient readers to the structure and serve to identify the core part of the tutorial. If you change the number or order of steps, remember to update the step numbers and update any links or mentions of specific steps, too. + +Each step should have a heading in the imperative, in sentence case. Beneath the heading should be a sentence or two introducing the action being taken at this stage of the code sample in more detail. The code samples should be in tabs per programming language. + +Each "step" of the tutorial should correspond to one code snippet (with tabs by programming language). There can be exceptions, for example if one programming language needs additional code in a separate place to make the same functionality work. Most tutorials should have 3-7 steps. If the tutorial has fewer, maybe it doesn't need to be a tutorial, or maybe you should go into more detail. If it has more, consider splitting it into multiple tutorials. + +#### 1. Install dependencies + +Many tutorials start with this step, because people tend to gloss over requirements mentioned in the prerequisites. It may also include dependencies that are specific to this tutorial rather than general to XRPL development as a whole. + +#### 2. Connect and get account(s) + +In many tutorials, this step is the first one to show source code from the script itself. The scope of this step typically includes all of the following: + +- Importing necessary dependencies. +- Instantiating a client and connecting to the network. +- Defining hard-coded values and constants. +- Deriving wallets or funding accounts via a faucet. + +If the scope of the code this covers is more than 15-20 lines, you may want to split it up into multiple steps. + +Use `{% code-snippet ... %}` tags instead of copy-paste to display the code samples, so that you don't have to manually keep the code in the doc synchronized with small changes to the code sample. To facilitate this, use `from=` and `before=` strings based on unique comments in the code. The first code snippet should omit `from=` and the last should omit `before=`. + +#### Additional notes on steps + +If a script depends on certain ledger data already existing (for example, you are supposed to create it with a different script), the script should have an explicit step to check for the existence of that data. You should also mention the requirement in the [**Prerequisites**](#prerequisites) section. + +Each step should directly continue the code sample from the previous step without skipping anything, to the extent possible. + +Optionally, you can provide additional text after the code snippet, such as an explanation of the expected output from this step, or details that you should note down for later. + +If the code snippet calls an API method, link to the relevant reference documentation. If you include the common links file, you can generally do this with an automatic link such as the `[ledger method][]`, which turns into the [ledger method][]. + +### See Also + +At the end of the tutorial, provide links to additional resources that would be a sensible next step in the learning journey. This could be more tutorials, use cases, or other pages. It's also a good idea to add links here to reference documentation for any API methods, transaction types, and ledger entries used in the tutorial—even though those links should be redundant with links scattered throughout the text of the tutorial. -# Rationale +## Sample Code Guidelines -What a developer wants comes down to two things: +Sample code is well commented scripts, snippets, or applications that illustrate common usage patterns and best practices. Advanced users can typically scan the example and use it immediately without a formal tutorial. Not every piece of sample code needs to be associated with a tutorial, but most tutorials will have a piece of sample code that serves as the basis for that tutorial. -1. Sample code snippets they can copy and paste into their own applications. -2. Complete API reference documentation. +The XRPL.org maintainers are generally committed to providing sample code in both **JavaScript** and **Python** using the official client libraries for those languages. The site may occasionally provide more, depending on what the community needs and provides. This site is open-source, so if you want to maintain examples in other languages, feel free to volunteer! -Keep the conceptual information to a minimum – only the information necessary to complete the tutorial. For background or deeper understanding, provide links to the conceptual topics at the end of the tutorial, if needed. +However, due to the security concerns of using third party libraries, it may take a while to accept contributions in other programming languages. -Modular tutorials follow Malcolm Knowles’ six assumptions for designing adult learning: +### Folder Layout -1. Adults need to know why they need to learn something. -2. Adults need to build on their experience. -3. Adults have a need to feel responsible for their learning. -4. Adults are ready to learn if training solves an immediate problem. -5. Adults want their training to be problem focused. -6. Adults learn best when motivation comes intrinsically. +Sample code should be provided in the `_code-samples/` folder at the top of this website's source repository, with separate subfolders by programming language. There should be a `README.md` file for each code sample _above_ the language folders describing what the code sample does in general, _and_ a `README.md` in each programming language folder describing how to install and run that code sample specifically. -Add into that Ralph Smedley’s quote, “We learn best in moments of enjoyment.” Taking a lighter touch helps to relax the learner so that the material flows into their brain with less resistance. +For example: +``` +- _code_samples/issue-credentials/ + - js/ + - README.md + - issue-credential.js + - package.json + - py/ + - README.md + - issue_credential.py + - requirements.txt + - README.md +``` -# Sample Code vs. Tasks vs. Concepts vs. Tutorials +This information is used to populate the [Code Samples](/resources/code-samples/) page. The outer readme's header is the title of the code sample on that page, and the first paragraph is the description. -To date, there have been some blurred lines where different types of documentation show up as _Tutorials_. Here are some comparisons that help define the distinction. +### Comments +Comments are an important part of readable, accurate code, but don't go overboard. The text of a tutorial may be translated into other languages such as Japanese, while the sample code is kept the same, so don't include any critical information _only_ in the comments. -## Sample Code +Use comments to separate out logical sections of the sample code. You can these comments as markers so that the `{% code-snippet ... %}` Markdoc component shows only the relevant section at a time in a tutorial. -Sample code is well commented snippets or applications that illustrate best practices for implementing a feature of the API. Sample code is modular and reusable with little customization required. +### Sample Code Types -Sample code is desirable, because advanced users can typically scan the example and use it immediately without a formal tutorial. It can also be used by others as a basis for tutorials. Sample code developers can focus on what they do well, while technical writers and support personnel can use the samples to create quality training materials. +Sample code can take many forms, such as the following: +- **Sample Application** - A fully functional program that accepts user input to perform tasks with some flexibility. It may have a graphical user interface or only a commandline interface. Complete sample code for different stages of application development may be provided, but is not recommended because it's more work than it's worth. +- **Script** - A simple program that performs a predetermined set of tasks, often using hard-coded values, with minimal branching. These scripts are not expected to perform robust error handling; they typically exit when an error occurs. +- **Snippet** - A self-contained function or piece of code that demonstrates the best practices for doing one thing, but is not a complete program. Snippets are most likely to _not_ have an associated tutorial. -## Tasks +All three types of sample code have their time and place. However, for most of this website's tutorials, _scripts_ are preferred for the following reasons: -Tasks are step-by-step instructions for how to accomplish a specific result. For example, “Installing rippled on a Red Hat Linux Server.” Task documentation is not intended to be particularly educational. It frequently describes tasks that are only performed one time per implementation, or maintenance tasks that always follow a familiar pattern. Tasks provide troubleshooting guidance, since there are likely variables that the user must adjust based on the specifics of their use case. +- They demonstrate the relevant, XRPL-specific functionality with minimal distractions. +- You can run a script to demonstrate that it works and is accurate. +- Scripts are easier to create and have a lesser maintenance burden than more complex apps. +### JSON-RPC / WebSocket / Commandline Examples as Sample Code -## Concepts +Some legacy tutorials show example requests and responses using WebSocket, JSON-RPC APIs, or the `rippled` commandline. These are not recommended, for the following reasons: -Conceptual information describes elements of the API, how they work, and when to use them. If a tutorial requires lengthy explanations before or during the programming tasks, consider how you might separate the exposition into a new topic, or link to existing topics that set the proper context. +- People (or LLMs) who copy these formats tend to end up submitting their secret keys to public servers, which is extremely insecure. +- Many tutorials involve steps where you need application logic that can't be represented in API requests/responses. You end up with code in other programming languages anyway, or pseudocode, or just steps that are missing examples of how to do critical work. +- The API references already provide examples in these formats. -For example, three paragraphs of context and a single line of code would be a concept, not a tutorial. +If you do have good reason to provide commandline, WebSocket, or JSON-RPC examples, show both the request and an example response in separate code blocks. +### Dependencies -## Tutorials +Dependencies can be a source of maintenance burden, because you need to stay up-to-date with security fixes and breaking changes to the dependencies. On the other hand, reimplementing common utilities in every code sample is its own maintenance burden, and it's even worse to "roll your own" security-sensitive code. Some users may be working on codebases that are locked into competing/incompatible dependencies, which can make it harder to adapt your code to their situation; the more dependencies you have, the more likely this is to occur. -Tutorials begin with sample code that illustrates best practices for implementing a feature. They take the developer step-by-step through the development process, explaining the purpose of each block of code. +Some guidelines: -Tutorials further combine a number of features to work together to solve a business problem. They describe the straightforward sunny day path to complete a task. Then, the tutorial might suggest modifications that let the developer try several different scenarios. Due to their focus on a certain limited scope of behavior, tutorials should not require extensive troubleshooting information. +1. Prefer standard library functions to third-party libraries, even if they're not quite as convenient to use. + - Use third-party libraries when they're _significantly_ more convenient than the standard library. For example, [even Python's official documentation recommends using the Requests lib instead of `urllib.request`](https://docs.python.org/3/library/urllib.request.html#module-urllib.request). + - When updating old code samples, look for cases where dependencies can be eliminated because the standard library has grown to encompass functionality that previously needed a library. +2. Implement your own functions when they're small and not security-sensitive; use libraries for complex or security-sensitive functions. +3. Prefer widely-used, actively maintained libraries. +### General Guidelines and Best Practices -## Use Cases +The following guidelines apply for XRP Ledger code samples regardless of language: -Use cases describe how to pull together multiple features to create a practical application that solves a business problem. They provide context and assistance with the decision making process, then provide links to the appropriate topics for each step of implementation. +- Don't hardcode secret keys, even example keys that don't hold real money. Instead, do any of the following: + - Fund a new account using the faucet. + - Prompt the user to paste the seed of the account they want to use. + - Read the secret key from an environment variable. +- Use `client` as the name for the API client instance. +- Print output to the console, especially before doing any network operations such as calling API methods or submitting transactions. +- Use the client library's "submit and wait" function when sending transactions. Autofill, sign, and submit the transaction all in one call. +- Use tabs for code samples even if you only have a code sample in one language. +- When making WebSocket/JSON-RPC API calls, use the latest API version and the `validated` ledger. +### Language-specific Guidelines -# Tutorial Components +{% tabs %} -This section describes the elements of the modular tutorials used on XRPL.org. +{% tab label="JavaScript" %} +JavaScript code samples should: +- Use xrpl.js as the XRPL client library. +- Provide a `package.json` file that specifies `"type": "module"` and any relevant dependencies. +- Use **ES Module** syntax such as `import { Client } from "xrpl"`, not Common JS syntax such as `require("xrpl")`. +- Use `await` instead of `.done(...)` or `.then(...)` +- Follow [**JavaScript Standard Style**](https://standardjs.com). +- Be compatible with Node.js versions that are currently in maintenance (security) support. + - Preferably, be compatible with most widely-used web browsers too. +- When writing JSON objects to the console, use `JSON.stringify(example_object, null, 2)` so that Node.js doesn't skip the interesting inner parts of the object. +{% /tab %} -## Sample Application +{% tab label="Python" %} +Python code samples should: -XRPL tutorial code samples are modular in nature. For example, Script 1 demonstrates how to create a test account, access the XRP Ledger, and transfer XRP between accounts. Any further samples can reuse the functions in Script 1. +- Use xrpl-py as the XRPL client library +- Provide a `requirements.txt` file with relevant dependencies. +- Use the `JsonRpcClient` unless asynchronous functionality is needed. +- Follow [**Black Style**](https://black.readthedocs.io/en/stable/). +- Be compatible with Python versions that are currently in maintenance (security) support. +{% /tab %} -Create a new script with the specific, minimal function code required to demonstrate the practical solution to a business problem. The examples should be incremental, with just enough behaviors to illustrate a business process. - -For example, the first NFT tutorial shows how to mint, retrieve, and burn an NFT. The next tutorial shows how to create and accept a sell offer, and create and accept a buy offer. - -Don’t focus too much on the UX of the application, unless the look and feel is pertinent to the topic. Use the standard CSS file with the look and feel for all of the tutorials. - -Reuse the code from other modules when possible. There might be situations where you need to modify the behavior from an earlier module. You can either overload the function name or modify the module and save it with a different name. +{% /tabs %} diff --git a/resources/contribute-documentation/tutorial-structure.md b/resources/contribute-documentation/tutorial-structure.md deleted file mode 100644 index 0a06af4edb..0000000000 --- a/resources/contribute-documentation/tutorial-structure.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -html: tutorial-structure.html -parent: contribute-documentation.html -seo: - description: A summary of the parts of a standard tutorial. ---- -# Tutorial Structure - -Each XRP Ledger tutorial follows the same format. - -1. A brief description of the features illustrated in the tutorial. -2. Prerequisites for running the code, if needed, or links to the sample code. -3. Usage examples of the features in the tutorial. -4. A code walkthrough of the sample application, highlighting unique elements in the scripts. -5. See Also, with links to conceptual information or good tutorials to try as a next step. - -Separate setup (prerequisites) from usage from code development. These are each different activities that engage different areas of the brain. Trying to think of all three elements at once leads to confusion and headaches. - -## Description - -![Description](/docs/img/tut-struct1.png) - -List what the sample demonstrates. If possible, each example should describe the steps to accomplish specific related tasks. (For example, create a NFT Sell Offer, Accept a Sell Offer, Delete a Sell Offer.) There should be enough conceptual information to understand what the tutorial illustrates, with links to additional conceptual information, if needed. - -## Prerequisites - -![Prerequisites](/docs/img/tut-struct2.png) - -Provide links to any required software and to all of the example code needed to run the tutorial. If necessary, give simple instructions for using third-party tools, but provide a link to the source website for the customer to do a deeper dive at their leisure. - -## Usage Example - -![Usage](/docs/img/tut-struct3.png) - -Start by providing a finished, working example of the tutorial application. This is an opportunity for immediate success working with the software to solve a problem. - -Use screenshots for each step of the tutorial – these allow the user to understand the tutorial without having to run the code themselves. Of course we _want_ them to run the code, but this gives them a choice. - -Describe the sunny day scenario. The application should run without problems if there is an uninterrupted connection to the internet. Don’t provide a lot of troubleshooting information, unless it’s pertinent to the tutorial. - -## Code Walkthrough - -![Code Walkthrough](/docs/img/tut-struct4.png) - -Walk through the code, one chunk at a time. Don’t belabor topics that have been discussed in earlier examples. Provide sample code, but don’t provide exhaustive explanations for how to program underlying platforms like HTML syntax unless there is something unique to the implementation. - -An important thing to emphasize is that every interaction with the XRPL is either a transaction or a request, and that all transactions and requests are essentially the same. The sample code we provide shows how to prepare the transaction or request, and how to process the returned results. Knowing how to submit and respond to one transaction or request gives a pretty good idea for how to submit and respond to any transaction or request. - -(Technically there is third category, similar to a request: a notification from a subscription stream. See [Subscription Methods](../../docs/references/http-websocket-apis/public-api-methods/subscription-methods/index.md).) - -## See Also - -![See Also](/docs/img/tut-struct5.png) - -At the end of the tutorial, provide links to additional resources, conceptual information, and any tutorials that would be a sensible next step in the learning journey. diff --git a/sidebars.yaml b/sidebars.yaml index 114c254bc2..971c40b18a 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -765,5 +765,4 @@ - page: resources/contribute-documentation/creating-diagrams.md - page: resources/contribute-documentation/markdoc-tags.md - page: resources/contribute-documentation/tutorial-guidelines.md - - page: resources/contribute-documentation/tutorial-structure.md - page: resources/contribute-blog/index.md