From d19c8ffdf8e406a2c2b66a900a09b323f56308ed Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Wed, 15 May 2019 19:39:59 -0700 Subject: [PATCH] Tx history chaining: done with 1st draft of code --- .../monitor-payments-websocket/chaining.js | 52 +++++++++++++++---- ...onitor-incoming-payments-with-websocket.md | 4 +- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/content/_code-samples/monitor-payments-websocket/chaining.js b/content/_code-samples/monitor-payments-websocket/chaining.js index 3cf748a713..068a386634 100644 --- a/content/_code-samples/monitor-payments-websocket/chaining.js +++ b/content/_code-samples/monitor-payments-websocket/chaining.js @@ -34,7 +34,7 @@ function hasGaps(affected_nodes) { return false } else { console.warn("Gap detected...") - return true + return ledger_entry.PreviousTxnID } } } @@ -43,16 +43,50 @@ function hasGaps(affected_nodes) { return false } +async function fill_gap(gap_tx_hash) { + response = await api_request({ + "command": "tx", + "transaction": gap_tx_hash + }) + if (response.status === "success") { + reduced_gap_status = hasGaps(response.result.meta.AffectedNodes) + if (gap_status === false) { + console.log("Successfully closed the gap! Found transaction: "+response.result) + // At this point, you'd process the transaction. Warning: the format + // returned by tx is different than transaction subscription messages. + return response.result.hash + } else if (typeof gap_status === "undefined") { + console.log("Can't fill gap. Undefined last known tx.") + return false + } else if (typeof gap_status === "string") { + console.log("Gap extends deeper. Expected PreviousTxnID: " + + knownPreviousTxnID + " vs. actual: " + gap_status) + return fill_gap(gap_status) + } + + } else { + console.error("Error looking up gap TX " + gap_tx_hash + + "; maybe it's older than our server's known history?") + return false + } +} + const lookOutForGaps = function(data) { const gap_status = hasGaps(data.meta.AffectedNodes) if (gap_status === false) { - console.log("Chained successfully. New tx:") - } else if (gap_status === true) { - // fill gap - } else if (gap_status === undefined) { - console.log("") + console.log("Chained successfully. New tx: "+data.transaction.hash) + knownPreviousTxnID = data.transaction.hash + } else if (typeof gap_status === "undefined") { + console.log("Unknown prior status. Starting chain from tx: " + + data.transaction.hash) + } else if (typeof gap_status === "string") { + console.warn("Gap detected. Expected PreviousTxnID: " + + knownPreviousTxnID + " vs. actual: " + gap_status) + fill_success = await fill_gap(gap_tx_hash) + if (fill_success) { + knownPreviousTxnID = data.transaction.hash + } } - - } -WS_HANDLERS['transaction'] = watchForGaps + +WS_HANDLERS['transaction'] = lookOutForGaps diff --git a/content/tutorials/use-simple-xrp-payments/monitor-incoming-payments-with-websocket.md b/content/tutorials/use-simple-xrp-payments/monitor-incoming-payments-with-websocket.md index 0e1b968ce2..6c3b754319 100644 --- a/content/tutorials/use-simple-xrp-payments/monitor-incoming-payments-with-websocket.md +++ b/content/tutorials/use-simple-xrp-payments/monitor-incoming-payments-with-websocket.md @@ -2,7 +2,7 @@ This tutorial shows how to monitor for incoming payments using the [WebSocket `rippled` API](rippled-api.html). Since all XRP Ledger transactions are public, anyone can monitor incoming payments to any address. -WebSocket follows a model where the client and server establish one connection, then send messages both ways through the same connection, which remains open until explicitly closed (or until the connection fails). This is in contrast to the HTTP-based API model (including JSON-RPC and RESTful APIs), where the client opens and closes a new connection for each request[¹](#footnote-1). +WebSocket follows a model where the client and server establish one connection, then send messages both ways through the same connection, which remains open until explicitly closed (or until the connection fails). This is in contrast to the HTTP-based API model (including JSON-RPC and RESTful APIs), where the client opens and closes a new connection for each request.[¹](#footnote-1) **Tip:** The examples in this page use JavaScript so that the examples can run natively in a web browser. If you are developing in JavaScript, you can also use the [RippleAPI library for JavaScript](https://developers.ripple.com/rippleapi-reference.html) to simplify some tasks. This tutorial shows how to monitor for transactions _without_ using RippleAPI so that you can translate the steps to other programming languages that don't have RippleAPI. @@ -459,7 +459,7 @@ To look for gaps that might affect a transaction's XRP balance, you can use this 1. When you first connect, establish a starting point by calling the [account_info method][] to look up the latest `PreviousTxnID` value and save it. 2. Whenever you get a new transaction, look through the modified ledger objects to see if the account in question was modified. This is the same as how you read the account's balance changes. Specifically, you should look in the `meta.AffectedNodes` array for a `ModifiedNode` object of ledger entry type `AccountRoot`, where the `FinalFields.Account` value matches the address of the account. - - If no such object exists, the account was not directly modified by the transaction. Since the XRP Ledger tracks XRP balances in this object, you know that the account's XRP balance did not change[²](#footnote-2) . (The `accounts` stream also reports several types of transactions that _indirectly_ affect an account without modifying the `AccountRoot` object itself.) + - If no such object exists, the account was not directly modified by the transaction. Since the XRP Ledger tracks XRP balances in this object, you know that the account's XRP balance did not change.[²](#footnote-2) (The `accounts` stream also reports several types of transactions that _indirectly_ affect an account without modifying the `AccountRoot` object itself.) 3. If the account _was_ modified, compare the `ModifiedNode` object's `PreviousTxnID` field to the value you have saved. - If they match, you did not miss any changes to that account's XRP balance. Update your saved `PreviousTxnID` to the identifying hash of the new transaction (the `transaction.hash` field in `transaction`-type subscription messages). **Continue processing transactions normally.** 4. If the account was modified _and_ the `PreviousTxnID` value did not match the hash you were expecting, use the [tx method][] to look up the missing transaction, using the `PreviousTxnID` value from the new transaction to identify the missing transaction. We'll call this the "gap" transaction.