Merge pull request #391 from clark800/get-pathfind

Convert getPathFind and add unit test
This commit is contained in:
sublimator
2015-07-07 13:16:16 +07:00
13 changed files with 627 additions and 159 deletions

View File

@@ -7,14 +7,13 @@
"type": "object", "type": "object",
"properties": { "properties": {
"address": {"$ref": "address"}, "address": {"$ref": "address"},
"amounts": { "currencies": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"currency": {"$ref": "currency"}, "currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"}, "counterparty": {"$ref": "address"}
"value": {"$ref": "value"}
}, },
"required": ["currency"], "required": ["currency"],
"additionalProperties": false "additionalProperties": false

View File

@@ -38,7 +38,14 @@ function composeAsync(wrapper, callback) {
callback(error); callback(error);
return; return;
} }
callback(null, wrapper(data)); let result;
try {
result = wrapper(data);
} catch (exception) {
callback(exception);
return;
}
callback(null, result);
}; };
} }

View File

@@ -10,7 +10,7 @@ const getTransaction = require('./ledger/transaction');
const getAccountTransactions = require('./ledger/transactions'); const getAccountTransactions = require('./ledger/transactions');
const getTrustlines = require('./ledger/trustlines'); const getTrustlines = require('./ledger/trustlines');
const getBalances = require('./ledger/balances'); const getBalances = require('./ledger/balances');
// const getPathFind = require('./ledger/pathfind'); const getPathFind = require('./ledger/pathfind');
const getOrders = require('./ledger/orders'); const getOrders = require('./ledger/orders');
const getOrderBook = require('./ledger/orderbook'); const getOrderBook = require('./ledger/orderbook');
const getSettings = require('./ledger/settings'); const getSettings = require('./ledger/settings');
@@ -39,7 +39,7 @@ RippleAPI.prototype = {
getAccountTransactions, getAccountTransactions,
getTrustlines, getTrustlines,
getBalances, getBalances,
// getPathFind, getPathFind,
getOrders, getOrders,
getOrderBook, getOrderBook,
getSettings, getSettings,

View File

@@ -5,12 +5,6 @@ const utils = require('./utils');
const getTrustlines = require('./trustlines'); const getTrustlines = require('./trustlines');
const validate = utils.common.validate; const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync; const composeAsync = utils.common.composeAsync;
const dropsToXrp = utils.common.dropsToXrp;
function getXRPBalance(remote, address, ledgerVersion, callback) {
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
}
function getTrustlineBalanceAmount(trustline) { function getTrustlineBalanceAmount(trustline) {
return { return {
@@ -23,9 +17,10 @@ function getTrustlineBalanceAmount(trustline) {
function formatBalances(balances) { function formatBalances(balances) {
const xrpBalance = { const xrpBalance = {
currency: 'XRP', currency: 'XRP',
value: balances[0] value: balances.xrp
}; };
return [xrpBalance].concat(balances[1].map(getTrustlineBalanceAmount)); return [xrpBalance].concat(
balances.trustlines.map(getTrustlineBalanceAmount));
} }
function getBalances(account, options, callback) { function getBalances(account, options, callback) {
@@ -34,10 +29,10 @@ function getBalances(account, options, callback) {
const ledgerVersion = options.ledgerVersion const ledgerVersion = options.ledgerVersion
|| this.remote.getLedgerSequence(); || this.remote.getLedgerSequence();
async.parallel([ async.parallel({
_.partial(getXRPBalance, this.remote, account, ledgerVersion), xrp: _.partial(utils.getXRPBalance, this.remote, account, ledgerVersion),
_.partial(getTrustlines.bind(this), account, options) trustlines: _.partial(getTrustlines.bind(this), account, options)
], composeAsync(formatBalances, callback)); }, composeAsync(formatBalances, callback));
} }
module.exports = utils.wrapCatch(getBalances); module.exports = utils.wrapCatch(getBalances);

View File

@@ -1,7 +1,13 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash');
const parseAmount = require('./amount'); const parseAmount = require('./amount');
function parsePaths(paths) {
return paths.map(steps => steps.map(step =>
_.omit(step, ['type', 'type_hex'])));
}
function parsePathfind(sourceAddress: string, function parsePathfind(sourceAddress: string,
destinationAmount: Object, pathfindResult: Object): Object { destinationAmount: Object, pathfindResult: Object): Object {
return pathfindResult.alternatives.map(function(alternative) { return pathfindResult.alternatives.map(function(alternative) {
@@ -14,9 +20,7 @@ function parsePathfind(sourceAddress: string,
address: pathfindResult.destination_account, address: pathfindResult.destination_account,
amount: destinationAmount amount: destinationAmount
}, },
paths: JSON.stringify(alternative.paths_computed), paths: JSON.stringify(parsePaths(alternative.paths_computed))
allowPartialPayment: false,
noDirectRipple: false
}; };
}); });
} }

View File

@@ -1,121 +1,84 @@
/* eslint-disable valid-jsdoc */
'use strict'; 'use strict';
const _ = require('lodash');
const async = require('async'); const async = require('async');
const asyncify = require('simple-asyncify'); const BigNumber = require('bignumber.js');
const bignum = require('bignumber.js');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const parsePathfind = require('./parse/pathfind'); const parsePathfind = require('./parse/pathfind');
const NotFoundError = utils.common.errors.NotFoundError; const NotFoundError = utils.common.errors.NotFoundError;
const TimeOutError = utils.common.errors.TimeOutError; const composeAsync = utils.common.composeAsync;
/** function addParams(params, result) {
* Get a ripple path find, a.k.a. payment options, return _.assign({}, result, {
* for a given set of parameters and respond to the source_account: params.src_account,
* client with an array of fully-formed Payments. source_currencies: params.src_currencies,
* destination_amount: params.dst_amount
* @param {Remote} remote });
* @param {RippleAddress} req.params.source_account }
* @param {Amount Array ["USD r...,XRP,..."]} req.query.source_currencies
* - Note that Express.js middleware replaces "+" signs with spaces.
* Clients should use "+" signs but the values here will end up
* as spaces
* @param {RippleAddress} req.params.destination_account
* @param {Amount "1+USD+r..."} req.params.destination_amount_string
*/
function getPathFind(pathfind, callback) {
const self = this;
validate.pathfind(pathfind);
function prepareOptions() { function requestPathFind(remote, pathfind, callback) {
const pathfindParams = { const params = {
src_account: pathfind.source.address, src_account: pathfind.source.address,
dst_account: pathfind.destination.address, dst_account: pathfind.destination.address,
dst_amount: utils.common.toRippledAmount(pathfind.destination.amount) dst_amount: utils.common.toRippledAmount(pathfind.destination.amount)
}; };
if (typeof pathfindParams.dst_amount === 'object' if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
&& !pathfindParams.dst_amount.issuer) {
// Convert blank issuer to sender's address // Convert blank issuer to sender's address
// (Ripple convention for 'any issuer') // (Ripple convention for 'any issuer')
// https://ripple.com/build/transactions/ // https://ripple.com/build/transactions/
// #special-issuer-values-for-sendmax-and-amount // #special-issuer-values-for-sendmax-and-amount
// https://ripple.com/build/ripple-rest/#counterparties-in-payments // https://ripple.com/build/ripple-rest/#counterparties-in-payments
pathfindParams.dst_amount.issuer = pathfindParams.dst_account; params.dst_amount.issuer = params.dst_account;
} }
if (pathfind.source.amounts && pathfind.source.amounts.length > 0) { if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
pathfindParams.src_currencies = pathfind.source.amounts; params.src_currencies = pathfind.source.currencies.map(amount =>
} _.omit(utils.common.toRippledAmount(amount), 'value'));
return pathfindParams;
} }
function findPath(pathfindParams, _callback) { remote.requestRipplePathFind(params,
const request = self.remote.requestRipplePathFind(pathfindParams); composeAsync(_.partial(addParams, params), callback));
request.once('error', _callback); }
request.once('success', function(pathfindResults) {
pathfindResults.source_account = pathfindParams.src_account;
pathfindResults.source_currencies = pathfindParams.src_currencies;
pathfindResults.destination_amount = pathfindParams.dst_amount;
_callback(null, pathfindResults);
});
function reconnectRippled() { function addDirectXrpPath(paths, xrpBalance) {
self.remote.disconnect(function() { // Add XRP "path" only if the source acct has enough XRP to make the payment
self.remote.connect(); const destinationAmount = paths.destination_amount;
}); if ((new BigNumber(xrpBalance)).greaterThanOrEqualTo(destinationAmount)) {
} paths.alternatives.unshift({
request.timeout(utils.common.server.CONNECTION_TIMEOUT, function() {
request.removeAllListeners();
reconnectRippled();
_callback(new TimeOutError('Path request timeout'));
});
request.request();
}
function addDirectXrpPath(pathfindResults, _callback) {
// Check if destination_amount is XRP and if destination_account accepts XRP
if (typeof pathfindResults.destination_amount.currency === 'string'
|| pathfindResults.destination_currencies.indexOf('XRP') === -1) {
return _callback(null, pathfindResults);
}
// Check source_account balance
self.remote.requestAccountInfo({account: pathfindResults.source_account},
function(error, result) {
if (error) {
return _callback(new Error(
'Cannot get account info for source_account. ' + error));
}
if (!result || !result.account_data || !result.account_data.Balance) {
return _callback(new Error('Internal Error. Malformed account info : '
+ JSON.stringify(result)));
}
// Add XRP "path" only if the source_account has enough money
// to execute the payment
if (bignum(result.account_data.Balance).greaterThan(
pathfindResults.destination_amount)) {
pathfindResults.alternatives.unshift({
paths_canonical: [],
paths_computed: [], paths_computed: [],
source_amount: pathfindResults.destination_amount source_amount: paths.destination_amount
});
}
_callback(null, pathfindResults);
}); });
} }
return paths;
}
function formatPath(pathfindResults) { function isRippledIOUAmount(amount) {
const alternatives = pathfindResults.alternatives; // rippled XRP amounts are specified as decimal strings
if (alternatives && alternatives.length > 0) { return (typeof amount === 'object') &&
return parsePathfind(pathfindResults); amount.currency && (amount.currency !== 'XRP');
}
function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) {
callback(null, paths);
} else {
utils.getXRPBalance(remote, address, undefined,
composeAsync(_.partial(addDirectXrpPath, paths), callback));
} }
if (pathfindResults.destination_currencies.indexOf( }
pathfind.destination.amount.currency) === -1) {
function formatResponse(pathfind, paths) {
if (paths.alternatives && paths.alternatives.length > 0) {
const address = pathfind.source.address;
return parsePathfind(address, pathfind.destination.amount, paths);
}
if (!_.includes(paths.destination_currencies,
pathfind.destination.amount.currency)) {
throw new NotFoundError('No paths found. ' + throw new NotFoundError('No paths found. ' +
'The destination_account does not accept ' + 'The destination_account does not accept ' +
pathfind.destination.amount.currency + pathfind.destination.amount.currency + ', they only accept: ' +
', they only accept: ' + paths.destination_currencies.join(', '));
pathfindResults.destination_currencies.join(', ')); } else if (paths.source_currencies && paths.source_currencies.length > 0) {
} else if (pathfindResults.source_currencies
&& pathfindResults.source_currencies.length > 0) {
throw new NotFoundError('No paths found. Please ensure' + throw new NotFoundError('No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' + ' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' + ' the payment in one of the specified source_currencies. If it does' +
@@ -127,21 +90,16 @@ function getPathFind(pathfind, callback) {
' execute the payment. If it does there may be insufficient liquidity' + ' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now'); ' in the network to execute this payment right now');
} }
} }
function formatResponse(payments) { function getPathFind(pathfind, callback) {
return {payments: payments}; validate.pathfind(pathfind);
}
const steps = [ const address = pathfind.source.address;
asyncify(prepareOptions), async.waterfall([
findPath, _.partial(requestPathFind, this.remote, pathfind),
addDirectXrpPath, _.partial(conditionallyAddDirectXRPPath, this.remote, address)
asyncify(formatPath), ], composeAsync(_.partial(formatResponse, pathfind), callback));
asyncify(formatResponse)
];
async.waterfall(steps, callback);
} }
module.exports = utils.wrapCatch(getPathFind); module.exports = utils.wrapCatch(getPathFind);

View File

@@ -1,6 +1,13 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const common = require('../common'); const common = require('../common');
const dropsToXrp = common.dropsToXrp;
const composeAsync = common.composeAsync;
function getXRPBalance(remote, address, ledgerVersion, callback) {
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
}
// If the marker is omitted from a response, you have reached the end // If the marker is omitted from a response, you have reached the end
// getter(marker, limit, callback), callback(error, {marker, results}) // getter(marker, limit, callback), callback(error, {marker, results})
@@ -56,6 +63,7 @@ function signum(num) {
* @param {Object} second * @param {Object} second
* @returns {Number} [-1, 0, 1] * @returns {Number} [-1, 0, 1]
*/ */
function compareTransactions(first, second) { function compareTransactions(first, second) {
if (first.ledgerVersion === second.ledgerVersion) { if (first.ledgerVersion === second.ledgerVersion) {
return signum(Number(first.indexInLedger) - Number(second.indexInLedger)); return signum(Number(first.indexInLedger) - Number(second.indexInLedger));
@@ -64,6 +72,7 @@ function compareTransactions(first, second) {
} }
module.exports = { module.exports = {
getXRPBalance: getXRPBalance,
compareTransactions: compareTransactions, compareTransactions: compareTransactions,
renameCounterpartyToIssuer: renameCounterpartyToIssuer, renameCounterpartyToIssuer: renameCounterpartyToIssuer,
renameCounterpartyToIssuerInOrder: renameCounterpartyToIssuerInOrder, renameCounterpartyToIssuerInOrder: renameCounterpartyToIssuerInOrder,

View File

@@ -78,6 +78,7 @@ function setTransactionFields(transaction, input) {
* @returns {Number|String} numbers will be converted while strings * @returns {Number|String} numbers will be converted while strings
* are returned * are returned
*/ */
function convertTransferRate(transferRate) { function convertTransferRate(transferRate) {
if (_.isNumber(transferRate)) { if (_.isNumber(transferRate)) {
return transferRate * Math.pow(10, 9); return transferRate * Math.pow(10, 9);

View File

@@ -23,9 +23,8 @@ function getFeeDrops(remote) {
return remote.feeTx(feeUnits).to_text(); return remote.feeTx(feeUnits).to_text();
} }
/*:: type Callback = (err: ?(typeof Error), data: {tx_json: any}) => void */ function createTxJSON(transaction: any, remote: any, instructions: any,
function createTxJSON(transaction: any, remote: any, callback: (err: ?(typeof Error), data: {tx_json: any}) => void): void {
instructions: any, callback: Callback): void {
common.validate.instructions(instructions); common.validate.instructions(instructions);
transaction.complete(); transaction.complete();

View File

@@ -2,7 +2,7 @@
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert-diff'); const assert = require('assert-diff');
const setupAPI = require('./setup-api'); const setupAPI = require('./setup-api');
const address = require('./fixtures/addresses').ACCOUNT; const addresses = require('./fixtures/addresses');
const hashes = require('./fixtures/hashes'); const hashes = require('./fixtures/hashes');
const paymentSpecification = require('./fixtures/payment-specification'); const paymentSpecification = require('./fixtures/payment-specification');
const paymentResponse = require('./fixtures/payment-response'); const paymentResponse = require('./fixtures/payment-response');
@@ -30,6 +30,8 @@ const getSettingsResponse = require('./fixtures/get-settings-response');
const getOrdersResponse = require('./fixtures/get-orders-response'); const getOrdersResponse = require('./fixtures/get-orders-response');
const getOrderBookResponse = require('./fixtures/get-orderbook-response'); const getOrderBookResponse = require('./fixtures/get-orderbook-response');
const getServerInfoResponse = require('./fixtures/get-server-info-response'); const getServerInfoResponse = require('./fixtures/get-server-info-response');
const getPathFindResponse = require('./fixtures/get-pathfind-response');
const address = addresses.ACCOUNT;
function checkResult(expected, done, error, response) { function checkResult(expected, done, error, response) {
if (error) { if (error) {
@@ -170,4 +172,22 @@ describe('RippleAPI', function() {
done(); done();
}); });
}); });
it('getPathFind', function(done) {
const pathfind = {
source: {
address: address
},
destination: {
address: addresses.OTHER_ACCOUNT,
amount: {
currency: 'USD',
counterparty: addresses.ISSUER,
value: '100'
}
}
};
this.api.getPathFind(pathfind,
_.partial(checkResult, getPathFindResponse, done));
});
}); });

View File

@@ -0,0 +1,58 @@
[
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "JPY",
"value": "0.1117218827811721",
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"value": "100"
}
},
"paths": "[[{\"account\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9\"},{\"account\":\"rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}]]"
},
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "0.001002",
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"value": "100"
}
},
"paths": "[[{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}]]"
},
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "XRP",
"value": "0.207669"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"value": "100"
}
},
"paths": "[[{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"currency\":\"USD\",\"issuer\":\"rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc\"},{\"account\":\"rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc\"},{\"account\":\"rf9X8QoYnWLHMHuDfjkmRcD2UE5qX5aYV\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"currency\":\"USD\",\"issuer\":\"rDVdJ62foD1sn7ZpxtXyptdkBSyhsQGviT\"},{\"account\":\"rDVdJ62foD1sn7ZpxtXyptdkBSyhsQGviT\"},{\"account\":\"rfQPFZ3eLcaSUKjUy7A3LAmDNM4F9Hz9j1\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}],[{\"currency\":\"USD\",\"issuer\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT\"},{\"account\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"}]]"
}
]

410
test/fixtures/paths.js vendored Normal file
View File

@@ -0,0 +1,410 @@
'use strict';
module.exports.generateIOUPaymentPaths =
function(request_id, sendingAccount, destinationAccount, destinationAmount) {
return JSON.stringify({
'id': request_id,
'status': 'success',
'type': 'response',
'result': {
'alternatives': [
{
'paths_canonical': [],
'paths_computed': [
[
{
'account': 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': destinationAmount.currency,
'issuer': destinationAmount.issuer,
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
},
{
'currency': destinationAmount.currency,
'issuer': destinationAmount.issuer,
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
},
{
'currency': destinationAmount.currency,
'issuer': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
},
{
'currency': destinationAmount.currency,
'issuer': 'rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
]
],
'source_amount': {
'currency': 'JPY',
'issuer': sendingAccount,
'value': '0.1117218827811721'
}
},
{
'paths_canonical': [],
'paths_computed': [
[
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': destinationAmount.currency,
'issuer': destinationAmount.issuer,
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
},
{
'currency': destinationAmount.currency,
'issuer': destinationAmount.issuer,
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
},
{
'currency': destinationAmount.currency,
'issuer': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
]
],
'source_amount': {
'currency': 'USD',
'issuer': sendingAccount,
'value': '0.001002'
}
},
{
'paths_canonical': [],
'paths_computed': [
[
{
'currency': destinationAmount.currency,
'issuer': destinationAmount.issuer,
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'currency': destinationAmount.currency,
'issuer': 'rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': 'rf9X8QoYnWLHMHuDfjkmRcD2UE5qX5aYV',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'currency': destinationAmount.currency,
'issuer': 'rDVdJ62foD1sn7ZpxtXyptdkBSyhsQGviT',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rDVdJ62foD1sn7ZpxtXyptdkBSyhsQGviT',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': 'rfQPFZ3eLcaSUKjUy7A3LAmDNM4F9Hz9j1',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
],
[
{
'currency': destinationAmount.currency,
'issuer': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 48,
'type_hex': '0000000000000030'
},
{
'account': 'rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': destinationAmount.issuer,
'type': 1,
'type_hex': '0000000000000001'
}
]
],
'source_amount': '207669'
}
],
'destination_account': destinationAccount,
'destination_currencies': [
'USD',
'JOE',
'BTC',
'DYM',
'CNY',
'EUR',
'015841551A748AD2C1F76FF6ECB0CCCD00000000',
'MXN',
'XRP'
]
}
});
};
module.exports.generateXRPPaymentPaths =
function(request_id, sendingAccount, destinationAccount) {
return JSON.stringify({
'id': request_id,
'status': 'success',
'type': 'response',
'result': {
'alternatives': [
{
'paths_canonical': [],
'paths_computed': [
[
{
'account': 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
}
]
],
'source_amount': {
'currency': 'JPY',
'issuer': sendingAccount,
'value': '0.00005460001'
}
},
{
'paths_canonical': [],
'paths_computed': [
[
{
'account': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
}
],
[
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
}
],
[
{
'account': destinationAccount,
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
}
],
[
{
'account': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'type': 1,
'type_hex': '0000000000000001'
},
{
'account': 'r3MeEnYZY9fAd5pGjAWf4dfJsQBVY9FZRL',
'type': 1,
'type_hex': '0000000000000001'
},
{
'currency': 'XRP',
'type': 16,
'type_hex': '0000000000000010'
}
]
],
'source_amount': {
'currency': 'USD',
'issuer': sendingAccount,
'value': '0.0000005158508428100899'
}
}
],
'destination_account': destinationAccount,
'destination_currencies': [
'USD',
'XRP'
]
}
});
};

View File

@@ -8,6 +8,7 @@ const addresses = require('./fixtures/addresses');
const hashes = require('./fixtures/hashes'); const hashes = require('./fixtures/hashes');
const accountOffersResponse = require('./fixtures/acct-offers-response'); const accountOffersResponse = require('./fixtures/acct-offers-response');
const bookOffers = require('./fixtures/book-offers-response'); const bookOffers = require('./fixtures/book-offers-response');
const paths = require('./fixtures/paths');
function isUSD(json) { function isUSD(json) {
return json === 'USD' || json === '0000000000000000000000005553440000000000'; return json === 'USD' || json === '0000000000000000000000005553440000000000';
@@ -150,5 +151,12 @@ module.exports = function(port) {
} }
}); });
mock.on('request_ripple_path_find', function(request, conn) {
const response = paths.generateIOUPaymentPaths(request.id,
request.source_account, request.destination_account,
request.destination_amount);
conn.send(response);
});
return mock; return mock;
}; };