Fix: Specify send_max when pathfinding with a source amount

When specifying a fixed sending amount during a pathfind request, the source
amount is specified as a `send_max` field. This fixes a bug where the amount was
specified as `source_amount`. Leading to strange pathfinding behavior.

Example bad behavior (rippled pathfind request/response):

```
 {
  "command": "ripple_path_find",
  "source_account": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
  "destination_account": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
  "destination_amount": {
    "currency": "EUR",
    "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
    "value": -1
  },
  "source_amount": {
    "currency": "USD",
    "issuer": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
    "value": "9.9"
  },
  "id": 2
}
{
  "id": 2,
  "result": {
    "alternatives": [
      {
        "destination_amount": {
          "currency": "EUR",
          "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
          "value": "147520.7583553951"
        },
        "paths_canonical": [],
        "paths_computed": [
          [
            {
              "account": "r9HqF3wexBb1vtu2DfZKiFuyy3HoTAwUnH",
              "type": 1,
              "type_hex": "0000000000000001"
            },
            {
              "currency": "EUR",
              "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
              "type": 48,
              "type_hex": "0000000000000030"
            }
          ]
        ],
        "source_amount": {
          "currency": "USD",
          "issuer": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
          "value": "160510.6025237665"
        }
      }
    ],
    "destination_account": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
    "destination_currencies": [
      "EUR",
      "XRP"
    ],
    "ledger_current_index": 2771044,
    "validated": false
  },
  "status": "success",
  "type": "response"
}
```

https://ripple.com/build/rippled-apis/#ripple-path-find
This commit is contained in:
Alan Cohen
2015-11-20 09:03:58 -08:00
parent 849ba999cb
commit 7514213918
3 changed files with 73 additions and 58 deletions

View File

@@ -1,55 +1,55 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
import type {Amount, LaxLaxAmount, RippledAmount, Adjustment, MaxAdjustment, import type {Amount, LaxLaxAmount, RippledAmount, Adjustment, MaxAdjustment,
MinAdjustment} from '../common/types.js'; MinAdjustment} from '../common/types.js';
type Path = { type Path = {
source: Adjustment | MaxAdjustment, source: Adjustment | MaxAdjustment,
destination: Adjustment | MinAdjustment, destination: Adjustment | MinAdjustment,
paths: string paths: string
} }
export type GetPaths = Array<Path> export type GetPaths = Array<Path>
export type PathFind = { export type PathFind = {
source: { source: {
address: string, address: string,
amount?: Amount, amount?: Amount,
currencies?: Array<{currency: string, counterparty?:string}> currencies?: Array<{currency: string, counterparty?:string}>
}, },
destination: { destination: {
address: string, address: string,
amount: LaxLaxAmount amount: LaxLaxAmount
} }
} }
export type PathFindRequest = { export type PathFindRequest = {
command: string, command: string,
source_account: string, source_account: string,
destination_amount: RippledAmount, destination_amount: RippledAmount,
destination_account: string, destination_account: string,
source_amount?: RippledAmount, source_currencies?: Array<string>,
source_currencies?: Array<string> send_max?: RippledAmount
} }
export type RippledPathsResponse = { export type RippledPathsResponse = {
alternatives: Array<{ alternatives: Array<{
paths_computed: Array<Array<{ paths_computed: Array<Array<{
type: number, type: number,
type_hex: string, type_hex: string,
account?: string, account?: string,
issuer?: string, issuer?: string,
currency?: string currency?: string
}>>, }>>,
source_amount: RippledAmount source_amount: RippledAmount
}>, }>,
type: string, type: string,
destination_account: string, destination_account: string,
destination_amount: RippledAmount, destination_amount: RippledAmount,
destination_currencies?: Array<string>, destination_currencies?: Array<string>,
source_account?: string, source_account?: string,
source_currencies?: Array<{currency: string}>, source_currencies?: Array<{currency: string}>,
full_reply?: boolean full_reply?: boolean
} }

View File

@@ -46,9 +46,9 @@ function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
throw new ValidationError('Cannot specify both source.amount' throw new ValidationError('Cannot specify both source.amount'
+ ' and destination.amount.value in getPaths'); + ' and destination.amount.value in getPaths');
} }
request.source_amount = toRippledAmount(pathfind.source.amount); request.send_max = toRippledAmount(pathfind.source.amount);
if (request.source_amount.currency && !request.source_amount.issuer) { if (request.send_max.currency && !request.send_max.issuer) {
request.source_amount.issuer = pathfind.source.address; request.send_max.issuer = pathfind.source.address;
} }
} }

View File

@@ -289,6 +289,21 @@ describe('integration tests', function() {
}); });
}); });
it('getPaths - send all', function() {
const pathfind = requests.getPaths.sendAll;
return this.api.getPaths(pathfind).then(data => {
assert(data && data.length > 0);
assert(_.every(data, path => {
return parseFloat(path.source.amount.value)
<= parseFloat(pathfind.source.amount.value);
}));
const path = data[0];
assert(path && path.source);
assert.strictEqual(path.source.address, pathfind.source.address);
assert(path.paths && path.paths.length > 0);
});
});
it('generateWallet', function() { it('generateWallet', function() {
const newWallet = this.api.generateAddress(); const newWallet = this.api.generateAddress();