Support source.amount in getPaths and destination.minAmount in preparePayment

This commit is contained in:
Chris Clark
2015-09-29 11:03:46 -07:00
parent 8edc3b1f36
commit 35acbb62c3
29 changed files with 641 additions and 91 deletions

View File

@@ -4,20 +4,8 @@
"type": "object", "type": "object",
"properties": { "properties": {
"address": {"$ref": "address"}, "address": {"$ref": "address"},
"amount": { "amount": {"$ref": "amount"},
"type": "object", "tag": {"$ref": "tag"}
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"value": {"$ref": "value"}
},
"required": ["currency", "value"],
"additionalProperties": false
},
"tag": {
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
"$ref": "uint32"
}
}, },
"required": ["address", "amount"], "required": ["address", "amount"],
"additionalProperties": false "additionalProperties": false

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "destinationAdjustment",
"type": "object",
"oneOf": [
{"$ref": "adjustment"},
{"$ref": "minAdjustment"}
]
}

View File

@@ -5,8 +5,8 @@
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"source": {"$ref": "maxAdjustment"}, "source": {"$ref": "sourceAdjustment"},
"destination": {"$ref": "adjustment"}, "destination": {"$ref": "destinationAdjustment"},
"paths": {"type": "string"} "paths": {"type": "string"}
}, },
"required": ["source", "destination", "paths"], "required": ["source", "destination", "paths"],

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "laxAmount",
"description": "Amount where counterparty is optional",
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"value": {"$ref": "value"}
},
"required": ["currency", "value"],
"additionalProperties": false
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "laxLaxAmount",
"description": "Amount where counterparty and value are optional",
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"value": {"$ref": "value"}
},
"required": ["currency"],
"additionalProperties": false
}

View File

@@ -4,20 +4,8 @@
"type": "object", "type": "object",
"properties": { "properties": {
"address": {"$ref": "address"}, "address": {"$ref": "address"},
"maxAmount": { "maxAmount": {"$ref": "laxAmount"},
"type": "object", "tag": {"$ref": "tag"}
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"value": {"$ref": "value"}
},
"required": ["currency", "value"],
"additionalProperties": false
},
"tag": {
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
"$ref": "uint32"
}
}, },
"required": ["address", "maxAmount"], "required": ["address", "maxAmount"],
"additionalProperties": false "additionalProperties": false

View File

@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "minAdjustment",
"type": "object",
"properties": {
"address": {"$ref": "address"},
"minAmount": {"$ref": "laxAmount"},
"tag": {"$ref": "tag"}
},
"required": ["address", "minAmount"],
"additionalProperties": false
}

View File

@@ -7,6 +7,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"address": {"$ref": "address"}, "address": {"$ref": "address"},
"amount": {"$ref": "laxAmount"},
"currencies": { "currencies": {
"type": "array", "type": "array",
"items": { "items": {
@@ -19,12 +20,23 @@
"additionalProperties": false "additionalProperties": false
}, },
"uniqueItems": true "uniqueItems": true
},
"not": {
"required": ["amount", "currencies"]
} }
}, },
"additionalProperties": false, "additionalProperties": false,
"required": ["address"] "required": ["address"]
}, },
"destination": {"$ref": "adjustment"} "destination": {
"type": "object",
"properties": {
"address": {"$ref": "address"},
"amount": {"$ref": "laxLaxAmount"}
},
"required": ["address", "amount"],
"additionalProperties": false
}
}, },
"required": ["source", "destination"], "required": ["source", "destination"],
"additionalProperties": false "additionalProperties": false

View File

@@ -3,8 +3,8 @@
"title": "payment", "title": "payment",
"type": "object", "type": "object",
"properties": { "properties": {
"source": {"$ref": "maxAdjustment"}, "source": {"$ref": "sourceAdjustment"},
"destination": {"$ref": "adjustment"}, "destination": {"$ref": "destinationAdjustment"},
"paths": {"type": "string"}, "paths": {"type": "string"},
"memos": { "memos": {
"type": "array", "type": "array",

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "sourceAdjustment",
"type": "object",
"oneOf": [
{"$ref": "adjustment"},
{"$ref": "maxAdjustment"}
]
}

View File

@@ -0,0 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "tag",
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
"$ref": "uint32"
}

View File

@@ -8,22 +8,42 @@ function parsePaths(paths) {
_.omit(step, ['type', 'type_hex']))); _.omit(step, ['type', 'type_hex'])));
} }
function parsePathfind(sourceAddress: string, function removeAnyCounterpartyEncoding(address: string, amount: Object) {
destinationAmount: Object, pathfindResult: Object return amount.counterparty === address ?
): Object { _.omit(amount, 'counterparty') : amount;
return pathfindResult.alternatives.map(function(alternative) { }
return {
source: { function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
address: sourceAddress, const amountKey = _.keys(adjustmentWithoutAddress)[0];
maxAmount: parseAmount(alternative.source_amount) const amount = adjustmentWithoutAddress[amountKey];
}, return _.set({address: address}, amountKey,
destination: { removeAnyCounterpartyEncoding(address, amount));
address: pathfindResult.destination_account, }
amount: destinationAmount
}, function parseAlternative(sourceAddress: string, destinationAddress: string,
paths: JSON.stringify(parsePaths(alternative.paths_computed)) destinationAmount: Object, alternative: Object
}; ) {
}); // we use "maxAmount"/"minAmount" here so that the result can be passed
// directly to preparePayment
const amounts = (alternative.destination_amount !== undefined) ?
{source: {amount: parseAmount(alternative.source_amount)},
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
{source: {maxAmount: parseAmount(alternative.source_amount)},
destination: {amount: parseAmount(destinationAmount)}};
return {
source: createAdjustment(sourceAddress, amounts.source),
destination: createAdjustment(destinationAddress, amounts.destination),
paths: JSON.stringify(parsePaths(alternative.paths_computed))
};
}
function parsePathfind(pathfindResult: Object): Object {
const sourceAddress = pathfindResult.source_account;
const destinationAddress = pathfindResult.destination_account;
const destinationAmount = pathfindResult.destination_amount;
return pathfindResult.alternatives.map(_.partial(parseAlternative,
sourceAddress, destinationAddress, destinationAmount));
} }
module.exports = parsePathfind; module.exports = parsePathfind;

View File

@@ -4,15 +4,18 @@ const _ = require('lodash');
const async = require('async'); const async = require('async');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate;
const parsePathfind = require('./parse/pathfind'); const parsePathfind = require('./parse/pathfind');
const validate = utils.common.validate;
const NotFoundError = utils.common.errors.NotFoundError; const NotFoundError = utils.common.errors.NotFoundError;
const ValidationError = utils.common.errors.ValidationError;
const composeAsync = utils.common.composeAsync; const composeAsync = utils.common.composeAsync;
const convertErrors = utils.common.convertErrors; const convertErrors = utils.common.convertErrors;
const toRippledAmount = utils.common.toRippledAmount;
type PathFindParams = { type PathFindParams = {
src_currencies?: Array<string>, src_account: string, dst_amount: string, src_currencies?: Array<string>, src_account: string,
dst_account?: string dst_amount: string | Object, dst_account?: string,
src_amount?: string | Object
} }
function addParams(params: PathFindParams, result: {}) { function addParams(params: PathFindParams, result: {}) {
@@ -29,10 +32,11 @@ type PathFind = {
} }
function requestPathFind(remote, pathfind: PathFind, callback) { function requestPathFind(remote, pathfind: PathFind, callback) {
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
const params: PathFindParams = { const params: PathFindParams = {
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: toRippledAmount(destinationAmount)
}; };
if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) { if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
// Convert blank issuer to sender's address // Convert blank issuer to sender's address
@@ -44,7 +48,17 @@ function requestPathFind(remote, pathfind: PathFind, callback) {
} }
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) { if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
params.src_currencies = pathfind.source.currencies.map(amount => params.src_currencies = pathfind.source.currencies.map(amount =>
_.omit(utils.common.toRippledAmount(amount), 'value')); _.omit(toRippledAmount(amount), 'value'));
}
if (pathfind.source.amount) {
if (pathfind.destination.amount.value !== undefined) {
throw new ValidationError('Cannot specify both source.amount'
+ ' and destination.amount.value in getPaths');
}
params.src_amount = toRippledAmount(pathfind.source.amount);
if (params.src_amount.currency && !params.src_amount.issuer) {
params.src_amount.issuer = pathfind.source.address;
}
} }
remote.createPathFind(params, remote.createPathFind(params,
@@ -81,8 +95,7 @@ function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
function formatResponse(pathfind, paths) { function formatResponse(pathfind, paths) {
if (paths.alternatives && paths.alternatives.length > 0) { if (paths.alternatives && paths.alternatives.length > 0) {
const address = pathfind.source.address; return parsePathfind(paths);
return parsePathfind(address, pathfind.destination.amount, paths);
} }
if (paths.destination_currencies !== undefined && if (paths.destination_currencies !== undefined &&
!_.includes(paths.destination_currencies, !_.includes(paths.destination_currencies,

View File

@@ -5,6 +5,7 @@ const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount; const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction; const Transaction = utils.common.core.Transaction;
const ValidationError = utils.common.errors.ValidationError;
function isXRPToXRPPayment(payment) { function isXRPToXRPPayment(payment) {
const sourceCurrency = _.get(payment, 'source.maxAmount.currency'); const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
@@ -23,24 +24,49 @@ function applyAnyCounterpartyEncoding(payment) {
// 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
if (isIOUWithoutCounterparty(payment.source.maxAmount)) { _.forEach([payment.source, payment.destination], (adjustment) => {
payment.source.maxAmount.counterparty = payment.source.address; _.forEach(['amount', 'minAmount', 'maxAmount'], (key) => {
} if (isIOUWithoutCounterparty(adjustment[key])) {
if (isIOUWithoutCounterparty(payment.destination.amount)) { adjustment[key].counterparty = adjustment.address;
payment.destination.amount.counterparty = payment.destination.address; }
} });
});
} }
function createPaymentTransaction(account, payment) { function createMaximalAmount(amount) {
const maxXRPValue = '100000000000';
const maxIOUValue = '9999999999999999e80';
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
return _.assign(amount, {value: maxValue});
}
function createPaymentTransaction(account, paymentArgument) {
const payment = _.cloneDeep(paymentArgument);
applyAnyCounterpartyEncoding(payment); applyAnyCounterpartyEncoding(payment);
validate.address(account); validate.address(account);
validate.payment(payment); validate.payment(payment);
if ((payment.source.maxAmount && payment.destination.minAmount) ||
(payment.source.amount && payment.destination.amount)) {
throw new ValidationError('payment must specify either (source.maxAmount '
+ 'and destination.amount) or (source.amount and destination.minAmount)');
}
// when using destination.minAmount, rippled still requires that we set
// a destination amount in addition to DeliverMin. the destination amount
// is interpreted as the maximum amount to send. we want to be sure to
// send the whole source amount, so we set the destination amount to the
// maximum possible amount. otherwise it's possible that the destination
// cap could be hit before the source cap.
const amount = payment.destination.minAmount && !isXRPToXRPPayment(payment) ?
createMaximalAmount(payment.destination.minAmount) :
(payment.destination.amount || payment.destination.minAmount);
const transaction = new Transaction(); const transaction = new Transaction();
transaction.payment({ transaction.payment({
from: payment.source.address, from: payment.source.address,
to: payment.destination.address, to: payment.destination.address,
amount: toRippledAmount(payment.destination.amount) amount: toRippledAmount(amount)
}); });
if (payment.invoiceID) { if (payment.invoiceID) {
@@ -57,9 +83,6 @@ function createPaymentTransaction(account, payment) {
transaction.addMemo(memo.type, memo.format, memo.data) transaction.addMemo(memo.type, memo.format, memo.data)
); );
} }
if (payment.allowPartialPayment) {
transaction.setFlags(['PartialPayment']);
}
if (payment.noDirectRipple) { if (payment.noDirectRipple) {
transaction.setFlags(['NoRippleDirect']); transaction.setFlags(['NoRippleDirect']);
} }
@@ -71,11 +94,22 @@ function createPaymentTransaction(account, payment) {
// temREDUNDANT_SEND_MAX removed in: // temREDUNDANT_SEND_MAX removed in:
// https://github.com/ripple/rippled/commit/ // https://github.com/ripple/rippled/commit/
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/ // c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
transaction.sendMax(toRippledAmount(payment.source.maxAmount)); if (payment.allowPartialPayment || payment.destination.minAmount) {
transaction.setFlags(['PartialPayment']);
}
transaction.setSendMax(toRippledAmount(
payment.source.maxAmount || payment.source.amount));
if (payment.destination.minAmount) {
transaction.setDeliverMin(toRippledAmount(payment.destination.minAmount));
}
if (payment.paths) { if (payment.paths) {
transaction.paths(JSON.parse(payment.paths)); transaction.paths(JSON.parse(payment.paths));
} }
} else if (payment.allowPartialPayment) {
throw new ValidationError('XRP to XRP payments cannot be partial payments');
} }
return transaction; return transaction;

View File

@@ -11,7 +11,8 @@ const Amount = require('./amount').Amount;
* the 'end' and 'superceded' events. * the 'end' and 'superceded' events.
*/ */
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies function PathFind(remote, src_account, dst_account, dst_amount,
src_currencies, src_amount
) { ) {
EventEmitter.call(this); EventEmitter.call(this);
@@ -21,6 +22,7 @@ function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
this.dst_account = dst_account; this.dst_account = dst_account;
this.dst_amount = dst_amount; this.dst_amount = dst_amount;
this.src_currencies = src_currencies; this.src_currencies = src_currencies;
this.src_amount = src_amount;
} }
util.inherits(PathFind, EventEmitter); util.inherits(PathFind, EventEmitter);
@@ -42,7 +44,8 @@ PathFind.prototype.create = function() {
source_account: this.src_account, source_account: this.src_account,
destination_account: this.dst_account, destination_account: this.dst_account,
destination_amount: this.dst_amount, destination_amount: this.dst_amount,
source_currencies: this.src_currencies source_currencies: this.src_currencies,
send_max: this.src_amount
}); });
req.once('error', function(err) { req.once('error', function(err) {

View File

@@ -1824,7 +1824,7 @@ Remote.prototype.createPathFind = function(options, callback) {
const pathFind = new PathFind(this, const pathFind = new PathFind(this,
options.src_account, options.dst_account, options.src_account, options.dst_account,
options.dst_amount, options.src_currencies); options.dst_amount, options.src_currencies, options.src_amount);
if (this._cur_path_find) { if (this._cur_path_find) {
this._cur_path_find.notify_superceded(); this._cur_path_find.notify_superceded();
@@ -2172,6 +2172,10 @@ Remote.prototype.requestPathFindCreate = function(options, callback) {
options.source_currencies.map(Remote.prepareCurrency); options.source_currencies.map(Remote.prepareCurrency);
} }
if (options.send_max) {
request.message.send_max = Amount.json_rewrite(options.send_max);
}
request.callback(callback); request.callback(callback);
return request; return request;
}; };

View File

@@ -48,7 +48,7 @@ describe('RippleAPI', function() {
}, instructions); }, instructions);
return this.api.preparePayment( return this.api.preparePayment(
address, requests.preparePayment, localInstructions).then( address, requests.preparePayment, localInstructions).then(
_.partial(checkResult, responses.preparePayment, 'prepare')); _.partial(checkResult, responses.preparePayment.normal, 'prepare'));
}); });
it('preparePayment with all options specified', function() { it('preparePayment with all options specified', function() {
@@ -59,7 +59,7 @@ describe('RippleAPI', function() {
}; };
return this.api.preparePayment( return this.api.preparePayment(
address, requests.preparePaymentAllOptions, localInstructions).then( address, requests.preparePaymentAllOptions, localInstructions).then(
_.partial(checkResult, responses.preparePaymentAllOptions, 'prepare')); _.partial(checkResult, responses.preparePayment.allOptions, 'prepare'));
}); });
}); });
@@ -67,10 +67,16 @@ describe('RippleAPI', function() {
const localInstructions = _.defaults({sequence: 23}, instructions); const localInstructions = _.defaults({sequence: 23}, instructions);
return this.api.preparePayment( return this.api.preparePayment(
address, requests.preparePaymentNoCounterparty, localInstructions).then( address, requests.preparePaymentNoCounterparty, localInstructions).then(
_.partial(checkResult, responses.preparePaymentNoCounterparty, _.partial(checkResult, responses.preparePayment.noCounterparty,
'prepare')); 'prepare'));
}); });
it('preparePayment - destination.minAmount', function() {
return this.api.preparePayment(address, responses.getPaths.sendAll[0],
instructions).then(_.partial(checkResult,
responses.preparePayment.minAmount, 'prepare'));
});
it('prepareOrder - buy order', function() { it('prepareOrder - buy order', function() {
return this.api.prepareOrder(address, requests.prepareOrder, instructions) return this.api.prepareOrder(address, requests.prepareOrder, instructions)
.then(_.partial(checkResult, responses.prepareOrder, 'prepare')); .then(_.partial(checkResult, responses.prepareOrder, 'prepare'));
@@ -639,6 +645,11 @@ describe('RippleAPI', function() {
}); });
}); });
it('getPaths - send all', function() {
return this.api.getPaths(requests.getPaths.sendAll).then(
_.partial(checkResult, responses.getPaths.sendAll, 'getPaths'));
});
it('getLedgerVersion', function(done) { it('getLedgerVersion', function(done) {
this.api.getLedgerVersion().then((ver) => { this.api.getLedgerVersion().then((ver) => {
assert.strictEqual(ver, 8819951); assert.strictEqual(ver, 8819951);

View File

@@ -0,0 +1,15 @@
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "5"
}
},
"destination": {
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"amount": {
"currency": "USD"
}
}
}

View File

@@ -25,7 +25,8 @@ module.exports = {
XrpToXrpNotEnough: require('./getpaths/xrp2xrp-not-enough'), XrpToXrpNotEnough: require('./getpaths/xrp2xrp-not-enough'),
NotAcceptCurrency: require('./getpaths/not-accept-currency'), NotAcceptCurrency: require('./getpaths/not-accept-currency'),
NoPaths: require('./getpaths/no-paths'), NoPaths: require('./getpaths/no-paths'),
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies') NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies'),
sendAll: require('./getpaths/send-all')
}, },
computeLedgerHash: { computeLedgerHash: {
header: require('./compute-ledger-hash'), header: require('./compute-ledger-hash'),

View File

@@ -23,7 +23,6 @@
} }
], ],
"invoiceID": "A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A", "invoiceID": "A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A",
"allowPartialPayment": true,
"noDirectRipple": true, "noDirectRipple": true,
"limitQuality": true, "limitQuality": true,
"paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"type\":49,\"type_hex\":\"0000000000000031\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]" "paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"type\":49,\"type_hex\":\"0000000000000031\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]"

View File

@@ -0,0 +1,70 @@
[
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "5"
}
},
"destination": {
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"minAmount": {
"currency": "USD",
"value": "4.93463759481038"
}
},
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
},
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "5"
}
},
"destination": {
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"minAmount": {
"currency": "USD",
"value": "4.93463759481038"
}
},
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"EUR\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun\"},{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}]]"
},
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "5"
}
},
"destination": {
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"minAmount": {
"currency": "USD",
"value": "4.93463759481038"
}
},
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"JPY\",\"issuer\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
},
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "USD",
"value": "5"
}
},
"destination": {
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"minAmount": {
"currency": "USD",
"value": "4.990019960079841"
}
},
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
}
]

View File

@@ -4,8 +4,7 @@
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", "address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"maxAmount": { "maxAmount": {
"currency": "USD", "currency": "USD",
"value": "0.000001002", "value": "0.000001002"
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo"
} }
}, },
"destination": { "destination": {

View File

@@ -4,8 +4,7 @@
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", "address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": { "maxAmount": {
"currency": "JPY", "currency": "JPY",
"value": "0.1117218827811721", "value": "0.1117218827811721"
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
} }
}, },
"destination": { "destination": {
@@ -23,8 +22,7 @@
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", "address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": { "maxAmount": {
"currency": "USD", "currency": "USD",
"value": "0.001002", "value": "0.001002"
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
} }
}, },
"destination": { "destination": {

View File

@@ -9,7 +9,8 @@ module.exports = {
getPaths: { getPaths: {
XrpToUsd: require('./get-paths.json'), XrpToUsd: require('./get-paths.json'),
UsdToUsd: require('./get-paths-send-usd.json'), UsdToUsd: require('./get-paths-send-usd.json'),
XrpToXrp: require('./get-paths-xrp-to-xrp.json') XrpToXrp: require('./get-paths-xrp-to-xrp.json'),
sendAll: require('./get-paths-send-all.json')
}, },
getServerInfo: require('./get-server-info.json'), getServerInfo: require('./get-server-info.json'),
getSettings: require('./get-settings.json'), getSettings: require('./get-settings.json'),
@@ -35,10 +36,12 @@ module.exports = {
prepareOrderCancellation: require('./prepare-order-cancellation.json'), prepareOrderCancellation: require('./prepare-order-cancellation.json'),
prepareOrder: require('./prepare-order.json'), prepareOrder: require('./prepare-order.json'),
prepareOrderSell: require('./prepare-order-sell.json'), prepareOrderSell: require('./prepare-order-sell.json'),
preparePayment: require('./prepare-payment.json'), preparePayment: {
preparePaymentAllOptions: require('./prepare-payment-all-options.json'), normal: require('./prepare-payment.json'),
preparePaymentNoCounterparty: allOptions: require('./prepare-payment-all-options.json'),
require('./prepare-payment-no-counterparty.json'), noCounterparty: require('./prepare-payment-no-counterparty.json'),
minAmount: require('./prepare-payment-min-amount.json')
},
prepareSettings: { prepareSettings: {
regularKey: require('./prepare-settings-regular-key.json'), regularKey: require('./prepare-settings-regular-key.json'),
flags: require('./prepare-settings.json'), flags: require('./prepare-settings.json'),

View File

@@ -1,5 +1,5 @@
{ {
"txJSON": "{\"Flags\":458752,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}", "txJSON": "{\"Flags\":327680,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": { "instructions": {
"fee": "12", "fee": "12",
"sequence": 23, "sequence": 23,

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":131072,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\",\"Amount\":{\"value\":\"9999999999999999e80\",\"currency\":\"USD\",\"issuer\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"},\"SendMax\":{\"value\":\"5\",\"currency\":\"USD\",\"issuer\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\"},\"DeliverMin\":{\"value\":\"9999999999999999e80\",\"currency\":\"USD\",\"issuer\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"},\"Paths\":[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\",\"currency\":\"USD\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -22,6 +22,7 @@ module.exports = {
path_find: { path_find: {
generate: require('./path-find'), generate: require('./path-find'),
sendUSD: require('./path-find-send-usd'), sendUSD: require('./path-find-send-usd'),
sendAll: require('./path-find-send-all'),
XrpToXrp: require('./path-find-xrp-to-xrp'), XrpToXrp: require('./path-find-xrp-to-xrp'),
srcActNotFound: require('./path-find-srcActNotFound') srcActNotFound: require('./path-find-srcActNotFound')
}, },

View File

@@ -0,0 +1,313 @@
{
"alternatives": [
{
"destination_amount": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "4.93463759481038"
},
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "5"
}
},
{
"destination_amount": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "4.93463759481038"
},
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "EUR",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "5"
}
},
{
"destination_amount": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "4.93463759481038"
},
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 48,
"type_hex": "0000000000000030"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "5"
}
},
{
"destination_amount": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "4.990019960079841"
},
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "5"
}
}
],
"destination_account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"destination_amount": {
"currency": "USD",
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"value": "-1"
},
"full_reply": true,
"id": 1,
"source_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"type": "path_find"
}

View File

@@ -266,10 +266,18 @@ module.exports = function(port) {
destination_amount: request.destination_amount, destination_amount: request.destination_amount,
destination_address: request.destination_address destination_address: request.destination_address
}); });
} else if (request.source_account === addresses.ACCOUNT) {
if (request.destination_account ===
'ra5nK24KXen9AHvsdFTKHSANinZseWnPcX') {
response = createResponse(request, fixtures.path_find.sendAll);
} else {
response = fixtures.path_find.generate.generateIOUPaymentPaths(
request.id, request.source_account, request.destination_account,
request.destination_amount);
}
} else { } else {
response = fixtures.path_find.generate.generateIOUPaymentPaths( assert(false, 'Unrecognized path find request: '
request.id, request.source_account, request.destination_account, + JSON.stringify(request));
request.destination_amount);
} }
// delay response to simulate calculation time so we can test queuing // delay response to simulate calculation time so we can test queuing
setTimeout(() => conn.send(response), 20); setTimeout(() => conn.send(response), 20);