diff --git a/docs/GUIDES.md b/docs/GUIDES.md index c3d7fc14..05baa061 100644 --- a/docs/GUIDES.md +++ b/docs/GUIDES.md @@ -250,3 +250,11 @@ remote.connect(function() { }); }); ``` + +##Transaction Metadata## + +The following is a supplement to https://wiki.ripple.com/Ledger_Format#RippleState + +The HighNode/LowNode fields in the transaction metadata are deletion hints to allow trustline deletion in constant time. Trustlines for a node are stored in a linked list of pages containing at most 32 trustlines each. A node may have a very large number of trustlines, so if you had to delete a trustline with only the addresses of the two nodes, you would have to iterate through all the pages in each node looking for the trustline that links to the other node. The HighNode/LowNode fields contain pointers to the relevant page so that rippled does not have to iterate through all the pages. If there is only one page, then HighNode/LowNode will be set to a string of zeros. + +For more information about what a HighNode and LowNode are, see the Trust Line section in rippled/app/ledger/README.md diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9e09eac8..eb3c9273 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -4,15 +4,23 @@ "dependencies": { "async": { "version": "0.8.0", - "from": "async@>=0.8.0 <0.9.0" + "from": "async@~0.8.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.8.0.tgz" }, "extend": { "version": "1.2.1", - "from": "extend@>=1.2.1 <1.3.0" + "from": "extend@~1.2.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz" + }, + "lodash": { + "version": "2.4.1", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" }, "lru-cache": { "version": "2.5.0", - "from": "lru-cache@>=2.5.0 <2.6.0" + "from": "lru-cache@~2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" }, "ripple-wallet-generator": { "version": "1.0.1", @@ -21,7 +29,8 @@ }, "superagent": { "version": "0.18.2", - "from": "superagent@>=0.18.0 <0.19.0", + "from": "superagent@^0.18.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-0.18.2.tgz", "dependencies": { "qs": { "version": "0.6.6", @@ -55,7 +64,8 @@ }, "debug": { "version": "1.0.4", - "from": "debug@>=1.0.1 <1.1.0", + "from": "debug@~1.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", "dependencies": { "ms": { "version": "0.6.2", @@ -75,8 +85,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz", "dependencies": { "combined-stream": { - "version": "0.0.5", - "from": "combined-stream@>=0.0.4 <0.1.0", + "version": "0.0.7", + "from": "combined-stream@~0.0.4", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", @@ -87,17 +98,20 @@ }, "async": { "version": "0.9.0", - "from": "async@>=0.9.0 <0.10.0" + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" } } }, "readable-stream": { "version": "1.0.27-1", "from": "readable-stream@1.0.27-1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0" + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", @@ -106,11 +120,13 @@ }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0" + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0" + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } } @@ -118,23 +134,28 @@ }, "ws": { "version": "0.4.32", - "from": "ws@>=0.4.31 <0.5.0", + "from": "ws@~0.4.31", + "resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz", "dependencies": { "commander": { "version": "2.1.0", - "from": "commander@>=2.1.0 <2.2.0" + "from": "commander@~2.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz" }, "nan": { "version": "1.0.0", - "from": "nan@>=1.0.0 <1.1.0" + "from": "nan@~1.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.0.0.tgz" }, "tinycolor": { "version": "0.0.1", - "from": "tinycolor@>=0.0.0 <1.0.0" + "from": "tinycolor@0.x", + "resolved": "https://registry.npmjs.org/tinycolor/-/tinycolor-0.0.1.tgz" }, "options": { "version": "0.0.6", - "from": "options@>=0.0.5" + "from": "options@>=0.0.5", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" } } } diff --git a/package.json b/package.json index 03663ce3..01ee3b57 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test": "test" }, "dependencies": { + "lodash": "~2.4.1", "async": "~0.8.0", "ws": "~0.4.31", "extend": "~1.2.1", diff --git a/src/js/ripple/amount.js b/src/js/ripple/amount.js index 852fd6bb..79f71547 100644 --- a/src/js/ripple/amount.js +++ b/src/js/ripple/amount.js @@ -17,6 +17,14 @@ var Currency = require('./currency').Currency; // http://docs.oracle.com/javase/1.3/docs/api/java/math/BigInteger.html // +function bignum(x) { + return Amount.from_json({value: x.toString()}); +}; + +function dropsToXrp(drops) { + return bignum(drops).divide(1000000).to_text(); +}; + function Amount() { // Json format: // integer : XRP @@ -1249,6 +1257,23 @@ Amount.prototype.to_json = function() { return result; }; +Amount.prototype.toJSON = function() { + if (this._is_native) { + return { + issuer: '', + currency: 'XRP', + value: dropsToXrp(this.to_text()) + }; + } else { + return { + issuer: this._issuer.to_json(), + currency: this._currency.has_interest() ? + this._currency.to_hex() : this._currency.to_json().toString(), + value: this.to_text() + }; + } +}; + Amount.prototype.to_text_full = function(opts) { return this._value instanceof BigInteger ? this._is_native diff --git a/src/js/ripple/meta.js b/src/js/ripple/meta.js index 6bca5575..bd4606bc 100644 --- a/src/js/ripple/meta.js +++ b/src/js/ripple/meta.js @@ -1,3 +1,4 @@ +var _ = require('lodash'); var extend = require('extend'); var utils = require('./utils'); var UInt160 = require('./uint160').UInt160; @@ -11,8 +12,6 @@ var Amount = require('./amount').Amount; */ function Meta(data) { - var self = this; - this.nodes = [ ]; if (typeof data !== 'object') { @@ -238,4 +237,136 @@ Meta.prototype.getAffectedBooks = function() { return this._affectedBooks; }; + +// TODO: migrate to external bignum library +function bignum(x) { + return Amount.from_json({value: x.toString()}); +}; + +// Merge balance changes that have the same account, issuer, and currency +// by summing them together +function mergeBalanceChanges(balanceChanges) { + var grouped = _.groupBy(balanceChanges, function(change) { + return (change.address + '-' + + change.balance_change.issuer().to_json().toString() + '-' + + change.balance_change.currency().to_json().toString()); + }); + return _.compact(Object.keys(grouped).map(function(key) { + var group = grouped[key]; + var valueSum = bignum(0); + group.forEach(function(change) { + valueSum = valueSum.add(bignum(change.balance_change.to_text())); + }); + if (valueSum.is_zero()) { + return null; // do not report balance change if there was no change + } + var currency = group[0].balance_change.currency().to_json(); + var balanceChange = currency === 'XRP' ? + Amount.from_json(valueSum.to_text()) : + Amount.from_json({ + issuer: group[0].balance_change.issuer().to_json(), + currency: currency, + value: valueSum.to_text() + }); + return { + address: group[0].address, + balance_change: balanceChange + }; + })); +}; + +function parseAccountRootBalanceChange(node, address) { + if (!_.isEmpty(node.fieldsNew)) { + return { + address: node.fieldsNew.Account, + balance_change: Amount.from_json(node.fieldsNew.Balance) + }; + } else if (!_.isEmpty(node.fieldsFinal)) { + var finalBal = bignum(node.fieldsFinal.Balance); + var prevBal = (typeof node.fieldsPrev.Balance === 'string') ? + bignum(node.fieldsPrev.Balance) : bignum(0); + + return { + address: node.fieldsFinal.Account, + balance_change: Amount.from_json(finalBal.subtract(prevBal).to_text()) + }; + } + + return null; +}; + +function isHighNodeIssuer(trustBalFinal, trustBalPrev, trustHigh, trustLow) { + if (trustBalFinal.is_positive()) { + return true; + } else if (trustBalFinal.is_negative()) { + return false; + } else if (trustBalPrev.is_positive()) { + return true; + } else if (trustBalPrev.is_negative()) { + return false; + } else if (trustLow.is_zero() && trustHigh.is_positive()) { + return false; // high node cannot issue + } else if (trustHigh.is_zero() && trustLow.is_positive()) { + return true; // low node cannot issue + } else { + return false; // totally arbitrary + } +}; + +function parseTrustlineBalanceChange(node, address) { + if (!_.isEmpty(node.fieldsFinal)) { + if (!(node.fieldsPrev.Balance)) { + return null; // setting a trustline limit, no balance change + } + } + var finalFields = _.isEmpty(node.fieldsNew) ? + node.fieldsFinal : node.fieldsNew; + var trustHighIssuer = finalFields.HighLimit.issuer; + var trustLowIssuer = finalFields.LowLimit.issuer; + if (address !== trustLowIssuer && address !== trustHighIssuer) { + return null; + } + var trustHigh = bignum(finalFields.HighLimit.value); + var trustLow = bignum(finalFields.LowLimit.value); + var trustBalFinal = bignum(finalFields.Balance.value); + var trustBalPrev = node.fieldsPrev.Balance ? + bignum(node.fieldsPrev.Balance.value) : bignum(0); + var trustBalChange = trustBalFinal.subtract(trustBalPrev); + var issuer = isHighNodeIssuer(trustBalFinal, trustBalPrev, trustHigh, + trustLow) ? trustHighIssuer : trustLowIssuer; + var balanceChange = (address === trustHighIssuer) ? + trustBalChange.negate() : trustBalChange; + + return { + address: address, + balance_change: Amount.from_json({ + value: balanceChange.to_text(), + currency: finalFields.Balance.currency, + issuer: issuer + }) + }; +}; + +function parseTrustlineBalanceChanges(node) { + var fields = _.isEmpty(node.fieldsNew) ? node.fieldsFinal : node.fieldsNew; + return [parseTrustlineBalanceChange(node, fields.HighLimit.issuer), + parseTrustlineBalanceChange(node, fields.LowLimit.issuer)]; +}; + +Meta.prototype.parseBalanceChanges = function() { + var balanceChanges = this.nodes.map(function(node) { + if (node.entryType === 'AccountRoot') { + // Look for XRP balance change in AccountRoot node + return [parseAccountRootBalanceChange(node)]; + } else if (node.entryType === 'RippleState') { + // Look for trustline balance change in RippleState node + return parseTrustlineBalanceChanges(node); + } else { + return [ ]; + } + }); + + return mergeBalanceChanges(_.compact(_.flatten(balanceChanges))); +}; + exports.Meta = Meta; diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 1a9e4524..1893b63f 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -1,5 +1,4 @@ var util = require('util'); -var assert = require('assert'); var EventEmitter = require('events').EventEmitter; var utils = require('./utils'); var sjcl = require('./utils').sjcl; @@ -12,6 +11,7 @@ var RippleError = require('./rippleerror').RippleError; var hashprefixes = require('./hashprefixes'); var config = require('./config'); var log = require('./log').internal.sub('transaction'); +var Meta = require('./meta').Meta; /** * @constructor Transaction @@ -48,6 +48,7 @@ function Transaction(remote) { this.attempts = 0; this.submissions = 0; this.responses = 0; + this.mmeta = void(0); this.once('success', function(message) { // Transaction definitively succeeded @@ -214,6 +215,10 @@ Transaction.prototype.setState = function(state) { Transaction.prototype.finalize = function(message) { this.finalized = true; + if (typeof message.metadata === 'object') { + this.mmeta = new Meta(message.metadata); + } + if (this.result) { this.result.ledger_index = message.ledger_index; this.result.ledger_hash = message.ledger_hash; @@ -909,7 +914,7 @@ Transaction.prototype.accountSet = function(src, set_flag, clear_flag) { return (typeof flag === 'number') ? flag : (SetClearFlags[flag] || SetClearFlags['asf' + flag]); - }; + } if (set_flag && (set_flag = prepareFlag(set_flag))) { this.tx_json.SetFlag = set_flag; @@ -1198,6 +1203,13 @@ Transaction.prototype.summary = function() { return result; }; +Transaction.prototype.getBalanceChanges = function() { + if (!this.finalized || this.mmeta === void(0)) { + return [ ]; + } + return this.mmeta.parseBalanceChanges(); +}; + exports.Transaction = Transaction; // vim:sw=2:sts=2:ts=8:et diff --git a/test/fixtures/payment-iou-destination-no-balance.json b/test/fixtures/payment-iou-destination-no-balance.json new file mode 100644 index 00000000..aabf8cf0 --- /dev/null +++ b/test/fixtures/payment-iou-destination-no-balance.json @@ -0,0 +1,123 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "1B549453005A924A4EEF6DA9E1BA7D8D1E51D7B685CE8DE072667321FBA792C8", + "ledger_index": 10425094, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.535330905250352" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.545330905250352" + } + }, + "PreviousTxnID": "CEB7B6040C2989B9849C8D7E49F710457EDDE1D95ECDF1E298FD30CF2AC5BE11", + "PreviousTxnLgrSeq": 10424776 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.01" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + } + }, + "PreviousTxnID": "A788447CF5FD7108CBF49416E2335F95ED3F5A9FC016686C8F9EFB34BBEA613A", + "PreviousTxnLgrSeq": 10425088 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Balance": "239807992", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 17 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818", + "PreviousFields": { + "Balance": "239819992", + "Sequence": 16 + }, + "PreviousTxnID": "3109F5A0F891CCA20B4D891EB7437973F40A7664C5176092EB2E5C0A949992AD", + "PreviousTxnLgrSeq": 10424942 + } + } + ], + "TransactionIndex": 3, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Amount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0.01" + }, + "Destination": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10425102, + "Sequence": 16, + "SigningPubKey": "03F16A52EBDCA6EBF5D99828E1E6918C64D45E6F136476A8F4757512FE553D18F0", + "TransactionType": "Payment", + "TxnSignature": "3044022017DC45606103FF2CCE5D63E340D1E2B302BE16C206673B99712058CA0DBD2CC5022074A19CF5AF9CDFCEC821650A4C22E83E49E14956E1F2A094181E8E14F6881200", + "date": 471494760, + "hash": "D038C9D95926EACAA9FBDF3E6CA0675ACF1700B3CE13FF4CA52B463B58860BBA" + } +} diff --git a/test/fixtures/payment-iou-spend-full-balance.json b/test/fixtures/payment-iou-spend-full-balance.json new file mode 100644 index 00000000..f60acc76 --- /dev/null +++ b/test/fixtures/payment-iou-spend-full-balance.json @@ -0,0 +1,124 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "605F02CC71269E0C3253FC582503D0A69533DF9E744F1613CB132A67261527F2", + "ledger_index": 10458307, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.545330905250352" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.345330905250352" + } + }, + "PreviousTxnID": "24525F80080EAC8857F1A29A47AEF23FD2B0A52DAF7DC3900A4E31831187FCB1", + "PreviousTxnLgrSeq": 10443886 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.2" + } + }, + "PreviousTxnID": "24525F80080EAC8857F1A29A47AEF23FD2B0A52DAF7DC3900A4E31831187FCB1", + "PreviousTxnLgrSeq": 10443886 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "99976002", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 3 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "PreviousFields": { + "Balance": "99988002", + "Sequence": 2 + }, + "PreviousTxnID": "A788447CF5FD7108CBF49416E2335F95ED3F5A9FC016686C8F9EFB34BBEA613A", + "PreviousTxnLgrSeq": 10425088 + } + } + ], + "TransactionIndex": 3, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Amount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0.2" + }, + "Destination": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10458314, + "Sequence": 2, + "SigningPubKey": "039371D0465097AC8F9C02EB60D5599AAD08AADBD623D6D40D642CF2D7C0481B83", + "TransactionType": "Payment", + "TxnSignature": "304502210097A62D87FF08A050F1832B974F06FB4F0C83F6661CC916AE190A439C8DB3863202204CD27C5C6A9067BA5A1DA9BB940FDF3F7C6B073225911E567B1E37E9C29E99D4", + "date": 471639670, + "hash": "5AC7632779C3AE649236F728C2C6811D7ADDE6CCC5018B8754C6EB953FCB1BC5" + } +} + diff --git a/test/fixtures/payment-iou.json b/test/fixtures/payment-iou.json new file mode 100644 index 00000000..6ed1c600 --- /dev/null +++ b/test/fixtures/payment-iou.json @@ -0,0 +1,123 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "F3F1416BF2E813396AB01FAA38E9F1023AC4D2368D94B0D52B2BC603CEEC01C3", + "ledger_index": 10459371, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.525330905250352" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.535330905250352" + } + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.02" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.01" + } + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Balance": "239555992", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 38 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818", + "PreviousFields": { + "Balance": "239567992", + "Sequence": 37 + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + } + ], + "TransactionIndex": 2, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Amount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0.01" + }, + "Destination": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10459379, + "Sequence": 37, + "SigningPubKey": "03F16A52EBDCA6EBF5D99828E1E6918C64D45E6F136476A8F4757512FE553D18F0", + "TransactionType": "Payment", + "TxnSignature": "3044022031D6AB55CDFD17E06DA0BAD6D6B7DC9B5CA8FFF50405F2FCD3ED8D3893B1835E02200524CC1E7D70AE3F00C9F94405C55EE179C363F534905168AE8B5BA01CF568A0", + "date": 471644150, + "hash": "34671C179737CC89E0F8BBAA83C313885ED1733488FC0F3088BAE16A3D9A5B1B" + } +} diff --git a/test/fixtures/payment-xrp-create-account.json b/test/fixtures/payment-xrp-create-account.json new file mode 100644 index 00000000..6f111e19 --- /dev/null +++ b/test/fixtures/payment-xrp-create-account.json @@ -0,0 +1,60 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "5FA9BBBE9EF6CE4BF9BB56CC3A70106C1475EA4C02EDE26651710FAA46E81F60", + "ledger_index": 10424084, + "metadata": { + "AffectedNodes": [ + { + "CreatedNode": { + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "NewFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "100000000", + "Sequence": 1 + } + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Balance": "339903994", + "Flags": 0, + "OwnerCount": 0, + "Sequence": 9 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818", + "PreviousFields": { + "Balance": "439915994", + "Sequence": 8 + }, + "PreviousTxnID": "0E6CF1A13C6A804BE50B08C1D0446C7405D8461254CC6B62337CA9FEA4DF13EC", + "PreviousTxnLgrSeq": 10424064 + } + } + ], + "TransactionIndex": 3, + "TransactionResult": "tesSUCCESS" + }, + "status": "closed", + "transaction": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Amount": "100000000", + "Destination": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10424091, + "Sequence": 8, + "SigningPubKey": "03F16A52EBDCA6EBF5D99828E1E6918C64D45E6F136476A8F4757512FE553D18F0", + "TransactionType": "Payment", + "TxnSignature": "304402207676FE35FF0D01E01F13531760658ADECC493181A4EF618A970E4C209FC989C102206660147F17B46469864E9E10152844C4DA62363DB70B5F610E6DAEA87A6781A9", + "date": 471490400, + "hash": "43CAAC76C95358CA2F84EA8BF5BFC327B90B21039A3C69EC4EAC7FEDC54CDB9F" + }, + "type": "transaction", + "validated": true +} diff --git a/test/fixtures/trustline-create.json b/test/fixtures/trustline-create.json new file mode 100644 index 00000000..50eb7aaa --- /dev/null +++ b/test/fixtures/trustline-create.json @@ -0,0 +1,109 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "CA4C25E271FDB00836A091E8073F6692784BEB116B33675533F9511B17FC2B72", + "ledger_index": 10483144, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Flags": 0, + "IndexPrevious": "00000000000001F1", + "Owner": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "RootIndex": "6319526CE8F9A8A44D7A870A89DC1B4AD848AA4F066FCB5390A9A268F6E16AEA" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "58E06162628C5E2292DA172A97573FA5613C4A223810686428BF8431B3D67C58" + } + }, + { + "CreatedNode": { + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "84C8F98961F2F10CB1B5C4FB649C18B05A9D3FC20C3A78B75C86CD30D7EAC39C", + "NewFields": { + "Owner": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "RootIndex": "84C8F98961F2F10CB1B5C4FB649C18B05A9D3FC20C3A78B75C86CD30D7EAC39C" + } + } + }, + { + "ModifiedNode": { + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "A3C1529122C3DBD6C96B9DF009FF4896023FE6B4E05A508B1E81F3DCD9A6274B", + "PreviousTxnID": "CA4BD65D1E29552B17041B219105E1BC0FE00837C798DE9F9E2EA670097ACE33", + "PreviousTxnLgrSeq": 10483128 + } + }, + { + "CreatedNode": { + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "NewFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + }, + "Flags": 65536, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001F2", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "100" + } + } + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "99740302", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 23 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "PreviousFields": { + "Balance": "99752302", + "OwnerCount": 0, + "Sequence": 22 + }, + "PreviousTxnID": "8A3F4CA1D349B4BE896DFDED6B6D0F0DCA4FCA75E082C30A4175813DDB9BCDA6", + "PreviousTxnLgrSeq": 10482869 + } + } + ], + "TransactionIndex": 10, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10483152, + "LimitAmount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "100" + }, + "Sequence": 22, + "SigningPubKey": "039371D0465097AC8F9C02EB60D5599AAD08AADBD623D6D40D642CF2D7C0481B83", + "TransactionType": "TrustSet", + "TxnSignature": "3044022048862FBF688AFFA81E8A08DB08C2559BB5D7E935B5E4597EE6ADF2E901BC9A6402200B7CDBAD2001CB9797AD56E22D9DA1C23F7F5DC1BB8DB22F013DEE78EAC46BE4", + "date": 471748760, + "hash": "8375C1ED96BB4DD66FD697F34755F9B03DB62CD098627B834028728670CF93EF" + } +} + diff --git a/test/fixtures/trustline-delete.json b/test/fixtures/trustline-delete.json new file mode 100644 index 00000000..d83139e3 --- /dev/null +++ b/test/fixtures/trustline-delete.json @@ -0,0 +1,157 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "BFD12A293442E8C083A98092C7A8A199A3732B2360643E40BB8704DE3DF8EA5E", + "ledger_index": 10482869, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.545330905250352" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.525330905250352" + } + }, + "PreviousTxnID": "D1B2B5508585E1BB48E1D76629C59F6368AAB9568457D058486DCC4DCAECCE30", + "PreviousTxnLgrSeq": 10482855 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Flags": 0, + "IndexPrevious": "00000000000001F1", + "Owner": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "RootIndex": "6319526CE8F9A8A44D7A870A89DC1B4AD848AA4F066FCB5390A9A268F6E16AEA" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "58E06162628C5E2292DA172A97573FA5613C4A223810686428BF8431B3D67C58" + } + }, + { + "DeletedNode": { + "FinalFields": { + "Flags": 0, + "Owner": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "RootIndex": "84C8F98961F2F10CB1B5C4FB649C18B05A9D3FC20C3A78B75C86CD30D7EAC39C" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "84C8F98961F2F10CB1B5C4FB649C18B05A9D3FC20C3A78B75C86CD30D7EAC39C" + } + }, + { + "ModifiedNode": { + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "A3C1529122C3DBD6C96B9DF009FF4896023FE6B4E05A508B1E81F3DCD9A6274B", + "PreviousTxnID": "758E69A2A2F5E7713ACCA70DC82DD89D7B45B9E020A7E19B9312F0C49A8834BA", + "PreviousTxnLgrSeq": 10482869 + } + }, + { + "DeletedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + }, + "Flags": 0, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001F2", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "0" + }, + "LowNode": "0000000000000000", + "PreviousTxnID": "CA491349DAF4D4EB1E5D2EF1DD4BBC84640C22B4C94C3C9AD40B190151A7878B", + "PreviousTxnLgrSeq": 10482862 + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.02" + }, + "Flags": 65536 + } + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "99752302", + "Flags": 0, + "OwnerCount": 0, + "Sequence": 22 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "PreviousFields": { + "Balance": "99764302", + "OwnerCount": 1, + "Sequence": 21 + }, + "PreviousTxnID": "CA491349DAF4D4EB1E5D2EF1DD4BBC84640C22B4C94C3C9AD40B190151A7878B", + "PreviousTxnLgrSeq": 10482862 + } + } + ], + "TransactionIndex": 9, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Amount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0.02" + }, + "Destination": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10482877, + "Sequence": 21, + "SigningPubKey": "039371D0465097AC8F9C02EB60D5599AAD08AADBD623D6D40D642CF2D7C0481B83", + "TransactionType": "Payment", + "TxnSignature": "3045022100E718DDE0149F89CF58FAD1847ED7AD9D61D833E07969A71FA7918D2F6D414AAE022041226ADCB68C2B64CA37AAB4C268922F2EE09DFBF8B234B282E9104643C6351D", + "date": 471747550, + "hash": "8A3F4CA1D349B4BE896DFDED6B6D0F0DCA4FCA75E082C30A4175813DDB9BCDA6" + } +} + diff --git a/test/fixtures/trustline-set-limit-to-zero.json b/test/fixtures/trustline-set-limit-to-zero.json new file mode 100644 index 00000000..f441addb --- /dev/null +++ b/test/fixtures/trustline-set-limit-to-zero.json @@ -0,0 +1,87 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "BCA3403873155ECFEB616ED9923D94430CB26EA7206CBCC4F86EACE47B33F950", + "ledger_index": 10460919, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.02" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "0" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "100" + } + }, + "PreviousTxnID": "1BBAC938211E7C2B5A0DD00153557E0A47B38F93AF0F1831C4ECE0E6387B8B96", + "PreviousTxnLgrSeq": 10460914 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "99940002", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 6 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "PreviousFields": { + "Balance": "99952002", + "Sequence": 5 + }, + "PreviousTxnID": "1BBAC938211E7C2B5A0DD00153557E0A47B38F93AF0F1831C4ECE0E6387B8B96", + "PreviousTxnLgrSeq": 10460914 + } + } + ], + "TransactionIndex": 8, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10460926, + "LimitAmount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "Sequence": 5, + "SigningPubKey": "039371D0465097AC8F9C02EB60D5599AAD08AADBD623D6D40D642CF2D7C0481B83", + "TransactionType": "TrustSet", + "TxnSignature": "3045022100CAF0DD8A64B68CC9CD44166A381FE07B69AAC5219BB3E90D5A2BD73FB9919DBA022071BE14E0E8ABB0B1841A7A4D1A983C8AC32CFA0E7C9A45FA3565AE5477C076A8", + "date": 471650760, + "hash": "B836ADBA4D77638C5F8C99B5FC0FC6A92D5F82FF3C90759F44773835BAD2AB86" + } +} diff --git a/test/fixtures/trustline-set-limit.json b/test/fixtures/trustline-set-limit.json new file mode 100644 index 00000000..21f753ed --- /dev/null +++ b/test/fixtures/trustline-set-limit.json @@ -0,0 +1,88 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "BB1814E087367EB56D7EBF0DB99481291894A53FF8B155511C6CC45D57F42E27", + "ledger_index": 10479523, + "status": "closed", + "type": "transaction", + "validated": true, + "metadata": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.02" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "200" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "100" + } + }, + "PreviousTxnID": "A9A654F03855FEBD714E49B2C190A62C310081339AA0AA49C38F0B734E81C173", + "PreviousTxnLgrSeq": 10479514 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Balance": "99884302", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 11 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "C24354B286600B8F28E51233B4AC41A3B4DDD0FDC9BCF96BB171573F6B40A4AE", + "PreviousFields": { + "Balance": "99896302", + "Sequence": 10 + }, + "PreviousTxnID": "BD850F01540CE35B68C1125E2055BBFEEF961D65AEFBC0CE39CF814685144117", + "PreviousTxnLgrSeq": 10479508 + } + } + ], + "TransactionIndex": 10, + "TransactionResult": "tesSUCCESS" + }, + "tx_json": { + "Account": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10479530, + "LimitAmount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "200" + }, + "Sequence": 10, + "SigningPubKey": "039371D0465097AC8F9C02EB60D5599AAD08AADBD623D6D40D642CF2D7C0481B83", + "TransactionType": "TrustSet", + "TxnSignature": "304502210088A7B69070516F1456AE93CC7189C213E60F1E5276F0FFEB64419CFD3A75FDBA02202E852593523240D5BD59B4DF3FAC97E112F75DF712B07E9C9FC64D824E2E0CB2", + "date": 471732720, + "hash": "9F54793122AD0CF1552A4E76991CB481E6FAD467176976500E11EA76CAC9D9EA" + } +} + diff --git a/test/metadata-test.js b/test/metadata-test.js new file mode 100644 index 00000000..a97f2ddd --- /dev/null +++ b/test/metadata-test.js @@ -0,0 +1,178 @@ +var assert = require('assert'); +var fs = require('fs'); +var utils = require('./testutils'); +var Meta = utils.load_module('meta').Meta; + +// Pay 100 XRP from rKmB to rLDY to create rLDY account +var createAccountBalanceChanges = [ + { address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '100', + currency: 'XRP', + issuer: '' + } + }, + { address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', + balance_change: { + value: '-100.012', + currency: 'XRP', + issuer: '' + }, + } +]; + +// Pay 0.01 USD from rKmB to rLDY where rLDY starts with no USD +var usdFirstPaymentBalanceChanges = [ + { address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', + balance_change: { + value: '-0.01', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '0.01', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', + balance_change: { + value: '-0.012', + currency: 'XRP', + issuer: '' + } + } +]; + +// Pay 0.2 USD from rLDY to rKmB where rLDY starts with 0.2 USD +var usdFullPaymentBalanceChanges = [ + { address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', + balance_change: { + value: '0.2', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '-0.2', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '-0.012', + currency: 'XRP', + issuer: '' + } + } +]; + +// Pay 0.01 USD from rKmB to rLDY where rLDY starts with 0.01 USD +var usdPaymentBalanceChanges = usdFirstPaymentBalanceChanges; + +// Set trust limit to 200 USD on rLDY when it has a trust limit of 100 USD +// and has a balance of 0.02 USD +var setTrustlineBalanceChanges = [ + { + address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '-0.012', + currency: 'XRP', + issuer: '' + } + } +]; + +// Set trust limit to 100 USD on rLDY when it has no trustline +var createTrustlineBalanceChanges = setTrustlineBalanceChanges; + +// Pay 0.02 USD from rLDY to rKmB when rLDY has a trust limit of 0 +// for USD, but still has a balance of 0.02 USD; which closes the trustline +var deleteTrustlineBalanceChanges = [ + { + address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', + balance_change: { + value: '0.02', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { + address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '-0.02', + currency: 'USD', + issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q' + } + }, + { + address: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K', + balance_change: { + value: '-0.012', + currency: 'XRP', + issuer: '' + } + } +]; + +// Set trust limit to zero on rLDY when it has a balance of 0.02 USD +var removeTrustBalanceChanges = setTrustlineBalanceChanges; + + +function loadFixture(filename) { + var path = __dirname + '/fixtures/' + filename; + return JSON.parse(fs.readFileSync(path)); +} + +function parseBalanceChanges(metadata) { + var meta = new Meta(metadata); + return JSON.parse(JSON.stringify(meta.parseBalanceChanges())); +} + +describe('parseBalanceChanges', function() { + it('XRP create account', function() { + var paymentResponse = loadFixture('payment-xrp-create-account.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, createAccountBalanceChanges); + }); + it('USD payment to account with no USD', function() { + var filename = 'payment-iou-destination-no-balance.json'; + var paymentResponse = loadFixture(filename); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, usdFirstPaymentBalanceChanges); + }); + it('USD payment of all USD in source account', function() { + var paymentResponse = loadFixture('payment-iou-spend-full-balance.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, usdFullPaymentBalanceChanges); + }); + it('USD payment to account with USD', function() { + var paymentResponse = loadFixture('payment-iou.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, usdPaymentBalanceChanges); + }); + it('Set trust limit to 0 with balance remaining', function() { + var paymentResponse = loadFixture('trustline-set-limit-to-zero.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, removeTrustBalanceChanges); + }); + it('Create trustline', function() { + var paymentResponse = loadFixture('trustline-create.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, createTrustlineBalanceChanges); + }); + it('Set trustline', function() { + var paymentResponse = loadFixture('trustline-set-limit.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, setTrustlineBalanceChanges); + }); + it('Delete trustline', function() { + var paymentResponse = loadFixture('trustline-delete.json'); + var result = parseBalanceChanges(paymentResponse.metadata); + assert.deepEqual(result, deleteTrustlineBalanceChanges); + }); +});