Compare commits

...

35 Commits

Author SHA1 Message Date
Chris Clark
c23c6e4fc9 Bump version to 0.16.2 2015-12-10 14:41:45 -08:00
Chris Clark
afdd60efe8 Merge pull request #671 from clark800/bump-binary
Bump ripple-binary-codec dependency version to 0.1.1 to fix issue wit…
2015-12-10 14:38:11 -08:00
Chris Clark
8f6ea573ff Bump ripple-binary-codec dependency version to 0.1.1 to fix issue with computeLedgerHash for transactions with DeliverMin 2015-12-10 14:29:28 -08:00
Alan Cohen
3271b544ef Bump version to 0.16.1 2015-12-09 16:51:26 -08:00
Alan Cohen
6e83130754 Merge pull request #669 from lumberj/fix-assertdiff
FIX: Use assert not assert-diff
2015-12-09 16:49:36 -08:00
Alan Cohen
f6ebe32519 FIX: Use assert not assert-diff 2015-12-09 16:45:08 -08:00
Chris Clark
3caed3c761 Bump version to 0.16.0 2015-12-09 13:17:04 -08:00
Chris Clark
ce1c55427a Merge pull request #668 from clark800/fix-ws-error
BREAKING CHANGE: Change error event format and fix crash due to error event on websocket
2015-12-09 13:06:45 -08:00
Chris Clark
9cd72595f0 BREAKING CHANGE: Change error event format and fix crash due to error event on websocket 2015-12-09 12:56:45 -08:00
Chris Clark
ad1d3e135f Merge pull request #663 from darkdarkdragon/develop-http-server
http server example
2015-12-09 11:15:52 -08:00
Ivan Tivonenko
76866ab901 http server example
allows to use both positional and named parameters
2015-12-09 21:00:47 +02:00
Chris Clark
20b647dfbf Merge pull request #667 from clark800/fix-server-info
BREAKING CHANGE: Fix types of XRP values in getServerInfo response
2015-12-07 17:03:09 -08:00
Chris Clark
99d08065e4 BREAKING CHANGE: Fix types of XRP values in getServerInfo response 2015-12-07 16:47:56 -08:00
Chris Clark
261fba3d21 Merge pull request #666 from clark800/fix-deliver-min
Fix DeliverMin value when specifying minAmount
2015-12-04 15:35:30 -08:00
Chris Clark
e1d9de7b1f Fix DeliverMin value when specifying minAmount 2015-12-04 15:18:03 -08:00
Chris Clark
391b2f3622 Merge pull request #665 from clark800/fix-quality
Fix parsing of quality for getTrustlines
2015-12-04 13:55:46 -08:00
Chris Clark
86ff315ef2 Fix parsing of quality for getTrustlines 2015-12-04 13:40:59 -08:00
Chris Clark
8d8a850864 Merge pull request #662 from lumberj/doc-fix
getFee returns a string not float
2015-12-02 13:32:11 -08:00
Alan Cohen
7bf2da6014 getFee returns a string not float
> api.connect().then(() => api.getFee().then(fee => console.log(typeof fee)));
> string
2015-12-02 13:17:35 -08:00
Chris Clark
7eae3ce709 Merge pull request #661 from clark800/client-cert
Add support for client certificates
2015-11-30 16:48:52 -08:00
Chris Clark
5f5e48e414 Add support for client certificates 2015-11-30 16:26:27 -08:00
Chris Clark
4f6a37f7b1 Merge pull request #660 from clark800/null-max
Allow setting maxLedgerVersion to null to specify no maximum
2015-11-30 16:13:41 -08:00
Chris Clark
82613e7e8b Allow setting maxLedgerVersion to null to specify no maximum 2015-11-30 15:52:36 -08:00
Chris Clark
588aa382a1 Merge pull request #659 from clark800/doc-fixes
Fix generateAddress docs and add error event listener to boilerplate
2015-11-30 15:32:02 -08:00
Chris Clark
809d981987 Fix generateAddress docs and add error event listener to boilerplate 2015-11-30 15:16:37 -08:00
Chris Clark
cfc21fde8c Bump version to 0.15.2 2015-11-25 13:13:53 -08:00
Geert Weening
5c06ef547b Merge pull request #658 from clark800/fix-proxy-auth
Fix support for proxy credentials in proxy URL and fix error when the…
2015-11-25 13:11:42 -08:00
Chris Clark
0990ad4a6f Fix support for proxy credentials in proxy URL and fix error when there are more than 10 outstanding requests 2015-11-25 12:53:50 -08:00
Chris Clark
d8f967d2b8 Bump version to 0.15.1 2015-11-25 11:46:33 -08:00
Geert Weening
fe1c3e7130 Merge pull request #657 from clark800/fix-polyfill
Fix babel-polyfill require
2015-11-25 11:38:52 -08:00
Chris Clark
062148674c Fix babel-polyfill require 2015-11-25 11:31:32 -08:00
Chris Clark
11320693fd Merge pull request #656 from clark800/fix-samples
Fix samples
2015-11-25 11:08:39 -08:00
Chris Clark
7af7eaccb4 Merge pull request #655 from darkdarkdragon/develop-RLJS-549-3
add unit tests for RippleAPIBroadcast
2015-11-25 11:04:55 -08:00
Chris Clark
5d5cf868a2 Fix samples 2015-11-25 10:26:30 -08:00
Ivan Tivonenko
ddf8fe5b1a add unit tests for RippleAPIBroadcast 2015-11-25 05:58:48 +02:00
51 changed files with 924 additions and 224 deletions

View File

@@ -1,3 +1,36 @@
##0.16.2
**Changes**
+ [Bump ripple-binary-codec dependency version to 0.1.1 to fix issue with computeLedgerHash for transactions with DeliverMin]
##0.16.1
**Changes**
+ [FIX: Use assert not assert-diff](https://github.com/ripple/ripple-lib/commit/f6ebe325193e7208c5ee8d8e84a7504714f0009e)
##0.16.0
**Breaking Changes**
+ [Fix types of XRP values in getServerInfo response](https://github.com/ripple/ripple-lib/commit/99d08065e4bda3dda6ae1f183adbd11abc70a9b7)
+ [Change error event format and fix crash due to error event on webscocket](https://github.com/ripple/ripple-lib/commit/9cd72595f0efc062d77b9d625695d6030c524cc6)
**Changes**
+ [Fix generateAddress docs and add error event listener to boilerplate](https://github.com/ripple/ripple-lib/commit/809d981987a2890fac3a73a40a05c598b9040334)
+ [Allow setting maxLedgerVersion to null to specify no maximum](https://github.com/ripple/ripple-lib/commit/82613e7e8b360d1ae1552eab4559ab4763c06d7e)
+ [Add support for client certificates](https://github.com/ripple/ripple-lib/commit/5f5e48e4140345d166b8c1a3ee0847b0d9e2d893)
+ [getFee returns a string not float](https://github.com/ripple/ripple-lib/commit/7bf2da6014c87e164542e69356efeaabb575a157)
+ [Fix parsing of quality for getTrustlines](https://github.com/ripple/ripple-lib/commit/86ff315ef2a39dfdc2ce97e0e1c4aa73f04e363b)
+ [Fix DeliverMin value when specifying minAmount](Fix DeliverMin value when specifying minAmount)
+ [http server example](https://github.com/ripple/ripple-lib/commit/76866ab901ea46a2dd73181605e0f7f4220043d4)
##0.15.2
**Changes**
+ [Fix support for proxy credentials in proxy URL and fix error when there are more than 10 outstanding requests](https://github.com/ripple/ripple-lib/commit/0990ad4a6f1d59ca9d2cb859b4e2d71693f3fc4b)
##0.15.1
**Changes**
+ [Fix babel-polyfill require](https://github.com/ripple/ripple-lib/commit/062148674c3b1293ab82c28e25615ddd530339fa)
+ [Fix samples](https://github.com/ripple/ripple-lib/commit/5d5cf868a2ddb1b1cd40e4a4f0a782d0066c2055)
+ [add unit tests for RippleAPIBroadcast](https://github.com/ripple/ripple-lib/commit/ddf8fe5b1a9c750490dca98fb9ffaaf8017f87e0)
##0.15.0
**Breaking Changes**
+ ["servers" parameter changed to single "server"](https://github.com/ripple/ripple-lib/commit/7061e9afe46f0682254d098adeff3dd7157521a1)

View File

@@ -86,6 +86,9 @@ const {RippleAPI} = require('ripple-lib');
const api = new RippleAPI({
server: 'wss://s1.ripple.com' // Public rippled server hosted by Ripple, Inc.
});
api.on('error', (errorCode, errorMessage) => {
console.log(errorCode + ': ' + errorMessage);
});
api.connect().then(() => {
/* insert code here */
}).then(() => {
@@ -105,6 +108,10 @@ All the code snippets in this documentation assume that you have surrounded them
If you omit the "catch" section, errors may not be visible.
</aside>
<aside class="notice">
The "error" event is emitted whenever an error occurs that cannot be associated with a specific request. If the listener is not registered, an exception will be thrown whenever the event is emitted.
</aside>
### Parameters
The RippleAPI constructor optionally takes one argument, an object with the following options:
@@ -112,7 +119,10 @@ The RippleAPI constructor optionally takes one argument, an object with the foll
Name | Type | Description
---- | ---- | -----------
authorization | string | *Optional* Username and password for HTTP basic authentication to the rippled server in the format **username:password**.
certificate | string | *Optional* A string containing the certificate key of the client in PEM format. (Can be an array of certificates).
feeCushion | number | *Optional* Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`.
key | string | *Optional* A string containing the private key of the client in PEM format. (Can be an array of keys).
passphrase | string | *Optional* The passphrase for the private key of the client.
proxy | uri string | *Optional* URI for HTTP/HTTPS proxy to use to connect to the rippled server.
proxyAuthorization | string | *Optional* Username and password for HTTP basic authentication to the proxy in the format **username:password**.
server | uri string | *Optional* URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.
@@ -277,8 +287,8 @@ Name | Type | Description
---- | ---- | -----------
fee | [value](#value) | *Optional* An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
maxFee | [value](#value) | *Optional* The maximum fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
maxLedgerVersionOffset | integer | *Optional* Offset from current legder version to highest ledger version that the transaction can be included in.
maxLedgerVersion | integer,null | *Optional* The highest ledger version that the transaction can be included in. If this option and `maxLedgerVersionOffset` are both omitted, the `maxLedgerVersion` option will default to 3 greater than the current validated ledger version (equivalent to `maxLedgerVersionOffset=3`). Use `null` to not set a maximum ledger version.
maxLedgerVersionOffset | integer | *Optional* Offset from current validated legder version to highest ledger version that the transaction can be included in.
sequence | [sequence](#account-sequence-number) | *Optional* The initiating account's sequence number for this transaction.
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
@@ -673,10 +683,10 @@ pubkeyNode | string | Public key used to verify this node for internal communica
serverState | string | A string indicating to what extent the server is participating in the network. See [Possible Server States](https://ripple.com/build/rippled-apis/#possible-server-states) for more details.
validatedLedger | object | Information about the fully-validated ledger with the highest sequence number (the most recent).
*validatedLedger.* age | integer | The time since the ledger was closed, in seconds.
*validatedLedger.* baseFeeXRP | number | Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005.
*validatedLedger.* baseFeeXRP | [value](#value) | Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005.
*validatedLedger.* hash | string | Unique hash for the ledger, as an uppercase hexadecimal string.
*validatedLedger.* reserveBaseXRP | integer | Minimum amount of XRP necessary for every account to keep in reserve.
*validatedLedger.* reserveIncrementXRP | integer | Amount of XRP added to the account reserve for each object an account is responsible for in the ledger.
*validatedLedger.* reserveBaseXRP | [value](#value) | Minimum amount of XRP necessary for every account to keep in reserve.
*validatedLedger.* reserveIncrementXRP | [value](#value) | Amount of XRP added to the account reserve for each object an account is responsible for in the ledger.
*validatedLedger.* ledgerVersion | integer | Identifying sequence number of this ledger version.
validationQuorum | number | Minimum number of trusted validations required in order to validate a ledger version. Some circumstances may cause the server to require more validations.
load | object | *Optional* *(Admin only)* Detailed information about the current load state of the server.
@@ -707,10 +717,10 @@ return api.getServerInfo().then(info => {/* ... */});
"serverState": "full",
"validatedLedger": {
"age": 5,
"baseFeeXRP": 0.00001,
"baseFeeXRP": "0.00001",
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
"reserveBaseXRP": 20,
"reserveIncrementXRP": 5,
"reserveBaseXRP": "20",
"reserveIncrementXRP": "5",
"ledgerVersion": 6595042
},
"validationQuorum": 3
@@ -730,7 +740,7 @@ This method has no parameters.
### Return Value
This method returns a promise that resolves with a floating point value representing the estimated fee to submit a transaction, expressed in XRP.
This method returns a promise that resolves with a string encoded floating point value representing the estimated fee to submit a transaction, expressed in XRP.
### Example
@@ -739,7 +749,7 @@ return api.getFee().then(fee => {/* ... */});
```
```json
0.012
"0.012"
```
## getLedgerVersion
@@ -2793,7 +2803,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -2862,7 +2872,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -2929,7 +2939,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -2994,7 +3004,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -3046,7 +3056,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -3111,7 +3121,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -3183,7 +3193,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -3240,7 +3250,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
@@ -3355,7 +3365,11 @@ Generate a new Ripple address and corresponding secret.
### Parameters
This method has no parameters.
Name | Type | Description
---- | ---- | -----------
options | object | *Optional* Options to control how the address and secret are generated.
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed.
### Return Value
@@ -3369,8 +3383,7 @@ secret | secret string | The secret corresponding to the `address`.
### Example
```javascript
return api.generateAddress()
.then(result => {/* ... */});
return api.generateAddress();
```
@@ -3451,7 +3464,7 @@ Name | Type | Description
baseFeeXRP | [value](#value) | Base fee, in XRP.
ledgerHash | string | Unique hash of the ledger that was closed, as hex.
ledgerTimestamp | date-time string | The time at which this ledger closed.
reserveBaseXRP | [value](#value) | The minimum reserve, in drops of XRP, that is required for an account.
reserveBaseXRP | [value](#value) | The minimum reserve, in XRP, that is required for an account.
reserveIncrementXRP | [value](#value) | The increase in account reserve that is added for each item the account owns, such as offers or trust lines.
transactionCount | integer | Number of new transactions included in this ledger.
ledgerVersion | integer | Ledger version of the ledger that closed.
@@ -3482,16 +3495,26 @@ api.on('ledger', ledger => {
## error
This event is emitted when there is an error on the connection to the server.
This event is emitted when there is an error on the connection to the server that cannot be associated to a specific request.
### Return Value
The first parameter is a string indicating the error type, which may be `badMessage` (meaning that rippled returned a malformed message), or one of the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors). The second parameter is a message explaining the error, or the message that caused the error in the case of `badMessage`.
The first parameter is a string indicating the error type:
* `badMessage` - rippled returned a malformed message
* `websocket` - the websocket library emitted an error
* one of the error codes found in the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors).
The second parameter is a message explaining the error.
The third parameter is:
* the message that caused the error for `badMessage`
* the error object emitted for `websocket`
* the parsed response for rippled errors
### Example
```javascript
api.on('error', (errorCode, errorMessage) => {
api.on('error', (errorCode, errorMessage, data) => {
console.log(errorCode + ': ' + errorMessage);
});
```

View File

@@ -1,7 +1,7 @@
'use strict';
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
api.connect().then(() => {

View File

@@ -4,13 +4,13 @@ const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
const address = 'INSERT ADDRESS HERE';
const secret = 'INSERT SECRET HERE';
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const instructions = {maxLedgerVersionOffset: 5};
const payment = {
source: {
address: address,
amount: {
maxAmount: {
value: '0.01',
currency: 'XRP'
}

View File

@@ -8,6 +8,9 @@ const {RippleAPI} = require('ripple-lib');
const api = new RippleAPI({
server: 'wss://s1.ripple.com' // Public rippled server hosted by Ripple, Inc.
});
api.on('error', (errorCode, errorMessage) => {
console.log(errorCode + ': ' + errorMessage);
});
api.connect().then(() => {
/* insert code here */
}).then(() => {
@@ -27,6 +30,10 @@ All the code snippets in this documentation assume that you have surrounded them
If you omit the "catch" section, errors may not be visible.
</aside>
<aside class="notice">
The "error" event is emitted whenever an error occurs that cannot be associated with a specific request. If the listener is not registered, an exception will be thrown whenever the event is emitted.
</aside>
### Parameters
The RippleAPI constructor optionally takes one argument, an object with the following options:

View File

@@ -20,16 +20,26 @@ api.on('ledger', ledger => {
## error
This event is emitted when there is an error on the connection to the server.
This event is emitted when there is an error on the connection to the server that cannot be associated to a specific request.
### Return Value
The first parameter is a string indicating the error type, which may be `badMessage` (meaning that rippled returned a malformed message), or one of the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors). The second parameter is a message explaining the error, or the message that caused the error in the case of `badMessage`.
The first parameter is a string indicating the error type:
* `badMessage` - rippled returned a malformed message
* `websocket` - the websocket library emitted an error
* one of the error codes found in the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors).
The second parameter is a message explaining the error.
The third parameter is:
* the message that caused the error for `badMessage`
* the error object emitted for `websocket`
* the parsed response for rippled errors
### Example
```javascript
api.on('error', (errorCode, errorMessage) => {
api.on('error', (errorCode, errorMessage, data) => {
console.log(errorCode + ': ' + errorMessage);
});
```

View File

@@ -6,7 +6,7 @@ Generate a new Ripple address and corresponding secret.
### Parameters
This method has no parameters.
<%- renderSchema('input/generate-address.json') %>
### Return Value
@@ -17,8 +17,7 @@ This method returns an object with the following structure:
### Example
```javascript
return api.generateAddress()
.then(result => {/* ... */});
return api.generateAddress();
```
<%- renderFixture('responses/generate-address.json') %>

View File

@@ -10,7 +10,7 @@ This method has no parameters.
### Return Value
This method returns a promise that resolves with a floating point value representing the estimated fee to submit a transaction, expressed in XRP.
This method returns a promise that resolves with a string encoded floating point value representing the estimated fee to submit a transaction, expressed in XRP.
### Example
@@ -19,5 +19,5 @@ return api.getFee().then(fee => {/* ... */});
```
```json
0.012
"0.012"
```

137
npm-shrinkwrap.json generated
View File

@@ -1,30 +1,35 @@
{
"name": "ripple-lib",
"version": "0.15.0",
"version": "0.16.2",
"dependencies": {
"ajv": {
"version": "1.4.8",
"from": "https://registry.npmjs.org/ajv/-/ajv-1.4.8.tgz",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-1.4.8.tgz",
"version": "1.4.10",
"from": "ajv@>=1.4.8 <2.0.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-1.4.10.tgz",
"dependencies": {
"json-stable-stringify": {
"version": "1.0.0",
"from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.0.tgz",
"from": "json-stable-stringify@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.0.tgz",
"dependencies": {
"jsonify": {
"version": "0.0.0",
"from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
"from": "jsonify@>=0.0.0 <0.1.0",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
}
}
}
}
},
"ajv-i18n": {
"version": "0.1.1",
"from": "ajv-i18n@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-0.1.1.tgz"
},
"babel-polyfill": {
"version": "6.2.0",
"from": "babel-polyfill@*",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.2.0.tgz",
"version": "6.3.14",
"from": "babel-polyfill@>=6.2.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.3.14.tgz",
"dependencies": {
"core-js": {
"version": "1.2.6",
@@ -32,28 +37,28 @@
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
},
"babel-regenerator-runtime": {
"version": "6.2.0",
"from": "babel-regenerator-runtime@>=6.2.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.2.0.tgz"
"version": "6.3.13",
"from": "babel-regenerator-runtime@>=6.3.13 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.3.13.tgz"
}
}
},
"babel-runtime": {
"version": "5.8.29",
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
"version": "5.8.34",
"from": "babel-runtime@>=5.5.4 <6.0.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
"dependencies": {
"core-js": {
"version": "1.2.3",
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz"
"version": "1.2.6",
"from": "core-js@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
}
}
},
"bignumber.js": {
"version": "2.1.0",
"version": "2.1.2",
"from": "bignumber.js@>=2.0.3 <3.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.0.tgz"
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.2.tgz"
},
"https-proxy-agent": {
"version": "1.0.0",
@@ -91,6 +96,52 @@
}
}
},
"jayson": {
"version": "1.2.2",
"from": "jayson@>=1.2.2 <2.0.0",
"resolved": "https://registry.npmjs.org/jayson/-/jayson-1.2.2.tgz",
"dependencies": {
"JSONStream": {
"version": "1.0.3",
"from": "JSONStream@1.0.3",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.0.3.tgz",
"dependencies": {
"jsonparse": {
"version": "1.0.0",
"from": "jsonparse@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.0.0.tgz"
},
"through": {
"version": "2.3.8",
"from": "through@>=2.2.7 <3.0.0",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
}
}
},
"commander": {
"version": "1.3.2",
"from": "commander@1.3.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz",
"dependencies": {
"keypress": {
"version": "0.1.0",
"from": "keypress@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz"
}
}
},
"eyes": {
"version": "0.1.8",
"from": "eyes@0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz"
},
"lodash": {
"version": "3.6.0",
"from": "lodash@3.6.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
}
}
},
"lodash": {
"version": "3.10.1",
"from": "lodash@>=3.1.0 <4.0.0",
@@ -128,9 +179,9 @@
}
},
"ripple-binary-codec": {
"version": "0.1.0",
"from": "ripple-binary-codec@0.1.0",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.0.tgz",
"version": "0.1.1",
"from": "ripple-binary-codec@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.1.tgz",
"dependencies": {
"bn.js": {
"version": "3.3.0",
@@ -166,14 +217,14 @@
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <3.0.0",
"from": "inherits@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
},
"ripple-hashes": {
"version": "0.1.0",
"from": "ripple-hashes@0.1.0",
"from": "ripple-hashes@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.1.0.tgz",
"dependencies": {
"create-hash": {
@@ -227,7 +278,7 @@
"dependencies": {
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <3.0.0",
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
@@ -265,40 +316,6 @@
"version": "1.0.2",
"from": "ultron@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
},
"bufferutil": {
"version": "1.1.0",
"from": "bufferutil@>=1.1.0 <1.2.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"from": "nan@>=1.8.0 <1.9.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
},
"utf-8-validate": {
"version": "1.1.0",
"from": "utf-8-validate@>=1.1.0 <1.2.0",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"from": "nan@>=1.8.0 <1.9.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.15.0",
"version": "0.16.2",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
@@ -20,9 +20,10 @@
"babel-runtime": "^5.5.4",
"bignumber.js": "^2.0.3",
"https-proxy-agent": "^1.0.0",
"jayson": "^1.2.2",
"lodash": "^3.1.0",
"ripple-address-codec": "^2.0.1",
"ripple-binary-codec": "^0.1.0",
"ripple-binary-codec": "^0.1.1",
"ripple-hashes": "^0.1.0",
"ripple-keypairs": "^0.10.0",
"ripple-lib-transactionparser": "^0.6.0",
@@ -65,7 +66,8 @@
"test": "istanbul test _mocha",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
"perf": "./scripts/perf_test.sh"
"perf": "./scripts/perf_test.sh",
"start": "babel-node scripts/http.js"
},
"repository": {
"type": "git",

View File

@@ -34,6 +34,7 @@ unittest() {
integrationtest() {
mocha test/integration/integration-test.js
mocha test/integration/http-integration-test.js
}
doctest() {

16
scripts/http.js Normal file
View File

@@ -0,0 +1,16 @@
'use strict';
const createHTTPServer = require('../src/index').createHTTPServer;
const port = 5990;
const serverUrl = 'wss://s1.ripple.com';
function main() {
const server = createHTTPServer({server: serverUrl}, port);
server.start().then(() => {
console.log('Server started on port ' + String(port));
});
}
main();

View File

@@ -9,7 +9,7 @@
// In node.js env, polyfill might be already loaded (from any npm package),
// that's why we do this check.
if (!global._babelPolyfill) {
require('babel-core/polyfill');
require('babel-polyfill');
}
const _ = require('lodash');
@@ -46,7 +46,8 @@ const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const generateAddress = common.generateAddressAPI;
const generateAddress =
require('./offline/generate-address').generateAddressAPI;
const computeLedgerHash = require('./offline/ledgerhash');
const getLedger = require('./ledger/ledger');
@@ -84,8 +85,8 @@ class RippleAPI extends EventEmitter {
this.connection.on('ledgerClosed', message => {
this.emit('ledger', server.formatLedgerClose(message));
});
this.connection.on('error', (type, info) => {
this.emit('error', type, info);
this.connection.on('error', (errorCode, errorMessage, data) => {
this.emit('error', errorCode, errorMessage, data);
});
} else {
// use null object pattern to provide better error message if user

View File

@@ -37,7 +37,8 @@ class RippleAPIBroadcast extends RippleAPI {
apis.forEach(api => {
api.on('ledger', this.onLedgerEvent.bind(this));
api.on('error', (type, info) => this.emit('error', type, info));
api.on('error', (errorCode, errorMessage, data) =>
this.emit('error', errorCode, errorMessage, data));
});
}

View File

@@ -1,4 +1,5 @@
'use strict';
const _ = require('lodash');
const {EventEmitter} = require('events');
const WebSocket = require('ws');
const parseURL = require('url').parse;
@@ -15,6 +16,7 @@ function isStreamMessageType(type) {
class Connection extends EventEmitter {
constructor(url, options = {}) {
super();
this.setMaxListeners(Infinity);
this._url = url;
this._trace = options.trace;
if (this._trace) {
@@ -25,6 +27,9 @@ class Connection extends EventEmitter {
this._proxyAuthorization = options.proxyAuthorization;
this._authorization = options.authorization;
this._trustedCertificates = options.trustedCertificates;
this._key = options.key;
this._passphrase = options.passphrase;
this._certificate = options.certificate;
this._timeout = options.timeout || (20 * 1000);
this._isReady = false;
this._ws = null;
@@ -50,7 +55,7 @@ class Connection extends EventEmitter {
}
return [data.type, data];
} else if (data.type === undefined && data.error) {
return ['error', data.error, data.error_message]; // e.g. slowDown
return ['error', data.error, data.error_message, data]; // e.g. slowDown
}
throw new ResponseFormatError('unrecognized message type: ' + data.type);
}
@@ -63,7 +68,7 @@ class Connection extends EventEmitter {
try {
parameters = this._parseMessage(message);
} catch (error) {
this.emit('error', 'badMessage', message);
this.emit('error', 'badMessage', error.message, message);
return;
}
// we don't want this inside the try/catch or exceptions in listener
@@ -103,18 +108,21 @@ class Connection extends EventEmitter {
});
}
_createWebSocket(url, proxyURL, proxyAuthorization, authorization,
trustedCertificates) {
_createWebSocket() {
const options = {};
if (proxyURL !== undefined) {
const parsedURL = parseURL(url);
const proxyOptions = parseURL(proxyURL);
proxyOptions.secureEndpoint = (parsedURL.protocol === 'wss:');
proxyOptions.secureProxy = (proxyOptions.protocol === 'https:');
proxyOptions.auth = proxyAuthorization;
if (trustedCertificates) {
proxyOptions.ca = trustedCertificates;
}
if (this._proxyURL !== undefined) {
const parsedURL = parseURL(this._url);
const parsedProxyURL = parseURL(this._proxyURL);
const proxyOverrides = _.omit({
secureEndpoint: (parsedURL.protocol === 'wss:'),
secureProxy: (parsedProxyURL.protocol === 'https:'),
auth: this._proxyAuthorization,
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
let HttpsProxyAgent;
try {
HttpsProxyAgent = require('https-proxy-agent');
@@ -123,11 +131,22 @@ class Connection extends EventEmitter {
}
options.agent = new HttpsProxyAgent(proxyOptions);
}
if (authorization !== undefined) {
const base64 = new Buffer(authorization).toString('base64');
if (this._authorization !== undefined) {
const base64 = new Buffer(this._authorization).toString('base64');
options.headers = {Authorization: `Basic ${base64}`};
}
return new WebSocket(url, options);
const optionsOverrides = _.omit({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const websocketOptions = _.assign({}, options, optionsOverrides);
const websocket = new WebSocket(this._url, websocketOptions);
// we will have a listener for each outstanding request,
// so we have to raise the limit (the default is 10)
websocket.setMaxListeners(Infinity);
return websocket;
}
connect() {
@@ -141,9 +160,13 @@ class Connection extends EventEmitter {
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve);
} else {
this._ws = this._createWebSocket(this._url, this._proxyURL,
this._proxyAuthorization, this._authorization,
this._trustedCertificates);
this._ws = this._createWebSocket();
// when an error causes the connection to close, the close event
// should still be emitted; the "ws" documentation says: "The close
// event is also emitted when then underlying net.Socket closes the
// connection (end or close)."
this._ws.on('error', error =>
this.emit('error', 'websocket', error.messsage, error));
this._ws.on('message', this._onMessage.bind(this));
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this);
this._ws.once('close', this._onUnexpectedCloseBound);

View File

@@ -92,8 +92,9 @@ function loadSchemas() {
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
require('./schemas/input/prepare-suspended-payment-execution.json'),
require('./schemas/input/compute-ledger-hash'),
require('./schemas/input/sign'),
require('./schemas/input/submit')
require('./schemas/input/sign.json'),
require('./schemas/input/submit.json'),
require('./schemas/input/generate-address.json')
];
const titles = _.map(schemas, schema => schema.title);
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));

View File

@@ -42,6 +42,18 @@
"type": "string",
"description": "A PEM-formatted SSL certificate to trust when connecting to a proxy."
}
},
"key": {
"type": "string",
"description": "A string containing the private key of the client in PEM format. (Can be an array of keys)."
},
"passphrase": {
"type": "string",
"description": "The passphrase for the private key of the client."
},
"certificate": {
"type": "string",
"description": "A string containing the certificate key of the client in PEM format. (Can be an array of certificates)."
}
},
"additionalProperties": false

View File

@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "generateAddressParameters",
"type": "object",
"properties": {
"options": {
"type": "object",
"description": "Options to control how the address and secret are generated.",
"properties": {
"entropy": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"description": "The entropy to use to generate the seed."
},
"algorithm": {
"type": "string",
"enum": ["ecdsa-secp256k1", "ed25519"],
"description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}

View File

@@ -18,11 +18,14 @@
"$ref": "value"
},
"maxLedgerVersion": {
"description": "The highest ledger version that the transaction can be included in.",
"$ref": "ledgerVersion"
"description": "The highest ledger version that the transaction can be included in. If this option and `maxLedgerVersionOffset` are both omitted, the `maxLedgerVersion` option will default to 3 greater than the current validated ledger version (equivalent to `maxLedgerVersionOffset=3`). Use `null` to not set a maximum ledger version.",
"oneOf": [
{"$ref": "ledgerVersion"},
{"type": "null"}
]
},
"maxLedgerVersionOffset": {
"description": "Offset from current legder version to highest ledger version that the transaction can be included in.",
"description": "Offset from current validated legder version to highest ledger version that the transaction can be included in.",
"type": "integer",
"minimum": 0
}

View File

@@ -83,7 +83,7 @@
"description": "The time since the ledger was closed, in seconds."
},
"baseFeeXRP": {
"type": "number",
"$ref": "value",
"description": "Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005."
},
"hash": {
@@ -91,13 +91,11 @@
"description": "Unique hash for the ledger, as an uppercase hexadecimal string."
},
"reserveBaseXRP": {
"type": "integer",
"minimum": 0,
"$ref": "value",
"description": "Minimum amount of XRP necessary for every account to keep in reserve."
},
"reserveIncrementXRP": {
"type": "integer",
"minimum": 0,
"$ref": "value",
"description": "Amount of XRP added to the account reserve for each object an account is responsible for in the ledger."
},
"ledgerVersion": {

View File

@@ -23,7 +23,7 @@
},
"reserveBaseXRP": {
"$ref": "value",
"description": "The minimum reserve, in drops of XRP, that is required for an account."
"description": "The minimum reserve, in XRP, that is required for an account."
},
"reserveIncrementXRP": {
"$ref": "value",

View File

@@ -21,12 +21,15 @@
"description": "The initiating account's sequence number for this transaction."
},
"maxLedgerVersion": {
"$ref": "ledgerVersion",
"description": "The highest ledger version that the transaction can be included in."
"oneOf": [
{"$ref": "ledgerVersion"},
{"type": "null"}
],
"description": "The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum."
}
},
"additionalProperties": false,
"required": ["fee", "sequence"]
"required": ["fee", "sequence", "maxLedgerVersion"]
}
},
"additionalProperties": false,

View File

@@ -6,7 +6,7 @@ import type {Connection} from './connection';
export type GetServerInfoResponse = {
buildVersion: string,
completeLedgers: string,
hostid: string,
hostID: string,
ioLatencyMs: number,
load?: {
jobTypes: Array<Object>,
@@ -23,11 +23,11 @@ export type GetServerInfoResponse = {
serverState: string,
validatedLedger: {
age: number,
baseFeeXrp: number,
baseFeeXRP: string,
hash: string,
reserveBaseXrp: number,
reserveIncXrp: number,
seq: number
reserveBaseXRP: string,
reserveIncrementXRP: string,
ledgerVersion: number
},
validationQuorum: number
}
@@ -49,6 +49,12 @@ function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
reserveIncXrp: 'reserveIncrementXRP',
seq: 'ledgerVersion'
});
info.validatedLedger.baseFeeXRP =
info.validatedLedger.baseFeeXRP.toString();
info.validatedLedger.reserveBaseXRP =
info.validatedLedger.reserveBaseXRP.toString();
info.validatedLedger.reserveIncrementXRP =
info.validatedLedger.reserveIncrementXRP.toString();
return info;
});
}

View File

@@ -2,8 +2,6 @@
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const errors = require('./errors');
const keypairs = require('ripple-keypairs');
const {deriveKeypair} = require('ripple-keypairs');
import type {Amount, RippledAmount} from './types.js';
@@ -37,21 +35,6 @@ function toRippledAmount(amount: Amount): RippledAmount {
};
}
function generateAddress(options?: Object): Object {
const secret = keypairs.generateSeed(options);
const keypair = keypairs.deriveKeypair(secret);
const address = keypairs.deriveAddress(keypair.publicKey);
return {secret, address};
}
function generateAddressAPI(options?: Object): Object {
try {
return generateAddress(options);
} catch (error) {
throw new errors.UnexpectedError(error.message);
}
}
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
@@ -101,8 +84,6 @@ module.exports = {
dropsToXrp,
xrpToDrops,
toRippledAmount,
generateAddress,
generateAddressAPI,
convertKeysFromSnakeCaseToCamelCase,
removeUndefined,
rippleTimeToISO8601,

View File

@@ -49,6 +49,7 @@ module.exports = {
sign: _.partial(schemaValidate, 'signParameters'),
submit: _.partial(schemaValidate, 'submitParameters'),
computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'),
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
apiOptions: _.partial(schemaValidate, 'api-options'),
instructions: _.partial(schemaValidate, 'instructions')
};

85
src/http.js Normal file
View File

@@ -0,0 +1,85 @@
/* eslint-disable new-cap */
'use strict';
const assert = require('assert');
const _ = require('lodash');
const jayson = require('jayson');
const RippleAPI = require('./api').RippleAPI;
function createHTTPServer(options, httpPort) {
const rippleAPI = new RippleAPI(options);
const methodNames = _.filter(_.keys(RippleAPI.prototype), k => {
return typeof RippleAPI.prototype[k] === 'function'
&& k !== 'connect'
&& k !== 'disconnect'
&& k !== 'constructor'
&& k !== 'RippleAPI';
});
function applyPromiseWithCallback(fnName, callback, args_) {
try {
let args = args_;
if (!_.isArray(args_)) {
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName]);
args = fnParameters.map(name => args_[name]);
const defaultArgs = _.omit(args_, fnParameters);
assert(_.size(defaultArgs) <= 1,
'Function must have no more than one default argument');
if (_.size(defaultArgs) > 0) {
args.push(defaultArgs[_.keys(defaultArgs)[0]]);
}
}
Promise.resolve(rippleAPI[fnName].apply(rippleAPI, args))
.then(res => callback(null, res))
.catch(err => {
callback({code: 99, message: err.message, data: {name: err.name}});
});
} catch (err) {
callback({code: 99, message: err.message, data: {name: err.name}});
}
}
const methods = {};
_.forEach(methodNames, fn => {
methods[fn] = jayson.Method((args, cb) => {
applyPromiseWithCallback(fn, cb, args);
}, {collect: true});
});
const server = jayson.server(methods);
let httpServer = null;
return {
server: server,
start: function() {
if (httpServer !== null) {
return Promise.reject('Already started');
}
return new Promise((resolve) => {
rippleAPI.connect().then(() => {
httpServer = server.http();
httpServer.listen(httpPort, resolve);
});
});
},
stop: function() {
if (httpServer === null) {
return Promise.reject('Not started');
}
return new Promise((resolve) => {
rippleAPI.disconnect();
httpServer.close(() => {
httpServer = null;
resolve();
});
});
}
};
}
module.exports = {
createHTTPServer
};

View File

@@ -2,5 +2,8 @@
module.exports = {
RippleAPI: require('./api').RippleAPI,
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast
// Broadcast api is experimental
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast,
// HTTP server is experimental
createHTTPServer: require('./http').createHTTPServer
};

View File

@@ -24,8 +24,8 @@ function parseAccountTrustline(trustline: Trustline): AccountTrustline {
limit: trustline.limit,
currency: trustline.currency,
counterparty: trustline.account,
qualityIn: trustline.quality_in || undefined,
qualityOut: trustline.quality_out || undefined,
qualityIn: utils.parseQuality(trustline.quality_in) || undefined,
qualityOut: utils.parseQuality(trustline.quality_out) || undefined,
ripplingDisabled: trustline.no_ripple || undefined,
frozen: trustline.freeze || undefined,
authorized: trustline.authorized || undefined

View File

@@ -3,7 +3,6 @@
const assert = require('assert');
const utils = require('./utils');
const flags = utils.txFlags.TrustSet;
const BigNumber = require('bignumber.js');
function parseFlag(flagsValue, trueValue, falseValue) {
if (flagsValue & trueValue) {
@@ -15,13 +14,6 @@ function parseFlag(flagsValue, trueValue, falseValue) {
return undefined;
}
function parseQuality(quality?: number) {
if (typeof quality === 'number') {
return (new BigNumber(quality)).shift(-9).toNumber();
}
return undefined;
}
function parseTrustline(tx: Object): Object {
assert(tx.TransactionType === 'TrustSet');
@@ -29,8 +21,8 @@ function parseTrustline(tx: Object): Object {
limit: tx.LimitAmount.value,
currency: tx.LimitAmount.currency,
counterparty: tx.LimitAmount.issuer,
qualityIn: parseQuality(tx.QualityIn),
qualityOut: parseQuality(tx.QualityOut),
qualityIn: utils.parseQuality(tx.QualityIn),
qualityOut: utils.parseQuality(tx.QualityOut),
ripplingDisabled: parseFlag(
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),

View File

@@ -17,6 +17,13 @@ function adjustQualityForXRP(
(new BigNumber(quality)).shift(shift).toString();
}
function parseQuality(quality: ?number) {
if (typeof quality === 'number') {
return (new BigNumber(quality)).shift(-9).toNumber();
}
return undefined;
}
function parseTimestamp(rippleTime: number): string | void {
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined;
}
@@ -80,6 +87,7 @@ function parseMemos(tx: Object): ?Array<Object> {
}
module.exports = {
parseQuality,
parseOutcome,
parseMemos,
hexToString,

View File

@@ -170,11 +170,11 @@ function getTransactions(address: string, options: TransactionsOptions = {}
const ledgerVersion = tx.outcome.ledgerVersion;
const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
const newOptions = _.assign(defaults, options, {startTx: tx}, bound);
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound);
return getTransactionsInternal(this.connection, address, newOptions);
});
}
const newOptions = _.assign(defaults, options);
const newOptions = _.assign({}, defaults, options);
return getTransactionsInternal(this.connection, address, newOptions);
}

View File

@@ -0,0 +1,24 @@
'use strict';
const keypairs = require('ripple-keypairs');
const common = require('../common');
const {errors, validate} = common;
function generateAddress(options?: Object): Object {
const secret = keypairs.generateSeed(options);
const keypair = keypairs.deriveKeypair(secret);
const address = keypairs.deriveAddress(keypair.publicKey);
return {secret, address};
}
function generateAddressAPI(options?: Object): Object {
validate.generateAddress({options});
try {
return generateAddress(options);
} catch (error) {
throw new errors.UnexpectedError(error.message);
}
}
module.exports = {
generateAddressAPI
};

View File

@@ -64,7 +64,7 @@ function createMaximalAmount(amount: Amount): Amount {
const maxXRPValue = '100000000000';
const maxIOUValue = '9999999999999999e80';
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
return _.assign(amount, {value: maxValue});
return _.assign({}, amount, {value: maxValue});
}
function createPaymentTransaction(address: string, paymentArgument: Payment

View File

@@ -10,7 +10,8 @@ function formatPrepareResponse(txJSON: Object): Object {
const instructions = {
fee: common.dropsToXrp(txJSON.Fee),
sequence: txJSON.Sequence,
maxLedgerVersion: txJSON.LastLedgerSequence
maxLedgerVersion: txJSON.LastLedgerSequence === undefined ?
null : txJSON.LastLedgerSequence
};
return {
txJSON: JSON.stringify(txJSON),
@@ -36,7 +37,9 @@ function prepareTransaction(txJSON: Object, api: Object,
function prepareMaxLedgerVersion(): Promise<Object> {
if (instructions.maxLedgerVersion !== undefined) {
txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
if (instructions.maxLedgerVersion !== null) {
txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
}
return Promise.resolve(txJSON);
}
const offset = instructions.maxLedgerVersionOffset !== undefined ?

View File

@@ -14,6 +14,7 @@ const address = addresses.ACCOUNT;
const utils = RippleAPI._PRIVATE.ledgerUtils;
const ledgerClosed = require('./fixtures/rippled/ledger-close-newer');
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
assert.options.strict = true;
function unused() {
}
@@ -186,6 +187,13 @@ describe('RippleAPI', function() {
_.partial(checkResult, responses.prepareSettings.flags, 'prepare'));
});
it('prepareSettings - no maxLedgerVersion', function() {
return this.api.prepareSettings(
address, requests.prepareSettings, {maxLedgerVersion: null}).then(
_.partial(checkResult, responses.prepareSettings.noMaxLedgerVersion,
'prepare'));
});
it('prepareSettings - no instructions', function() {
return this.api.prepareSettings(
address, requests.prepareSettings).then(

View File

@@ -0,0 +1,62 @@
/* eslint-disable max-nested-callbacks */
'use strict';
const _ = require('lodash');
const assert = require('assert-diff');
const setupAPI = require('./setup-api');
const responses = require('./fixtures').responses;
const ledgerClosed = require('./fixtures/rippled/ledger-close');
const RippleAPI = require('ripple-api').RippleAPI;
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
function checkResult(expected, schemaName, response) {
if (expected.txJSON) {
assert(response.txJSON);
assert.deepEqual(JSON.parse(response.txJSON), JSON.parse(expected.txJSON));
}
assert.deepEqual(_.omit(response, 'txJSON'), _.omit(expected, 'txJSON'));
if (schemaName) {
schemaValidator.schemaValidate(schemaName, response);
}
return response;
}
describe('RippleAPIBroadcast', function() {
beforeEach(setupAPI.setupBroadcast);
afterEach(setupAPI.teardown);
it('base', function() {
const expected = {request_server_info: 1};
this.mocks.forEach(mock => mock.expect(_.assign({}, expected)));
assert(this.api.isConnected());
return this.api.getServerInfo().then(
_.partial(checkResult, responses.getServerInfo, 'getServerInfo'));
});
it('ledger', function(done) {
let gotLedger = 0;
this.api.on('ledger', () => {
gotLedger++;
});
const ledgerNext = _.assign({}, ledgerClosed);
ledgerNext.ledger_index++;
this.mocks.forEach(mock => mock.socket.send(JSON.stringify(ledgerNext)));
setTimeout(() => {
console.log('-- ledgerVersion', this.api.ledgerVersion);
assert.strictEqual(gotLedger, 1);
done();
}, 50);
});
it('error propagation', function(done) {
this.api.once('error', (type, info) => {
assert.strictEqual(type, 'type');
assert.strictEqual(info, 'info');
done();
});
this.mocks[1].socket.send(
JSON.stringify({error: 'type', error_message: 'info'}));
});
});

View File

@@ -227,8 +227,9 @@ describe('Connection', function() {
});
it('invalid message id', function(done) {
this.api.on('error', (type, message) => {
assert.strictEqual(type, 'badMessage');
this.api.on('error', (errorCode, errorMessage, message) => {
assert.strictEqual(errorCode, 'badMessage');
assert.strictEqual(errorMessage, 'valid id not found in response');
assert.strictEqual(message,
'{"type":"response","id":"must be integer"}');
done();
@@ -239,9 +240,10 @@ describe('Connection', function() {
});
it('propagate error message', function(done) {
this.api.on('error', (type, message) => {
assert.strictEqual(type, 'slowDown');
assert.strictEqual(message, 'slow down');
this.api.on('error', (errorCode, errorMessage, data) => {
assert.strictEqual(errorCode, 'slowDown');
assert.strictEqual(errorMessage, 'slow down');
assert.deepEqual(data, {error: 'slowDown', error_message: 'slow down'});
done();
});
this.api.connection._onMessage(JSON.stringify({
@@ -250,8 +252,9 @@ describe('Connection', function() {
});
it('unrecognized message type', function(done) {
this.api.on('error', (type, message) => {
assert.strictEqual(type, 'badMessage');
this.api.on('error', (errorCode, errorMessage, message) => {
assert.strictEqual(errorCode, 'badMessage');
assert.strictEqual(errorMessage, 'unrecognized message type: unknown');
assert.strictEqual(message, '{"type":"unknown"}');
done();
});

View File

@@ -13,10 +13,10 @@
"serverState": "full",
"validatedLedger": {
"age": 5,
"baseFeeXRP": 0.00001,
"baseFeeXRP": "0.00001",
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
"reserveBaseXRP": 20,
"reserveIncrementXRP": 5,
"reserveBaseXRP": "20",
"reserveIncrementXRP": "5",
"ledgerVersion": 6595042
},
"validationQuorum": 3

View File

@@ -3,7 +3,8 @@
"specification": {
"limit": "0",
"currency": "ASP",
"counterparty": "r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z"
"counterparty": "r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z",
"qualityIn": 1
},
"counterparty": {
"limit": "10"

View File

@@ -71,8 +71,8 @@ module.exports = {
},
preparePayment: {
normal: require('./prepare-payment.json'),
minAmountXRP: require('./prepare-payment-min-amont-xrp.json'),
minAmountXRPXRP: require('./prepare-payment-min-amont-xrp-xrp.json'),
minAmountXRP: require('./prepare-payment-min-amount-xrp.json'),
minAmountXRPXRP: require('./prepare-payment-min-amount-xrp-xrp.json'),
allOptions: require('./prepare-payment-all-options.json'),
noCounterparty: require('./prepare-payment-no-counterparty.json'),
minAmount: require('./prepare-payment-min-amount.json')
@@ -86,7 +86,8 @@ module.exports = {
setTransferRate: require('./prepare-settings-set-transfer-rate.json'),
fieldClear: require('./prepare-settings-field-clear.json'),
noInstructions: require('./prepare-settings-no-instructions.json'),
signed: require('./prepare-settings-signed.json')
signed: require('./prepare-settings-signed.json'),
noMaxLedgerVersion: require('./prepare-settings-no-maxledgerversion.json')
},
prepareSuspendedPaymentCreation: {
normal: require('./prepare-suspended-payment-creation'),

View File

@@ -1,5 +1,5 @@
{
"txJSON": "{\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"100000000000000000\",\"Flags\":2147614720,\"SendMax\":{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\",\"value\":\"0.01\"},\"DeliverMin\":\"100000000000000000\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"txJSON": "{\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"100000000000000000\",\"Flags\":2147614720,\"SendMax\":{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\",\"value\":\"0.01\"},\"DeliverMin\":\"10000\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,

View File

@@ -1,5 +1,5 @@
{
"txJSON": "{\"Flags\":2147614720,\"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}",
"txJSON": "{\"Flags\":2147614720,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\",\"Amount\":{\"value\":\"9999999999999999e80\",\"currency\":\"USD\",\"issuer\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"},\"SendMax\":{\"value\":\"5\",\"currency\":\"USD\",\"issuer\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\"},\"DeliverMin\":{\"value\":\"4.93463759481038\",\"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": "0.000012",
"sequence": 23,

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Memos\":[{\"Memo\":{\"MemoData\":\"7465787465642064617461\",\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\"}}],\"Domain\":\"726970706C652E636F6D\",\"Flags\":2147483648,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
"maxLedgerVersion": null
}
}

View File

@@ -22,7 +22,7 @@ module.exports.normal = function(request, options = {}) {
currency: 'ASP',
limit: '0',
limit_peer: '10',
quality_in: 0,
quality_in: 1000000000,
quality_out: 0
},
{

View File

@@ -0,0 +1,74 @@
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"type": "order",
"address": "rK5j9n8baXfL4gzUoZsfxBvvsv97P5swaV",
"sequence": 7973823,
"id": "4EB6B76237DEEE99F1EA16FAACED2D1E69C5F9CB54F727A4ECA51A08AD3AF466",
"specification": {
"direction": "buy",
"quantity": {
"currency": "USD",
"value": "0.000709756467",
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"totalPrice": {
"currency": "JPY",
"value": "0.086630181788",
"counterparty": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN"
}
},
"outcome": {
"result": "tesSUCCESS",
"timestamp": "2015-12-03T00:53:21.000Z",
"fee": "0.010001",
"balanceChanges": {
"rK5j9n8baXfL4gzUoZsfxBvvsv97P5swaV": [
{
"currency": "XRP",
"value": "-0.010001"
}
]
},
"orderbookChanges": {
"rK5j9n8baXfL4gzUoZsfxBvvsv97P5swaV": [
{
"direction": "buy",
"quantity": {
"currency": "USD",
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0.000709260645"
},
"totalPrice": {
"currency": "JPY",
"counterparty": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "0.086665436143"
},
"sequence": 7973725,
"status": "cancelled",
"makerExchangeRate": "0.008183892870852266"
},
{
"direction": "buy",
"quantity": {
"currency": "USD",
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0.000709756467"
},
"totalPrice": {
"currency": "JPY",
"counterparty": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "0.086630181788"
},
"sequence": 7973823,
"status": "created",
"makerExchangeRate": "0.008192946757712049"
}
]
},
"ledgerVersion": 17445469,
"indexInLedger": 2
}
}
}

View File

@@ -0,0 +1,72 @@
{
"jsonrpc": "2.0",
"id": "3",
"result": [
{
"type": "order",
"address": "rpP2JgiMyTF5jR5hLG3xHCPi1knBb1v9cM",
"sequence": 3372206,
"id": "848397FA686BD4A59F91EC1F4DE717360470EE8BD67CAA01D5FD333EDA8D97B3",
"specification": {
"direction": "buy",
"quantity": {
"currency": "JPY",
"value": "27865.90216965619",
"counterparty": "rJRi8WW24gt9X85PHAxfWNPCizMMhqUQwg"
},
"totalPrice": {
"currency": "XRP",
"value": "40000"
}
},
"outcome": {
"result": "tesSUCCESS",
"fee": "0.011",
"balanceChanges": {
"rpP2JgiMyTF5jR5hLG3xHCPi1knBb1v9cM": [
{
"currency": "XRP",
"value": "-0.011"
}
]
},
"orderbookChanges": {
"rpP2JgiMyTF5jR5hLG3xHCPi1knBb1v9cM": [
{
"direction": "buy",
"quantity": {
"currency": "JPY",
"counterparty": "rJRi8WW24gt9X85PHAxfWNPCizMMhqUQwg",
"value": "27880.6855384734"
},
"totalPrice": {
"currency": "XRP",
"value": "40000"
},
"sequence": 3372204,
"status": "cancelled",
"makerExchangeRate": "0.697017138461835"
},
{
"direction": "buy",
"quantity": {
"currency": "JPY",
"counterparty": "rJRi8WW24gt9X85PHAxfWNPCizMMhqUQwg",
"value": "27865.90216965619"
},
"totalPrice": {
"currency": "XRP",
"value": "40000"
},
"sequence": 3372206,
"status": "created",
"makerExchangeRate": "0.6966475542414048"
}
]
},
"ledgerVersion": 17533547,
"indexInLedger": 12
}
}
]
}

View File

@@ -0,0 +1,6 @@
'use strict';
module.exports = {
getTransaction: require('./get-transaction'),
getTransactions: require('./get-transactions')
};

View File

@@ -0,0 +1,157 @@
/* eslint-disable max-nested-callbacks */
'use strict';
const assert = require('assert-diff');
const _ = require('lodash');
const jayson = require('jayson');
const createHTTPServer = require('../../src/index').createHTTPServer;
const apiFixtures = require('../fixtures');
const apiRequests = apiFixtures.requests;
const apiResponses = apiFixtures.responses;
const fixtures = require('./fixtures');
const TIMEOUT = 20000; // how long before each test case times out
const apiOptions = {
server: 'wss://s1.ripple.com'
};
const httpPort = 3000;
function createClient() {
return jayson.client.http({port: httpPort, hostname: 'localhost'});
}
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
function makePositionalParams(params) {
return params.map(value => value[_.keys(value)[0]]);
}
function makeNamedParams(params) {
return _.reduce(params, _.assign, {});
}
function random() {
return _.fill(Array(16), 0);
}
describe('http server integration tests', function() {
this.timeout(TIMEOUT);
let server = null;
let client = null;
function createTestInternal(testName, methodName, params, testFunc, id) {
it(testName, function() {
return new Promise((resolve, reject) => {
client.request(methodName, params, id,
(err, result) => err ? reject(err) : resolve(testFunc(result)));
});
});
}
function createTest(name, params, testFunc, id) {
createTestInternal(name + ' - positional params', name,
makePositionalParams(params), testFunc, id);
createTestInternal(name + ' - named params', name,
makeNamedParams(params), testFunc, id);
}
beforeEach(function() {
server = createHTTPServer(apiOptions, httpPort);
return server.start().then(() => {
this.client = createClient();
client = this.client;
});
});
afterEach(function() {
return server.stop();
});
createTest(
'getLedgerVersion',
[],
result => assert(_.isNumber(result.result))
);
createTest(
'getServerInfo',
[],
result => assert(_.isNumber(result.result.validatedLedger.ledgerVersion))
);
createTest(
'getTransaction',
[{id: '4EB6B76237DEEE99F1EA16FAACED2D1E69C5F9CB54F727A4ECA51A08AD3AF466'}],
result => assert.deepEqual(result, fixtures.getTransaction),
'2'
);
createTest(
'getTransactions',
[{address: 'rpP2JgiMyTF5jR5hLG3xHCPi1knBb1v9cM'}, {
options: {
binary: true,
limit: 1,
start:
'FBAAC31D6BAEEFA9E501266FD62DA7A7982662BC19BC42F49BB41405C2F820DB'
}
}],
result => assert.deepEqual(result, fixtures.getTransactions),
'3'
);
createTest(
'prepareSettings',
[
{address},
{settings: apiRequests.prepareSettings},
{instructions: {
maxFee: '0.000012',
sequence: 23,
maxLedgerVersion: 8820051
}}
],
result => {
const got = JSON.parse(result.result.txJSON);
const expected = JSON.parse(apiResponses.prepareSettings.flags.txJSON);
assert.deepEqual(got, expected);
}
);
createTest(
'sign',
[{txJSON: apiRequests.sign.normal.txJSON},
{secret: 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'}],
result => assert.deepEqual(result.result, apiResponses.sign.normal)
);
createTest(
'generateAddress',
[{options: {entropy: random()}}],
result => assert.deepEqual(result.result, apiResponses.generateAddress)
);
createTest(
'computeLedgerHash',
[{ledger: _.assign({}, apiResponses.getLedger.full,
{parentCloseTime: apiResponses.getLedger.full.closeTime})
}],
result => {
assert.strictEqual(result.result,
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
}
);
createTest(
'isConnected',
[],
result => assert(result.result)
);
});

View File

@@ -71,6 +71,7 @@ module.exports = function(port) {
};
mock.on('connection', function(conn) {
this.socket = conn;
conn.on('message', function(requestJSON) {
const request = JSON.parse(requestJSON);
mock.emit('request_' + request.command, request, conn);

View File

@@ -1,52 +1,77 @@
'use strict';
const net = require('net');
const RippleAPI = require('ripple-api').RippleAPI;
const RippleAPIBroadcast = require('ripple-api').RippleAPIBroadcast;
const ledgerClosed = require('./fixtures/rippled/ledger-close');
const createMockRippled = require('./mock-rippled');
// using a free port instead of a constant port enables parallelization
function getFreePort(callback) {
const server = net.createServer();
let port;
server.on('listening', function() {
port = server.address().port;
server.close();
function getFreePort() {
return new Promise((resolve, reject) => {
const server = net.createServer();
let port;
server.on('listening', function() {
port = server.address().port;
server.close();
});
server.on('close', function() {
resolve(port);
});
server.on('error', function(error) {
reject(error);
});
server.listen(0);
});
server.on('close', function() {
callback(null, port);
});
server.on('error', function(error) {
callback(error);
});
server.listen(0);
}
function setupMockRippledConnection(testcase, port, done) {
testcase.mockRippled = createMockRippled(port);
testcase.api = new RippleAPI({server: 'ws://localhost:' + port});
testcase.api.connect().then(() => {
testcase.api.once('ledger', () => done());
testcase.api.connection._ws.emit('message', JSON.stringify(ledgerClosed));
}).catch(done);
function setupMockRippledConnection(testcase, port) {
return new Promise((resolve, reject) => {
testcase.mockRippled = createMockRippled(port);
testcase.api = new RippleAPI({server: 'ws://localhost:' + port});
testcase.api.connect().then(() => {
testcase.api.once('ledger', () => resolve());
testcase.api.connection._ws.emit('message', JSON.stringify(ledgerClosed));
}).catch(reject);
});
}
function setup(done) {
getFreePort((error, port) => {
if (error) {
throw new Error('Unable to obtain a free port: ' + error);
}
setupMockRippledConnection(this, port, done);
function setupMockRippledConnectionForBroadcast(testcase, ports) {
return new Promise((resolve, reject) => {
const servers = ports.map(port => 'ws://localhost:' + port);
testcase.mocks = ports.map(port => createMockRippled(port));
testcase.api = new RippleAPIBroadcast(servers);
testcase.api.connect().then(() => {
testcase.api.once('ledger', () => resolve());
testcase.mocks[0].socket.send(JSON.stringify(ledgerClosed));
}).catch(reject);
});
}
function setup() {
return getFreePort().then(port => {
return setupMockRippledConnection(this, port);
});
}
function setupBroadcast() {
return Promise.all([getFreePort(), getFreePort()]).then(ports => {
return setupMockRippledConnectionForBroadcast(this, ports);
});
}
function teardown(done) {
this.api.disconnect().then(() => {
this.mockRippled.close();
if (this.mockRippled !== undefined) {
this.mockRippled.close();
} else {
this.mocks.forEach(mock => mock.close());
}
setImmediate(done);
}).catch(done);
}
module.exports = {
setup: setup,
teardown: teardown
teardown: teardown,
setupBroadcast: setupBroadcast
};