From 6b8cd6151d8f303bf664a9f0be717d3a4b638544 Mon Sep 17 00:00:00 2001 From: Alan Cohen Date: Fri, 15 Jan 2016 13:03:42 -0800 Subject: [PATCH] Filter insufficient source funds paths from pathfind results When pathfinding with source amount, we need to filter out paths where source amount is not equal to the specified source amount. This is due to the behavior of rippled when specifying a source amount during pathfinding. Example: { "command": "ripple_path_find", "source_account": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF", "destination_account": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "destination_amount": { "currency": "EUR", "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "value": -1 }, "send_max": { "currency": "USD", "issuer": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF", "value": "1234567891" }, "id": 2 } { "id": 2, "result": { "alternatives": [ { "destination_amount": { "currency": "EUR", "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "value": "3999889.62127857" }, "paths_canonical": [], "paths_computed": [ [ { "account": "rcsxQxEqU2qquAKp3tBUJy8Z2t19ioQPJ", "type": 1, "type_hex": "0000000000000001" }, { "currency": "EUR", "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "type": 48, "type_hex": "0000000000000030" } ] ], "source_amount": { "currency": "USD", "issuer": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF", "value": "4170759.906037564" } } ], "destination_account": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "destination_amount": { "currency": "EUR", "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb", "value": "-1" }, "destination_currencies": [ "EUR", "XRP" ], "full_reply": true, "id": 2, "source_account": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF", "status": "success" }, "status": "success", "type": "response" } --- src/ledger/pathfind.js | 18 +++- test/api-test.js | 9 ++ test/fixtures/addresses.js | 3 +- .../getpaths/no-paths-source-amount.json | 13 +++ test/fixtures/requests/index.js | 1 + test/fixtures/rippled/index.js | 3 +- .../fixtures/rippled/path-find-srcAmtLow.json | 97 +++++++++++++++++++ test/mock-rippled.js | 2 + 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/requests/getpaths/no-paths-source-amount.json create mode 100644 test/fixtures/rippled/path-find-srcAmtLow.json diff --git a/src/ledger/pathfind.js b/src/ledger/pathfind.js index e633ab68..7b934707 100644 --- a/src/ledger/pathfind.js +++ b/src/ledger/pathfind.js @@ -85,6 +85,20 @@ function conditionallyAddDirectXRPPath(connection: Connection, address: string, xrpBalance => addDirectXrpPath(paths, xrpBalance)); } +function filterSourceFundsLowPaths(pathfind: PathFind, + paths: RippledPathsResponse +): RippledPathsResponse { + if (pathfind.source.amount && + pathfind.destination.amount.value === undefined && paths.alternatives) { + paths.alternatives = _.filter(paths.alternatives, alt => { + return alt.source_amount && + pathfind.source.amount && + alt.source_amount.value === pathfind.source.amount.value; + }); + } + return paths; +} + function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) { if (paths.alternatives && paths.alternatives.length > 0) { return parsePathfind(paths); @@ -116,7 +130,9 @@ function getPaths(pathfind: PathFind): Promise { const address = pathfind.source.address; return requestPathFind(this.connection, pathfind).then(paths => conditionallyAddDirectXRPPath(this.connection, address, paths) - ).then(paths => formatResponse(pathfind, paths)); + ) + .then(paths => filterSourceFundsLowPaths(pathfind, paths)) + .then(paths => formatResponse(pathfind, paths)); } module.exports = getPaths; diff --git a/test/api-test.js b/test/api-test.js index 712ae8d4..4d1d5e08 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -1049,6 +1049,15 @@ describe('RippleAPI', function() { }); }); + it('getPaths - no paths source amount', function() { + return this.api.getPaths(requests.getPaths.NoPathsSource).then(() => { + assert(false, 'Should throw NotFoundError'); + }).catch(error => { + assert(error instanceof this.api.errors.NotFoundError); + }); + }); + + it('getPaths - no paths with source currencies', function() { const pathfind = requests.getPaths.NoPathsWithCurrencies; return this.api.getPaths(pathfind).then(() => { diff --git a/test/fixtures/addresses.js b/test/fixtures/addresses.js index 40de71da..813030cc 100644 --- a/test/fixtures/addresses.js +++ b/test/fixtures/addresses.js @@ -6,5 +6,6 @@ module.exports = { FOURTH_ACCOUNT: 'rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr', ISSUER: 'rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM', NOTFOUND: 'rajTAg3hon5Lcu1RxQQPxTgHvqfhc1EaUS', - SECRET: 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV' + SECRET: 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV', + SOURCE_LOW_FUNDS: 'rhVgDEfS1r1fLyRUZCpab4TdowZcAJwHy2' }; diff --git a/test/fixtures/requests/getpaths/no-paths-source-amount.json b/test/fixtures/requests/getpaths/no-paths-source-amount.json new file mode 100644 index 00000000..0d371a62 --- /dev/null +++ b/test/fixtures/requests/getpaths/no-paths-source-amount.json @@ -0,0 +1,13 @@ +{ + "source": { + "address": "rhVgDEfS1r1fLyRUZCpab4TdowZcAJwHy2", + "amount": { + "value": "1000002", + "currency": "USD" + } + }, + "destination": { + "address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + "amount": {"currency": "USD"} + } +} diff --git a/test/fixtures/requests/index.js b/test/fixtures/requests/index.js index 199a5c0c..16d421bb 100644 --- a/test/fixtures/requests/index.js +++ b/test/fixtures/requests/index.js @@ -53,6 +53,7 @@ module.exports = { XrpToXrpNotEnough: require('./getpaths/xrp2xrp-not-enough'), NotAcceptCurrency: require('./getpaths/not-accept-currency'), NoPaths: require('./getpaths/no-paths'), + NoPathsSource: require('./getpaths/no-paths-source-amount'), NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies'), sendAll: require('./getpaths/send-all'), invalid: require('./getpaths/invalid'), diff --git a/test/fixtures/rippled/index.js b/test/fixtures/rippled/index.js index 9ef0d97a..56191fc3 100644 --- a/test/fixtures/rippled/index.js +++ b/test/fixtures/rippled/index.js @@ -38,7 +38,8 @@ module.exports = { sendUSD: require('./path-find-send-usd'), sendAll: require('./path-find-send-all'), XrpToXrp: require('./path-find-xrp-to-xrp'), - srcActNotFound: require('./path-find-srcActNotFound') + srcActNotFound: require('./path-find-srcActNotFound'), + sourceAmountLow: require('./path-find-srcAmtLow') }, tx: { Payment: require('./tx/payment.json'), diff --git a/test/fixtures/rippled/path-find-srcAmtLow.json b/test/fixtures/rippled/path-find-srcAmtLow.json new file mode 100644 index 00000000..66313fef --- /dev/null +++ b/test/fixtures/rippled/path-find-srcAmtLow.json @@ -0,0 +1,97 @@ +{ + "id": 0, + "result": { + "full_reply": true, + "alternatives": [ + { + "paths_canonical": [], + "paths_computed": [ + [ + { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "type": 1, + "type_hex": "0000000000000001" + } + ], + [ + { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "type": 1, + "type_hex": "0000000000000001" + }, + { + "account": "rLMJ4db4uwHcd6NHg6jvTaYb8sH5Gy4tg5", + "type": 1, + "type_hex": "0000000000000001" + }, + { + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "type": 1, + "type_hex": "0000000000000001" + } + ], + [ + { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "type": 1, + "type_hex": "0000000000000001" + }, + { + "currency": "USD", + "issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "type": 48, + "type_hex": "0000000000000030" + }, + { + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "type": 1, + "type_hex": "0000000000000001" + } + ], + [ + { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "type": 1, + "type_hex": "0000000000000001" + }, + { + "account": "rLMJ4db4uwHcd6NHg6jvTaYb8sH5Gy4tg5", + "type": 1, + "type_hex": "0000000000000001" + }, + { + "account": "r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X", + "type": 1, + "type_hex": "0000000000000001" + } + ] + ], + "source_amount": { + "currency": "USD", + "issuer": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + "value": "0.000001002" + } + } + ], + "source_account": "rhVgDEfS1r1fLyRUZCpab4TdowZcAJwHy2", + "destination_amount": { + "currency": "USD", + "value": "-1", + "issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" + }, + "destination_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + "destination_currencies": [ + "JOE", + "BTC", + "DYM", + "CNY", + "EUR", + "015841551A748AD2C1F76FF6ECB0CCCD00000000", + "MXN", + "USD", + "XRP" + ] + }, + "status": "success", + "type": "response" +} diff --git a/test/mock-rippled.js b/test/mock-rippled.js index 6ca63497..da2efc1a 100644 --- a/test/mock-rippled.js +++ b/test/mock-rippled.js @@ -323,6 +323,8 @@ module.exports = function(port) { } if (request.source_account === addresses.NOTFOUND) { response = createResponse(request, fixtures.path_find.srcActNotFound); + } else if (request.source_account === addresses.SOURCE_LOW_FUNDS) { + response = createResponse(request, fixtures.path_find.sourceAmountLow); } else if (request.source_account === addresses.OTHER_ACCOUNT) { response = createResponse(request, fixtures.path_find.sendUSD); } else if (request.source_account === addresses.THIRD_ACCOUNT) {