From 7c6257c2d8989697eecda99733634612c8338bf4 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Mon, 25 Oct 2021 22:56:31 -0700 Subject: [PATCH] Freeze tutorials: fixes and cleanup - Move JS code samples to JS folder (future-proofing for other languages) - Make "Freeze a Trust Line" tutorial interactive - Add necessary functionality to interactive_tutorial helper JS - Rework freeze a trust line tutorial/code sample to make an incoming line from another address and make looking up trust lines a full-fledged step --- assets/js/interactive-tutorial.js | 19 +- assets/js/tutorials/enact-global-freeze.js | 5 +- assets/js/tutorials/freeze-individual-line.js | 176 ++++++++++++++++++ .../freeze/{ => js}/check-global-freeze.js | 0 .../{ => js}/check-individual-freeze.js | 0 .../freeze/{ => js}/check-no-freeze.js | 0 .../freeze/{ => js}/package.json | 0 .../{ => js}/set-global-freeze-with-checks.js | 0 .../freeze/{ => js}/set-global-freeze.js | 0 .../freeze/js/set-individual-freeze.js | 133 +++++++++++++ .../freeze/{ => js}/set-no-freeze.js | 0 .../freeze/set-individual-freeze.js | 104 ----------- .../tutorials/use-tokens/enable-no-freeze.md | 4 +- .../use-tokens/enact-global-freeze.md | 6 +- .../use-tokens/freeze-a-trust-line.md | 118 +++++++++++- 15 files changed, 448 insertions(+), 117 deletions(-) create mode 100644 assets/js/tutorials/freeze-individual-line.js rename content/_code-samples/freeze/{ => js}/check-global-freeze.js (100%) rename content/_code-samples/freeze/{ => js}/check-individual-freeze.js (100%) rename content/_code-samples/freeze/{ => js}/check-no-freeze.js (100%) rename content/_code-samples/freeze/{ => js}/package.json (100%) rename content/_code-samples/freeze/{ => js}/set-global-freeze-with-checks.js (100%) rename content/_code-samples/freeze/{ => js}/set-global-freeze.js (100%) create mode 100644 content/_code-samples/freeze/js/set-individual-freeze.js rename content/_code-samples/freeze/{ => js}/set-no-freeze.js (100%) delete mode 100644 content/_code-samples/freeze/set-individual-freeze.js diff --git a/assets/js/interactive-tutorial.js b/assets/js/interactive-tutorial.js index 5fc7635ad3..83af4494cc 100644 --- a/assets/js/interactive-tutorial.js +++ b/assets/js/interactive-tutorial.js @@ -255,6 +255,7 @@ function get_wallet(event) { const block = $(event.target).closest(".interactive-block") if (!block.length) {return} show_error(block, tl("Can't use the example secret here. Check that the previous steps were completed successfully.")) + return } return xrpl.Wallet.fromSeed(secret) } @@ -285,6 +286,16 @@ async function call_faucet(faucet_url, destination) { return data } +/** + * Tutorials' scripts should push functions to this array to have them run + * automatically after connecting to the network. The scopes don't work out + * right to use api.on("connect", callback) directly from the tutorials' unique + * scripts because the api instance (specific to the network) is instantiated + * by the setup_connect_step() in this file, below. + */ +window.after_connect = window.after_connect || []; + + /** * To be used with _snippets/interactive-tutorials/connect-step.md * Adds an event to the "Connect" button to connect to the appropriate @@ -322,10 +333,14 @@ function setup_connect_step() { disable_followup_steps() }) - $("#connect-button").click(() => { + $("#connect-button").click(async (event) => { $("#connection-status").text( tl("Connecting...") ) $("#loader-connect").show() - api.connect() + await api.connect() + + for (const fn of after_connect) { + fn() + } }) } diff --git a/assets/js/tutorials/enact-global-freeze.js b/assets/js/tutorials/enact-global-freeze.js index 568180347b..099a4d4e53 100644 --- a/assets/js/tutorials/enact-global-freeze.js +++ b/assets/js/tutorials/enact-global-freeze.js @@ -14,10 +14,13 @@ $(document).ready(() => { "TransactionType": "AccountSet", "Account": address } + let step_name if ($(event.target).data("action") === "start_freeze") { astx["SetFlag"] = xrpl.AccountSetAsfFlags.asfGlobalFreeze + step_name = "Send AccountSet" } else if ($(event.target).data("action") === "end_freeze") { astx["ClearFlag"] = xrpl.AccountSetAsfFlags.asfGlobalFreeze + step_name = "End Freeze" } else { show_error(block, "There was an error with this tutorial: the button clicked must have data-action defined.") } @@ -25,7 +28,7 @@ $(document).ready(() => { try { block.find(".loader").show() await generic_full_send(event, astx) - complete_step("Send AccountSet") + complete_step(step_name) } catch(err) { block.find(".loader").hide() show_error(block, err) diff --git a/assets/js/tutorials/freeze-individual-line.js b/assets/js/tutorials/freeze-individual-line.js new file mode 100644 index 0000000000..c0cb8419d1 --- /dev/null +++ b/assets/js/tutorials/freeze-individual-line.js @@ -0,0 +1,176 @@ +// 1. Generate +// 2. Connect +// The code for these steps is handled by interactive-tutorial.js +const CURRENCY_TO_FREEZE = "FOO" + +// Helper to get a Wallet instance for the peer wallet +function get_peer_wallet(event) { + let peer_seed + try { + peer_seed = $("#peer-seed").val() + } catch(e) { + show_error(block, err) + return + } + if (!peer_seed) { + const block = $(event.target).closest(".interactive-block") + if (!block.length) {return} + show_error(block, "Couldn't get information on the peer account. Check that the previous steps were completed successfully.") + return + } + return xrpl.Wallet.fromSeed(peer_seed) +} + +let trust_line_setup_done = false +window.after_connect = window.after_connect || []; +window.after_connect.push(async () => { + // 2.5. One-time setup on connect to create an incoming trust line after + // the "api" instance has been created by the connect handler. + if (trust_line_setup_done) {return} // Don't repeat if we disconnect/reconnect + console.log("Setting up an incoming trust line so our test address has something to freeze...") + $("#trust-line-setup-loader").show() + const address = get_address() + const peer = (await api.fundWallet()).wallet + const tx_json = { + "TransactionType": "TrustSet", + "Account": peer.address, + "LimitAmount": { + "currency": CURRENCY_TO_FREEZE, + "issuer": address, + "value": "123456.789" // arbitrary limit + } + } + try { + const submitted = await api.submitAndWait(tx_json, {wallet: peer}) + console.log("Set up incoming trust line result:", submitted) + } catch(e) { + const block = $("#trust-line-setup-loader").closest(".interactive-block") + show_err(block, e) + $("#trust-line-setup-loader").hide() + return + } + $("#trust-line-setup-loader").hide() + $("#look-up-trust-lines").prop("disabled", false).prop("title", "") + $("#peer-seed").val(peer.seed) + trust_line_setup_done = true +}) + +$(document).ready(() => { + + + + // 3. Choose Trust Line ------------------------------------------------------ + $("#look-up-trust-lines").click( async (event) => { + const block = $(event.target).closest(".interactive-block") + block.find(".output-area").html("") + const address = get_address(event) + if (!address) {return} + const peer = get_peer_wallet(event) + if (!peer) {return} + + block.find(".loader-looking").show() + let account_lines + try { + account_lines = await api.request({ + "command": "account_lines", + "account": address, + "peer": peer.address, + "ledger_index": "validated" + }) + } catch(e) { + show_error(block, err) + } + + block.find(".loader-looking").hide() + block.find(".output-area").append( + `

Found trust line(s) between ${address} and ${peer.address}:

+
${pretty_print(account_lines.result.lines)}
+

Choosing ${CURRENCY_TO_FREEZE} trust line.

`) + complete_step("Choose Trust Line") + }) + + // 4. Send TrustSet to freeze ------------------------------------------------ + // also 7. Send TrustSet to end the freeze + $(".send-trustset").click( async (event) => { + const block = $(event.target).closest(".interactive-block") + const address = get_address(event) + if (!address) {return} + const peer = get_peer_wallet(event) + if (!peer) {return} + + let tstx = { + "TransactionType": "TrustSet", + "Account": address, + "LimitAmount": { + "currency": CURRENCY_TO_FREEZE, + "issuer": peer.address, + "value": "0" + } + } + + let step_name + if ($(event.target).data("action") === "start_freeze") { + tstx["Flags"] = xrpl.TrustSetFlags.tfSetFreeze + step_name = "Send TrustSet to Freeze" + } else if ($(event.target).data("action") === "end_freeze") { + tstx["Flags"] = xrpl.TrustSetFlags.tfClearFreeze + step_name = "Send TrustSet to End Freeze" + } else { + show_error(block, "There was an error with this tutorial: the button clicked must have data-action defined.") + } + + try { + await generic_full_send(event, tstx) + complete_step(step_name) + } catch(err) { + block.find(".loader").hide() + show_error(block, err) + } + }) + + // 5. Wait for Validation is handled by the snippet + + // 6. Check Trust Line Freeze Status + $("#confirm-settings").click( async (event) => { + const block = $(event.target).closest(".interactive-block") + const address = get_address(event) + if (!address) {return} + const peer = get_peer_wallet(event) + if (!peer) {return} + + block.find(".output-area").html("") + block.find(".loader").show() + const account_lines = await api.request({ + "command": "account_lines", + "account": address, + "peer": peer.address, + "ledger_index": "validated" + }) + console.log(account_lines) + block.find(".loader").hide() + + const trustlines = account_lines.result.lines + for (let i = 0; i < trustlines.length; i++) { + if(trustlines[i].currency === CURRENCY_TO_FREEZE) { + const line = trustlines[i] + block.find(".output-area").append( + `

Status of ${CURRENCY_TO_FREEZE} line between ${address} and ${peer.address}:

+
${pretty_print(line)}
`) + if (line.freeze === true) { + block.find(".output-area").append(`

+ Line is frozen.`) + } else { + block.find(".output-area").append(`

+ Line is NOT FROZEN.

`) + } + complete_step("Check Freeze Status") + return + } + } + + show_error(block, `Couldn't find a ${CURRENCY_TO_FREEZE} line between ${address} and ${peer.address}`) + }) + + // 7. Send TrustSet to End the Freeze is covered by the shared handler above + +}) diff --git a/content/_code-samples/freeze/check-global-freeze.js b/content/_code-samples/freeze/js/check-global-freeze.js similarity index 100% rename from content/_code-samples/freeze/check-global-freeze.js rename to content/_code-samples/freeze/js/check-global-freeze.js diff --git a/content/_code-samples/freeze/check-individual-freeze.js b/content/_code-samples/freeze/js/check-individual-freeze.js similarity index 100% rename from content/_code-samples/freeze/check-individual-freeze.js rename to content/_code-samples/freeze/js/check-individual-freeze.js diff --git a/content/_code-samples/freeze/check-no-freeze.js b/content/_code-samples/freeze/js/check-no-freeze.js similarity index 100% rename from content/_code-samples/freeze/check-no-freeze.js rename to content/_code-samples/freeze/js/check-no-freeze.js diff --git a/content/_code-samples/freeze/package.json b/content/_code-samples/freeze/js/package.json similarity index 100% rename from content/_code-samples/freeze/package.json rename to content/_code-samples/freeze/js/package.json diff --git a/content/_code-samples/freeze/set-global-freeze-with-checks.js b/content/_code-samples/freeze/js/set-global-freeze-with-checks.js similarity index 100% rename from content/_code-samples/freeze/set-global-freeze-with-checks.js rename to content/_code-samples/freeze/js/set-global-freeze-with-checks.js diff --git a/content/_code-samples/freeze/set-global-freeze.js b/content/_code-samples/freeze/js/set-global-freeze.js similarity index 100% rename from content/_code-samples/freeze/set-global-freeze.js rename to content/_code-samples/freeze/js/set-global-freeze.js diff --git a/content/_code-samples/freeze/js/set-individual-freeze.js b/content/_code-samples/freeze/js/set-individual-freeze.js new file mode 100644 index 0000000000..fcaf248a2c --- /dev/null +++ b/content/_code-samples/freeze/js/set-individual-freeze.js @@ -0,0 +1,133 @@ +// Dependencies for Node.js. +// In browsers, use + ## Example Code @@ -37,6 +42,8 @@ Complete sample code for all of the steps of this tutorial is available under th To transact on the XRP Ledger, you need an address and secret key, and some XRP. If you use the best practice of having separate ["cold" and "hot" addresses](issuing-and-operational-addresses.html), you need the keys to the _cold address_, which is the **issuer** of the token. +{% include '_snippets/interactive-tutorials/generate-step.md' %} + ### {{n.next()}}. Connect to the Network You must be connected to the network to submit transactions to it. The following code shows how to connect to a public XRP Ledger Testnet server a supported [client library](client-libraries.html): @@ -55,6 +62,78 @@ _WebSocket_ +For purposes of this tutorial, use the following interface to connect and perform setup: + +{% include '_snippets/interactive-tutorials/connect-step.md' %} + + +### {{n.next()}}. Choose Trust Line + +You can only freeze one trust line per transaction, so you need to know which one you want. A trust line is uniquely identified by these 3 things: + +- Your own address. +- The address of the account linked to yours via the trust line. +- The currency code of the trust line. + +There can be multiple [trust lines](trust-lines-and-issuing.html) between two accounts, each for a different currency. If you suspect a particular account is behaving maliciously, you may want to freeze all the trust lines between your accounts, one at a time. Use the [account_lines method][] with a pair of accounts to find all trust lines between those accounts, then choose a trust line to freeze from among the results. For example: + + + +_JavaScript_ + +{{ include_code("_code-samples/freeze/js/set-individual-freeze.js", language="js", start_with="// Look up current trust lines", end_before="// Send a TrustSet") }} + +_WebSocket_ + +```json +Example Request: + +{ + "id": "example_look_up_trust_lines", + "command": "account_lines", + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "peer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + "ledger_index": "validated" +} + +// Example Response: + +{ + "id": "example_look_up_trust_lines", + "result": { + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "ledger_hash": "AF5C6E6FBC44183D8662C7F5BF88D52F99738A0E66FF07FC7B5A516AC8EA1B37", + "ledger_index": 67268474, + "lines": [ + { + "account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + "balance": "0", + "currency": "USD", + "limit": "0", + "limit_peer": "110", + "quality_in": 0, + "quality_out": 0 + } + ], + "validated": true + }, + "status": "success", + "type": "response" +} +``` + + + +For purposes of this tutorial, a second test address has created a trust line to the test address for the currency "FOO", which you can see in the following example: + +{{ start_step("Choose Trust Line")}} +
Waiting for setup to complete...
+ + +
Looking...
+
+{{ end_step() }} + ### {{n.next()}}. Send TrustSet Transaction to Freeze the Trust Line @@ -76,7 +155,7 @@ As always, to send a transaction, you _prepare_ it by filling in all the necessa _JavaScript_ -{{ include_code("_code-samples/freeze/set-individual-freeze.js", language="js", start_with="// Prepare a TrustSet", end_before="// Investigate") }} +{{ include_code("_code-samples/freeze/js/set-individual-freeze.js", language="js", start_with="// Send a TrustSet", end_before="// Investigate") }} _WebSocket_ @@ -103,11 +182,22 @@ _WebSocket_ +{{ start_step("Send TrustSet to Freeze") }} + +
Sending...
+
+{{ end_step() }} + +**Note:** If you want to freeze multiple trust lines in different currencies with the same counterparty, repeat this step for each one, choosing a different currency code each time. It is possible to send several transactions in a single ledger if you use different [sequence numbers](basic-data-types.html#account-sequence) for each. + ### {{n.next()}}. Wait for Validation Most transactions are accepted into the next ledger version after they're submitted, which means it may take 4-7 seconds for a transaction's outcome to be final. If the XRP Ledger is busy or poor network connectivity delays a transaction from being relayed throughout the network, a transaction may take longer to be confirmed. (For information on how to set an expiration for transactions, see [Reliable Transaction Submission](reliable-transaction-submission.html).) +{{ start_step("Wait") }} +{% include '_snippets/interactive-tutorials/wait-step.md' %} +{{ end_step() }} ### {{n.next()}}. Check Trust Line Freeze Status @@ -120,13 +210,13 @@ At this point, the trust line from the counterparty should be frozen. You can ch **Caution:** The response includes _all_ trust lines between the two accounts. (Each different currency code uses a different trust line.) Be sure to check the one for the right token. -In the response, the field `"freeze": true` indicates that the account from the request has enabled an Individual Freeze on that trust line. The field `"freeze_peer": true` indicates that the counterparty (`peer`) from the request has frozen the trust line. +In the response, the field `"freeze": true` indicates that the account from the request has enabled an Individual Freeze on that trust line. The field `"freeze_peer": true` indicates that the counterparty (`peer`) from the request has frozen the trust line. For example: _JavaScript_ -{{ include_code("_code-samples/freeze/check-individual-freeze.js", language="js", start_with="// Look up current state", end_before="await client.disconnect()") }} +{{ include_code("_code-samples/freeze/js/set-individual-freeze.js", language="js", start_with="// Confirm trust line status", end_before="// Investigate") }} _WebSocket_ @@ -167,6 +257,13 @@ Example Response: +{{ start_step("Check Freeze Status") }} + +
Checking...
+
+{{ end_step() }} + + ### {{n.next()}}. (Optional) Send TrustSet Transaction to End the Freeze If you decide that the trust line no longer needs to be frozen (for example, you investigated and decided that the suspicious activity was benign), you can end the individual freeze in almost the same way that you froze the trust line in the first place. To end an individual freeze, send a [TrustSet transaction][] with the [`tfClearFreeze` flag enabled](trustset.html#trustset-flags). The other fields of the transaction should be the same as when you froze the trust line: @@ -187,7 +284,7 @@ As always, to send a transaction, you _prepare_ it by filling in all the necessa _JavaScript_ -{{ include_code("_code-samples/freeze/set-individual-freeze.js", language="js", start_with="// Clear the individual", end_before="// End main") }} +{{ include_code("_code-samples/freeze/js/set-individual-freeze.js", language="js", start_with="// Clear the individual", end_before="// End main") }} _WebSocket_ @@ -214,10 +311,21 @@ _WebSocket_ +{{ start_step("Send TrustSet to End Freeze") }} + +
Sending...
+
+{{ end_step() }} + ### {{n.next()}}. Wait for Validation -As before, wait for the previous transaction to be validated by consensus before continuing. +As before, wait for the transaction to be validated by consensus. + +{{ start_step("Wait (again)") }} +{% include '_snippets/interactive-tutorials/wait-step.md' %} +{{ end_step() }} + ## See Also