mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-12 16:45:49 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
208f5f6c5c | ||
|
|
de3e2a9867 | ||
|
|
40eea3c659 | ||
|
|
4a848ec527 | ||
|
|
10414e169c | ||
|
|
1a6c68d028 | ||
|
|
9156734ced | ||
|
|
0dfe3ff4ac | ||
|
|
3a8c7f02cc | ||
|
|
e03b192fcc | ||
|
|
cf40bd2c30 | ||
|
|
4082e88416 | ||
|
|
27abc10d93 | ||
|
|
d6474d71f2 | ||
|
|
d57603e854 | ||
|
|
ac92584678 | ||
|
|
c234be0a8c | ||
|
|
7c6b8398cf | ||
|
|
7de677c953 | ||
|
|
aa23f44555 | ||
|
|
901d75a1eb | ||
|
|
aa95286810 | ||
|
|
f6b3f661d6 | ||
|
|
0850d85791 | ||
|
|
c564400ac4 | ||
|
|
94ab545ffe | ||
|
|
e10df203b7 | ||
|
|
eea20a6eab | ||
|
|
5f208801ee | ||
|
|
0a22697e5d | ||
|
|
30cf4f0b00 | ||
|
|
e4bb88a725 | ||
|
|
e3822e6bc3 | ||
|
|
20d3be0d1d | ||
|
|
1785863686 | ||
|
|
ea4ced3cc1 | ||
|
|
149008d18b | ||
|
|
55a21d2eec | ||
|
|
c7491e631a | ||
|
|
468a205e36 | ||
|
|
bebe951a57 | ||
|
|
85a8ab32ef | ||
|
|
34ddbe170c | ||
|
|
e9846eb249 | ||
|
|
7cc418ac93 | ||
|
|
69532a4f23 | ||
|
|
f59419d96f | ||
|
|
7f288d0555 | ||
|
|
53afa8c276 | ||
|
|
22f4dd2f75 | ||
|
|
0989152024 | ||
|
|
f74809d361 | ||
|
|
9724cf7776 | ||
|
|
909e5438a8 | ||
|
|
3c534d87c0 | ||
|
|
4022a59705 | ||
|
|
138e7942da | ||
|
|
d7d26a3ae1 | ||
|
|
23504821cf | ||
|
|
b09da3e8f1 | ||
|
|
f3dd2fec99 | ||
|
|
66db127245 | ||
|
|
932be02e9e | ||
|
|
fc524894c6 | ||
|
|
fa6a2c5bbb |
35
HISTORY.md
35
HISTORY.md
@@ -1,5 +1,38 @@
|
||||
# ripple-lib Release History
|
||||
|
||||
## 1.6.2 (2020-01-17)
|
||||
|
||||
* Bug fix: Catch possible error in reconnect() on _heartbeat(), emit reconnect error (#1179)
|
||||
* Docs: Fix whitespace
|
||||
* Dependencies
|
||||
* Update @typescript-eslint/eslint-plugin, ts-node, @types/node, @types/ws, typescript
|
||||
|
||||
## 1.6.1 (2020-01-14)
|
||||
|
||||
> **Update Note:** In this release we refactored the internal connection logic of ripple-lib to improve resiliency across dropped messages and reconnects. The external interface remains the same, with zero changes to public methods/properties. However, If you experience any problems around connections, requests, and request retries, please [file an issue]( https://github.com/ripple/ripple-lib/issues/new) with the repo (and be sure to test against v1.6.0 to confirm that the problem was introduced in this version).
|
||||
|
||||
* Documentation
|
||||
* Document message type 'path_find' (#1121) (thanks @r0bertz)
|
||||
* Improve documentation for address generation; functions that can be called offline; generateXAddress() (#1158, #1159, #1169) (thanks @hbergren)
|
||||
* Add [Security Policy](https://github.com/ripple/ripple-lib/blob/develop/SECURITY.md)
|
||||
* Bug fixes
|
||||
* Types: Fix AccountObjectsResponse structure (#1127) (thanks @you21979)
|
||||
* In some cases, ripple-lib would fail to wait for the connection to be ready (#1119)
|
||||
* Dependencies
|
||||
* Update docs dependencies, ejs and doctoc (#1153)
|
||||
* Update eslint, ripple-lib-transactionparser, typescript, nyc, ws, @types/node, ripple-binary-codec, mocha, mocha-junit-reporter
|
||||
* Internal
|
||||
* Add LedgerHistory to Connection (#1119): Moved ledger logic into its own Ledger class
|
||||
|
||||
## 1.6.0 (2020-01-06)
|
||||
|
||||
* Add support for AccountDelete (#1120)
|
||||
* Improve error type given on rejected message _send to be DisconnectedError (#1098)
|
||||
* Internal
|
||||
* Add unit test for unhandled promise rejection warning on message _send (#1098)
|
||||
* Dependencies
|
||||
* Update @types/node, @typescript-eslint/parser
|
||||
|
||||
## 1.5.1 (2019-12-28)
|
||||
|
||||
* Fix support for CDNs (#1142)
|
||||
@@ -438,7 +471,7 @@ f28921f57a133678dcb3cb54c497626bd76b1f953d22d61f3ddca31c8947d552 ripple-1.1.0-m
|
||||
The SHA-256 checksums for the browser version of this release can be found
|
||||
below.
|
||||
```
|
||||
% shasum -a 256 *
|
||||
% shasum -a 256 *
|
||||
2556fe17296e127ed44e7066e90a6175e2b164f00ca3c1aa7b1c554f31c688dd ripple-1.0.2-debug.js
|
||||
e0342ea21eac32a1024c62034fba09c6f26dd3e7371b23ea1e153e03135cd590 ripple-1.0.2-min.js
|
||||
c7286c517497d018d02d09257e81172b61d36c8b9885a077af68e8133c3b3b9b ripple-1.0.2.js
|
||||
|
||||
29
SECURITY.md
Normal file
29
SECURITY.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
This table shows which versions of ripple-lib are currently supported with security updates:
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ---------------------- |
|
||||
| 1.x | :white_check_mark: Yes |
|
||||
| 0.x | :x: No |
|
||||
|
||||
## Responsible disclosure security policy
|
||||
|
||||
The responsible disclosure of vulnerabilities helps to protect users of the project. Vulnerabilities are first triaged in a private manner, and only publicly disclosed after a reasonable time period that allows patching the vulnerability and provides an upgrade path for users.
|
||||
|
||||
When contacting us directly via email, we will do our best to respond in a reasonable time to resolve the issue. Do not disclose the vulnerability until it has been patched and users have been given time to upgrade.
|
||||
|
||||
We kindly ask you to refrain from malicious acts that put our users, the project, or any of the project’s team members at risk.
|
||||
|
||||
## Reporting a security issue
|
||||
|
||||
Security is a top priority. But no matter how much effort we put into security, there can still be vulnerabilities present.
|
||||
|
||||
If you discover a security vulnerability, please use the following means of communications to report it to us:
|
||||
|
||||
- Report the security issue to bugs@ripple.com
|
||||
- [Ripple Bug Bounty](https://ripple.com/bug-bounty/)
|
||||
|
||||
Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions.
|
||||
@@ -221,6 +221,7 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
* [prepareEscrowExecution](#prepareescrowexecution)
|
||||
* [sign](#sign)
|
||||
* [generateAddress](#generateaddress)
|
||||
* [generateXAddress](#generatexaddress)
|
||||
* [computeLedgerHash](#computeledgerhash)
|
||||
|
||||
# Basic Types
|
||||
@@ -834,6 +835,7 @@ Type | Description
|
||||
`manifestReceived` | Sent by the `manifests` stream when the server receives a manifest.
|
||||
`transaction` | Sent by many subscriptions including `transactions`, `transactions_proposed`, `accounts`, `accounts_proposed`, and `book` (Order Book). See [Transaction Streams](https://ripple.com/build/rippled-apis/#transaction-streams) for details.
|
||||
`peerStatusChange` | (Admin-only) Reports a large amount of information on the activities of other `rippled` servers to which the server is connected.
|
||||
`path_find` | Asynchronous follow-up response to the currently open path\_find request. See [rippled path\_find method](https://xrpl.org/path_find.html) for details.
|
||||
|
||||
To register your listener function, use `connection.on(type, handler)`.
|
||||
|
||||
@@ -4511,7 +4513,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
|
||||
|
||||
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
|
||||
|
||||
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
|
||||
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
|
||||
|
||||
### Parameters
|
||||
|
||||
@@ -5662,7 +5664,8 @@ 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.
|
||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)
|
||||
*options.* includeClassicAddress | boolean | *Optional* Specifies whether the classic address should also be included in the returned payload.
|
||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
|
||||
|
||||
### Return Value
|
||||
@@ -5703,7 +5706,7 @@ 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.
|
||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)
|
||||
*options.* includeClassicAddress | boolean | *Optional* If `true`, return the classic address, in addition to the X-address.
|
||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
|
||||
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
<% include introduction.md.ejs %>
|
||||
<% include boilerplate.md.ejs %>
|
||||
<% include offline.md.ejs %>
|
||||
<% include basictypes.md.ejs %>
|
||||
<% include transactions.md.ejs %>
|
||||
<% include specifications.md.ejs %>
|
||||
<% include rippledAPIs.md.ejs %>
|
||||
<% include request.md.ejs %>
|
||||
<% include hasNextPage.md.ejs %>
|
||||
<% include requestNextPage.md.ejs %>
|
||||
<%- include('introduction.md.ejs') %>
|
||||
<%- include('boilerplate.md.ejs') %>
|
||||
<%- include('offline.md.ejs') %>
|
||||
<%- include('basictypes.md.ejs') %>
|
||||
<%- include('transactions.md.ejs') %>
|
||||
<%- include('specifications.md.ejs') %>
|
||||
<%- include('rippledAPIs.md.ejs') %>
|
||||
<%- include('request.md.ejs') %>
|
||||
<%- include('hasNextPage.md.ejs') %>
|
||||
<%- include('requestNextPage.md.ejs') %>
|
||||
|
||||
<% include staticMethods.md.ejs %>
|
||||
<% include renameCounterpartyToIssuer.md.ejs %>
|
||||
<% include formatBidsAndAsks.md.ejs %>
|
||||
<%- include('staticMethods.md.ejs') %>
|
||||
<%- include('renameCounterpartyToIssuer.md.ejs') %>
|
||||
<%- include('formatBidsAndAsks.md.ejs') %>
|
||||
|
||||
<% include methods.md.ejs %>
|
||||
<% include connect.md.ejs %>
|
||||
<% include disconnect.md.ejs %>
|
||||
<% include isConnected.md.ejs %>
|
||||
<% include getServerInfo.md.ejs %>
|
||||
<% include getFee.md.ejs %>
|
||||
<% include getLedgerVersion.md.ejs %>
|
||||
<% include getTransaction.md.ejs %>
|
||||
<% include getTransactions.md.ejs %>
|
||||
<% include getTrustlines.md.ejs %>
|
||||
<% include getBalances.md.ejs %>
|
||||
<% include getBalanceSheet.md.ejs %>
|
||||
<% include getPaths.md.ejs %>
|
||||
<% include getOrders.md.ejs %>
|
||||
<% include getOrderbook.md.ejs %>
|
||||
<% include getSettings.md.ejs %>
|
||||
<% include getAccountInfo.md.ejs %>
|
||||
<% include getAccountObjects.md.ejs %>
|
||||
<% include getPaymentChannel.md.ejs %>
|
||||
<% include getLedger.md.ejs %>
|
||||
<% include parseAccountFlags.md.ejs %>
|
||||
<% include prepareTransaction.md.ejs %>
|
||||
<% include preparePayment.md.ejs %>
|
||||
<% include prepareTrustline.md.ejs %>
|
||||
<% include prepareOrder.md.ejs %>
|
||||
<% include prepareOrderCancellation.md.ejs %>
|
||||
<% include prepareSettings.md.ejs %>
|
||||
<% include prepareEscrowCreation.md.ejs %>
|
||||
<% include prepareEscrowCancellation.md.ejs %>
|
||||
<% include prepareEscrowExecution.md.ejs %>
|
||||
<% include preparePaymentChannelCreate.md.ejs %>
|
||||
<% include preparePaymentChannelClaim.md.ejs %>
|
||||
<% include preparePaymentChannelFund.md.ejs %>
|
||||
<% include prepareCheckCreate.md.ejs %>
|
||||
<% include prepareCheckCancel.md.ejs %>
|
||||
<% include prepareCheckCash.md.ejs %>
|
||||
<% include sign.md.ejs %>
|
||||
<% include combine.md.ejs %>
|
||||
<% include submit.md.ejs %>
|
||||
<% include generateXAddress.md.ejs %>
|
||||
<% include generateAddress.md.ejs %>
|
||||
<% include isValidAddress.md.ejs %>
|
||||
<% include isValidSecret.md.ejs %>
|
||||
<% include deriveKeypair.md.ejs %>
|
||||
<% include deriveAddress.md.ejs %>
|
||||
<% include signPaymentChannelClaim.md.ejs %>
|
||||
<% include verifyPaymentChannelClaim.md.ejs %>
|
||||
<% include computeLedgerHash.md.ejs %>
|
||||
<% include xrpToDropsAndDropsToXrp.md.ejs %>
|
||||
<% include iso8601ToRippleTime.md.ejs %>
|
||||
<% include rippleTimeToISO8601.md.ejs %>
|
||||
<% include txFlags.md.ejs %>
|
||||
<% include schemaValidator.md.ejs %>
|
||||
<% include events.md.ejs %>
|
||||
<%- include('methods.md.ejs') %>
|
||||
<%- include('connect.md.ejs') %>
|
||||
<%- include('disconnect.md.ejs') %>
|
||||
<%- include('isConnected.md.ejs') %>
|
||||
<%- include('getServerInfo.md.ejs') %>
|
||||
<%- include('getFee.md.ejs') %>
|
||||
<%- include('getLedgerVersion.md.ejs') %>
|
||||
<%- include('getTransaction.md.ejs') %>
|
||||
<%- include('getTransactions.md.ejs') %>
|
||||
<%- include('getTrustlines.md.ejs') %>
|
||||
<%- include('getBalances.md.ejs') %>
|
||||
<%- include('getBalanceSheet.md.ejs') %>
|
||||
<%- include('getPaths.md.ejs') %>
|
||||
<%- include('getOrders.md.ejs') %>
|
||||
<%- include('getOrderbook.md.ejs') %>
|
||||
<%- include('getSettings.md.ejs') %>
|
||||
<%- include('getAccountInfo.md.ejs') %>
|
||||
<%- include('getAccountObjects.md.ejs') %>
|
||||
<%- include('getPaymentChannel.md.ejs') %>
|
||||
<%- include('getLedger.md.ejs') %>
|
||||
<%- include('parseAccountFlags.md.ejs') %>
|
||||
<%- include('prepareTransaction.md.ejs') %>
|
||||
<%- include('preparePayment.md.ejs') %>
|
||||
<%- include('prepareTrustline.md.ejs') %>
|
||||
<%- include('prepareOrder.md.ejs') %>
|
||||
<%- include('prepareOrderCancellation.md.ejs') %>
|
||||
<%- include('prepareSettings.md.ejs') %>
|
||||
<%- include('prepareEscrowCreation.md.ejs') %>
|
||||
<%- include('prepareEscrowCancellation.md.ejs') %>
|
||||
<%- include('prepareEscrowExecution.md.ejs') %>
|
||||
<%- include('preparePaymentChannelCreate.md.ejs') %>
|
||||
<%- include('preparePaymentChannelClaim.md.ejs') %>
|
||||
<%- include('preparePaymentChannelFund.md.ejs') %>
|
||||
<%- include('prepareCheckCreate.md.ejs') %>
|
||||
<%- include('prepareCheckCancel.md.ejs') %>
|
||||
<%- include('prepareCheckCash.md.ejs') %>
|
||||
<%- include('sign.md.ejs') %>
|
||||
<%- include('combine.md.ejs') %>
|
||||
<%- include('submit.md.ejs') %>
|
||||
<%- include('generateXAddress.md.ejs') %>
|
||||
<%- include('generateAddress.md.ejs') %>
|
||||
<%- include('isValidAddress.md.ejs') %>
|
||||
<%- include('isValidSecret.md.ejs') %>
|
||||
<%- include('deriveKeypair.md.ejs') %>
|
||||
<%- include('deriveAddress.md.ejs') %>
|
||||
<%- include('signPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('verifyPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('computeLedgerHash.md.ejs') %>
|
||||
<%- include('xrpToDropsAndDropsToXrp.md.ejs') %>
|
||||
<%- include('iso8601ToRippleTime.md.ejs') %>
|
||||
<%- include('rippleTimeToISO8601.md.ejs') %>
|
||||
<%- include('txFlags.md.ejs') %>
|
||||
<%- include('schemaValidator.md.ejs') %>
|
||||
<%- include('events.md.ejs') %>
|
||||
|
||||
@@ -23,4 +23,5 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
* [prepareEscrowExecution](#prepareescrowexecution)
|
||||
* [sign](#sign)
|
||||
* [generateAddress](#generateaddress)
|
||||
* [generateXAddress](#generatexaddress)
|
||||
* [computeLedgerHash](#computeledgerhash)
|
||||
|
||||
@@ -6,7 +6,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
|
||||
|
||||
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
|
||||
|
||||
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
|
||||
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ Type | Description
|
||||
`manifestReceived` | Sent by the `manifests` stream when the server receives a manifest.
|
||||
`transaction` | Sent by many subscriptions including `transactions`, `transactions_proposed`, `accounts`, `accounts_proposed`, and `book` (Order Book). See [Transaction Streams](https://ripple.com/build/rippled-apis/#transaction-streams) for details.
|
||||
`peerStatusChange` | (Admin-only) Reports a large amount of information on the activities of other `rippled` servers to which the server is connected.
|
||||
`path_find` | Asynchronous follow-up response to the currently open path\_find request. See [rippled path\_find method](https://xrpl.org/path_find.html) for details.
|
||||
|
||||
To register your listener function, use `connection.on(type, handler)`.
|
||||
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.2",
|
||||
"license": "ISC",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/ws": "^6.0.3",
|
||||
"@types/ws": "^7.2.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"jsonschema": "1.2.2",
|
||||
@@ -30,7 +30,7 @@
|
||||
"ripple-address-codec": "^4.0.0",
|
||||
"ripple-binary-codec": "^0.2.5",
|
||||
"ripple-keypairs": "^0.11.0",
|
||||
"ripple-lib-transactionparser": "0.8.1",
|
||||
"ripple-lib-transactionparser": "0.8.2",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -39,14 +39,14 @@
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.3.3",
|
||||
"assert-diff": "^2.0.3",
|
||||
"doctoc": "^0.15.0",
|
||||
"ejs": "^2.3.4",
|
||||
"doctoc": "^1.4.0",
|
||||
"ejs": "^3.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "6.2.2",
|
||||
"mocha": "7.0.0",
|
||||
"mocha-junit-reporter": "^1.9.1",
|
||||
"nyc": "^14.1.1",
|
||||
"nyc": "^15.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.4",
|
||||
|
||||
@@ -153,10 +153,10 @@ class RippleAPI extends EventEmitter {
|
||||
})
|
||||
this.connection.on('disconnected', code => {
|
||||
let finalCode = code
|
||||
// This is a backwards-compatible fix for this change in the ws library:
|
||||
// https://github.com/websockets/ws/issues/1257
|
||||
// 1005: This is a backwards-compatible fix for this change in the ws library: https://github.com/websockets/ws/issues/1257
|
||||
// 4000: Connection uses a 4000 code internally to indicate a manual disconnect/close
|
||||
// TODO: Remove in next major, breaking version
|
||||
if (finalCode === 1005) {
|
||||
if (finalCode === 1005 || finalCode === 4000) {
|
||||
finalCode = 1000
|
||||
}
|
||||
this.emit('disconnected', finalCode)
|
||||
|
||||
44
src/common/backoff.ts
Normal file
44
src/common/backoff.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Original code based on "backo" - https://github.com/segmentio/backo
|
||||
* MIT License - Copyright 2014 Segment.io
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Back off strategy that increases exponentionally. Useful with repeated
|
||||
* setTimeout calls over a network (where the destination may be down).
|
||||
*/
|
||||
export class ExponentialBackoff {
|
||||
private readonly ms: number
|
||||
private readonly max: number
|
||||
private readonly factor: number = 2
|
||||
private readonly jitter: number = 0
|
||||
attempts: number = 0
|
||||
|
||||
constructor(opts: {min?: number; max?: number} = {}) {
|
||||
this.ms = opts.min || 100
|
||||
this.max = opts.max || 10000
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the backoff duration.
|
||||
*/
|
||||
duration() {
|
||||
var ms = this.ms * Math.pow(this.factor, this.attempts++)
|
||||
if (this.jitter) {
|
||||
var rand = Math.random()
|
||||
var deviation = Math.floor(rand * this.jitter * ms)
|
||||
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation
|
||||
}
|
||||
return Math.min(ms, this.max) | 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the number of attempts.
|
||||
*/
|
||||
reset() {
|
||||
this.attempts = 0
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,5 +34,5 @@ export {
|
||||
iso8601ToRippleTime,
|
||||
rippleTimeToISO8601
|
||||
} from './utils'
|
||||
export {default as Connection} from './connection'
|
||||
export {Connection} from './connection'
|
||||
export {txFlags} from './txflags'
|
||||
|
||||
@@ -62,6 +62,8 @@ function loadSchemas() {
|
||||
require('./schemas/specifications/check-cash.json'),
|
||||
require('./schemas/specifications/check-cancel.json'),
|
||||
require('./schemas/specifications/trustline.json'),
|
||||
require('./schemas/specifications/deposit-preauth.json'),
|
||||
require('./schemas/specifications/account-delete.json'),
|
||||
require('./schemas/output/sign.json'),
|
||||
require('./schemas/output/submit.json'),
|
||||
require('./schemas/output/get-account-info.json'),
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"description": "The entropy to use to generate the seed."
|
||||
"description": "The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)"
|
||||
},
|
||||
"algorithm": {
|
||||
"type": "string",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"description": "The entropy to use to generate the seed."
|
||||
"description": "The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)"
|
||||
},
|
||||
"algorithm": {
|
||||
"type": "string",
|
||||
@@ -24,6 +24,10 @@
|
||||
"test": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`."
|
||||
},
|
||||
"includeClassicAddress": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether the classic address should also be included in the returned payload."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"paymentChannelClaim",
|
||||
"checkCreate",
|
||||
"checkCancel",
|
||||
"checkCash"
|
||||
"checkCash",
|
||||
"depositPreauth",
|
||||
"accountDelete"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -208,6 +208,30 @@
|
||||
"$ref": "paymentChannelClaim"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"depositPreauth"
|
||||
]
|
||||
},
|
||||
"specification": {
|
||||
"$ref": "depositPreauth"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"accountDelete"
|
||||
]
|
||||
},
|
||||
"specification": {
|
||||
"$ref": "accountDelete"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
29
src/common/schemas/specifications/account-delete.json
Normal file
29
src/common/schemas/specifications/account-delete.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "accountDelete",
|
||||
"link": "account-delete",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destination": {
|
||||
"$ref": "address",
|
||||
"description": "Address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
|
||||
},
|
||||
"destinationTag": {
|
||||
"$ref": "tag",
|
||||
"description": "(Optional) Arbitrary destination tag that identifies a hosted recipient or other information for the recipient of the deleted account's leftover XRP."
|
||||
},
|
||||
"destinationXAddress": {
|
||||
"$ref": "address",
|
||||
"description": "X-address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
"required": ["destination"]
|
||||
},
|
||||
{
|
||||
"required": ["destinationXAddress"]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
21
src/common/schemas/specifications/deposit-preauth.json
Normal file
21
src/common/schemas/specifications/deposit-preauth.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "depositPreauth",
|
||||
"link": "deposit-preauth",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorize": {
|
||||
"$ref": "address",
|
||||
"description": "Address of the account that can cash the check."
|
||||
},
|
||||
"unauthorize": {
|
||||
"$ref": "address",
|
||||
"description": "Address of the account that can cash the check."
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{"required": ["authorize"]},
|
||||
{"required": ["unauthorize"]}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export interface SignerEntry {
|
||||
Account: string
|
||||
SignerWeight: number
|
||||
SignerEntry: {
|
||||
Account: string
|
||||
SignerWeight: number
|
||||
}
|
||||
}
|
||||
|
||||
34
src/ledger/parse/account-delete.ts
Normal file
34
src/ledger/parse/account-delete.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {classicAddressToXAddress} from 'ripple-address-codec'
|
||||
|
||||
export type FormattedAccountDelete = {
|
||||
// account (address) of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destination: string
|
||||
|
||||
// (Optional) Arbitrary destination tag that identifies a hosted recipient or other information
|
||||
// for the recipient of the deleted account's leftover XRP. NB: Ensure that the hosted recipient is
|
||||
// able to account for AccountDelete transactions; if not, your balance may not be properly credited.
|
||||
destinationTag?: number
|
||||
|
||||
// X-address of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destinationXAddress: string
|
||||
}
|
||||
|
||||
function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
assert.ok(tx.TransactionType === 'AccountDelete')
|
||||
|
||||
return removeUndefined({
|
||||
destination: tx.Destination,
|
||||
destinationTag: tx.DestinationTag,
|
||||
destinationXAddress: classicAddressToXAddress(
|
||||
tx.Destination,
|
||||
tx.DestinationTag === undefined ? false : tx.DestinationTag,
|
||||
false
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export default parseAccountDelete
|
||||
@@ -1,27 +1,31 @@
|
||||
import {parseOutcome} from './utils'
|
||||
import {removeUndefined} from '../../common'
|
||||
import parsePayment from './payment'
|
||||
import parseTrustline from './trustline'
|
||||
import parseOrder from './order'
|
||||
import parseOrderCancellation from './cancellation'
|
||||
|
||||
import parseSettings from './settings'
|
||||
import parseAccountDelete from './account-delete'
|
||||
import parseCheckCancel from './check-cancel'
|
||||
import parseCheckCash from './check-cash'
|
||||
import parseCheckCreate from './check-create'
|
||||
import parseDepositPreauth from './deposit-preauth'
|
||||
import parseEscrowCancellation from './escrow-cancellation'
|
||||
import parseEscrowCreation from './escrow-creation'
|
||||
import parseEscrowExecution from './escrow-execution'
|
||||
import parseEscrowCancellation from './escrow-cancellation'
|
||||
import parseCheckCreate from './check-create'
|
||||
import parseCheckCash from './check-cash'
|
||||
import parseCheckCancel from './check-cancel'
|
||||
import parseDepositPreauth from './deposit-preauth'
|
||||
import parseOrderCancellation from './cancellation'
|
||||
import parseOrder from './order'
|
||||
import parsePayment from './payment'
|
||||
import parsePaymentChannelClaim from './payment-channel-claim'
|
||||
import parsePaymentChannelCreate from './payment-channel-create'
|
||||
import parsePaymentChannelFund from './payment-channel-fund'
|
||||
import parsePaymentChannelClaim from './payment-channel-claim'
|
||||
import parseFeeUpdate from './fee-update'
|
||||
import parseAmendment from './amendment'
|
||||
import parseTrustline from './trustline'
|
||||
|
||||
import parseAmendment from './amendment' // pseudo-transaction
|
||||
import parseFeeUpdate from './fee-update' // pseudo-transaction
|
||||
|
||||
function parseTransactionType(type) {
|
||||
// Ordering matches https://developers.ripple.com/transaction-types.html
|
||||
const mapping = {
|
||||
AccountSet: 'settings',
|
||||
AccountDelete: 'accountDelete',
|
||||
CheckCancel: 'checkCancel',
|
||||
CheckCash: 'checkCash',
|
||||
CheckCreate: 'checkCreate',
|
||||
@@ -49,23 +53,25 @@ function parseTransactionType(type) {
|
||||
function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
const type = parseTransactionType(tx.TransactionType)
|
||||
const mapping = {
|
||||
payment: parsePayment,
|
||||
trustline: parseTrustline,
|
||||
order: parseOrder,
|
||||
orderCancellation: parseOrderCancellation,
|
||||
settings: parseSettings,
|
||||
accountDelete: parseAccountDelete,
|
||||
checkCancel: parseCheckCancel,
|
||||
checkCash: parseCheckCash,
|
||||
checkCreate: parseCheckCreate,
|
||||
depositPreauth: parseDepositPreauth,
|
||||
escrowCancellation: parseEscrowCancellation,
|
||||
escrowCreation: parseEscrowCreation,
|
||||
escrowExecution: parseEscrowExecution,
|
||||
escrowCancellation: parseEscrowCancellation,
|
||||
checkCreate: parseCheckCreate,
|
||||
checkCash: parseCheckCash,
|
||||
checkCancel: parseCheckCancel,
|
||||
depositPreauth: parseDepositPreauth,
|
||||
orderCancellation: parseOrderCancellation,
|
||||
order: parseOrder,
|
||||
payment: parsePayment,
|
||||
paymentChannelClaim: parsePaymentChannelClaim,
|
||||
paymentChannelCreate: parsePaymentChannelCreate,
|
||||
paymentChannelFund: parsePaymentChannelFund,
|
||||
paymentChannelClaim: parsePaymentChannelClaim,
|
||||
feeUpdate: parseFeeUpdate,
|
||||
amendment: parseAmendment
|
||||
trustline: parseTrustline,
|
||||
|
||||
amendment: parseAmendment, // pseudo-transaction
|
||||
feeUpdate: parseFeeUpdate // pseudo-transaction
|
||||
}
|
||||
const parser: Function = mapping[type]
|
||||
|
||||
|
||||
@@ -9,12 +9,14 @@ function getLedgerVersion(this: RippleAPI): Promise<number> {
|
||||
return this.connection.getLedgerVersion()
|
||||
}
|
||||
|
||||
function connect(this: RippleAPI): Promise<void> {
|
||||
async function connect(this: RippleAPI): Promise<void> {
|
||||
return this.connection.connect()
|
||||
}
|
||||
|
||||
function disconnect(this: RippleAPI): Promise<void> {
|
||||
return this.connection.disconnect()
|
||||
async function disconnect(this: RippleAPI): Promise<void> {
|
||||
// backwards compatibility: connection.disconnect() can return a number, but
|
||||
// this method returns nothing. SO we await but don't return any result.
|
||||
await this.connection.disconnect()
|
||||
}
|
||||
|
||||
function formatLedgerClose(ledgerClose: any): object {
|
||||
|
||||
@@ -19,12 +19,10 @@ export default <TestSuite>{
|
||||
},
|
||||
|
||||
'getFee - high load_factor': async (api, address) => {
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {highLoadFactor: true}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {highLoadFactor: true}
|
||||
})
|
||||
const fee = await api.getFee()
|
||||
assert.strictEqual(fee, '2')
|
||||
},
|
||||
@@ -33,12 +31,10 @@ export default <TestSuite>{
|
||||
// Ensure that overriding with high maxFeeXRP of '51540' causes no errors.
|
||||
// (fee will actually be 51539.607552)
|
||||
api._maxFeeXRP = '51540'
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {highLoadFactor: true}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {highLoadFactor: true}
|
||||
})
|
||||
const fee = await api.getFee()
|
||||
assert.strictEqual(fee, '51539.607552')
|
||||
},
|
||||
|
||||
@@ -14,12 +14,10 @@ export default <TestSuite>{
|
||||
},
|
||||
|
||||
'error': async (api, address) => {
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {returnErrorOnServerInfo: true}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {returnErrorOnServerInfo: true}
|
||||
})
|
||||
try {
|
||||
await api.getServerInfo()
|
||||
throw new Error('Should throw NetworkError')
|
||||
@@ -31,12 +29,10 @@ export default <TestSuite>{
|
||||
},
|
||||
|
||||
'no validated ledger': async (api, address) => {
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {serverInfoWithoutValidated: true}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {serverInfoWithoutValidated: true}
|
||||
})
|
||||
const serverInfo = await api.getServerInfo()
|
||||
assert.strictEqual(serverInfo.networkLedger, 'waiting')
|
||||
},
|
||||
|
||||
@@ -354,6 +354,16 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'AccountDelete': async (api, address) => {
|
||||
const hash = 'EC2AB14028DC84DE525470AB4DAAA46358B50A8662C63804BFF38244731C0CB9'
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.accountDelete,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'no Meta': async (api, address) => {
|
||||
const hash =
|
||||
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B'
|
||||
|
||||
@@ -416,12 +416,10 @@ export default <TestSuite>{
|
||||
api,
|
||||
address
|
||||
) => {
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {loadFactor: 5407.96875}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {loadFactor: 5407.96875}
|
||||
})
|
||||
const expectedResponse = {
|
||||
txJSON:
|
||||
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"64896","Sequence":23}',
|
||||
|
||||
@@ -745,6 +745,33 @@ export default <TestSuite>{
|
||||
return assertResultMatch(response, expected, 'prepare')
|
||||
},
|
||||
|
||||
'AccountDelete': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '5.0' // 5 XRP fee for AccountDelete
|
||||
}
|
||||
|
||||
const txJSON = {
|
||||
TransactionType: 'AccountDelete',
|
||||
Account: address,
|
||||
Destination: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe'
|
||||
}
|
||||
|
||||
const response = await api.prepareTransaction(txJSON, localInstructions)
|
||||
const expected = {
|
||||
txJSON:
|
||||
'{"TransactionType":"AccountDelete","Account":"' +
|
||||
address +
|
||||
'","Destination":"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}',
|
||||
instructions: {
|
||||
fee: '0.000012',
|
||||
sequence: 23,
|
||||
maxLedgerVersion: 8820051
|
||||
}
|
||||
}
|
||||
return assertResultMatch(response, expected, 'prepare')
|
||||
},
|
||||
|
||||
// prepareTransaction - Payment
|
||||
'Payment - normal': async (api, address) => {
|
||||
const localInstructions = {
|
||||
@@ -1023,12 +1050,10 @@ export default <TestSuite>{
|
||||
api,
|
||||
address
|
||||
) => {
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {loadFactor: 5407.96875}
|
||||
})
|
||||
)
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {loadFactor: 5407.96875}
|
||||
})
|
||||
|
||||
const expectedResponse = {
|
||||
txJSON:
|
||||
|
||||
@@ -212,7 +212,7 @@ export default <TestSuite>{
|
||||
) => {
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
const request = {
|
||||
// TODO: This fails when address is X-Address
|
||||
// TODO: This fails when address is X-address
|
||||
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1.2","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
|
||||
instructions: {
|
||||
fee: '0.0000012',
|
||||
@@ -232,7 +232,7 @@ export default <TestSuite>{
|
||||
) => {
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
const request = {
|
||||
// TODO: This fails when address is X-Address
|
||||
// TODO: This fails when address is X-address
|
||||
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1123456.7","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
|
||||
instructions: {
|
||||
fee: '1.1234567',
|
||||
@@ -289,7 +289,7 @@ export default <TestSuite>{
|
||||
api._maxFeeXRP = '2.1'
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
const request = {
|
||||
// TODO: This fails when address is X-Address
|
||||
// TODO: This fails when address is X-address
|
||||
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
|
||||
instructions: {
|
||||
fee: '2.01',
|
||||
|
||||
37
test/backoff-test.ts
Normal file
37
test/backoff-test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import assert from 'assert-diff'
|
||||
import {ExponentialBackoff} from '../src/common/backoff'
|
||||
|
||||
describe('ExponentialBackoff', function() {
|
||||
it('duration() return value starts with the min value', function() {
|
||||
// default: 100ms
|
||||
assert(new ExponentialBackoff().duration(), 100)
|
||||
assert(new ExponentialBackoff({min: 100}).duration(), 100)
|
||||
assert(new ExponentialBackoff({min: 123}).duration(), 123)
|
||||
})
|
||||
|
||||
it('duration() return value increases when called multiple times', function() {
|
||||
const backoff = new ExponentialBackoff({min: 100, max: 1000})
|
||||
assert.strictEqual(backoff.duration(), 100)
|
||||
assert.strictEqual(backoff.duration(), 200)
|
||||
assert.strictEqual(backoff.duration(), 400)
|
||||
assert.strictEqual(backoff.duration(), 800)
|
||||
})
|
||||
|
||||
it('duration() never returns greater than the max value', function() {
|
||||
const backoff = new ExponentialBackoff({min: 300, max: 1000})
|
||||
assert.strictEqual(backoff.duration(), 300)
|
||||
assert.strictEqual(backoff.duration(), 600)
|
||||
assert.strictEqual(backoff.duration(), 1000)
|
||||
assert.strictEqual(backoff.duration(), 1000)
|
||||
})
|
||||
|
||||
it('reset() will reset the duration() value', function() {
|
||||
const backoff = new ExponentialBackoff({min: 100, max: 1000})
|
||||
assert.strictEqual(backoff.duration(), 100)
|
||||
assert.strictEqual(backoff.duration(), 200)
|
||||
assert.strictEqual(backoff.duration(), 400)
|
||||
backoff.reset()
|
||||
assert.strictEqual(backoff.duration(), 100)
|
||||
assert.strictEqual(backoff.duration(), 200)
|
||||
})
|
||||
})
|
||||
@@ -4,6 +4,7 @@ import setupAPI from './setup-api'
|
||||
import responses from './fixtures/responses'
|
||||
import ledgerClosed from './fixtures/rippled/ledger-close.json'
|
||||
import {RippleAPI} from 'ripple-api'
|
||||
import {ignoreWebSocketDisconnect} from './utils'
|
||||
const schemaValidator = RippleAPI._PRIVATE.schemaValidator
|
||||
|
||||
const TIMEOUT = 20000
|
||||
@@ -43,12 +44,12 @@ describe('RippleAPIBroadcast', function() {
|
||||
ledgerNext.ledger_index++
|
||||
|
||||
this.api._apis.forEach(api =>
|
||||
api.connection._send(
|
||||
JSON.stringify({
|
||||
api.connection
|
||||
.request({
|
||||
command: 'echo',
|
||||
data: ledgerNext
|
||||
})
|
||||
)
|
||||
.catch(ignoreWebSocketDisconnect)
|
||||
)
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -63,11 +64,11 @@ describe('RippleAPIBroadcast', function() {
|
||||
assert.strictEqual(info, 'info')
|
||||
done()
|
||||
})
|
||||
this.api._apis[1].connection._send(
|
||||
JSON.stringify({
|
||||
this.api._apis[1].connection
|
||||
.request({
|
||||
command: 'echo',
|
||||
data: {error: 'type', error_message: 'info'}
|
||||
})
|
||||
)
|
||||
.catch(ignoreWebSocketDisconnect)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import assert from 'assert-diff'
|
||||
import setupAPI from './setup-api'
|
||||
import {RippleAPI} from 'ripple-api'
|
||||
import ledgerClose from './fixtures/rippled/ledger-close.json'
|
||||
import {ignoreWebSocketDisconnect} from './utils'
|
||||
const utils = RippleAPI._PRIVATE.ledgerUtils
|
||||
|
||||
const TIMEOUT = 200000 // how long before each test case times out
|
||||
@@ -35,11 +36,12 @@ describe('Connection', function() {
|
||||
})
|
||||
|
||||
describe('trace', () => {
|
||||
const message1 = '{"type": "transaction"}'
|
||||
const message2 = '{"type": "path_find"}'
|
||||
const mockedRequestData = {mocked: 'request'}
|
||||
const mockedResponse = JSON.stringify({mocked: 'response', id: 0})
|
||||
const expectedMessages = [
|
||||
['send', message1],
|
||||
['receive', message2]
|
||||
// We add the ID here, since it's not a part of the user-provided request.
|
||||
['send', JSON.stringify({...mockedRequestData, id: 0})],
|
||||
['receive', mockedResponse]
|
||||
]
|
||||
const originalConsoleLog = console.log
|
||||
|
||||
@@ -52,8 +54,8 @@ describe('Connection', function() {
|
||||
console.log = (id, message) => messages.push([id, message])
|
||||
const connection: any = new utils.common.Connection('url', {trace: false})
|
||||
connection._ws = {send: function() {}}
|
||||
connection._send(message1)
|
||||
connection._onMessage(message2)
|
||||
connection.request(mockedRequestData)
|
||||
connection._onMessage(mockedResponse)
|
||||
assert.deepEqual(messages, [])
|
||||
})
|
||||
|
||||
@@ -62,8 +64,8 @@ describe('Connection', function() {
|
||||
console.log = (id, message) => messages.push([id, message])
|
||||
const connection: any = new utils.common.Connection('url', {trace: true})
|
||||
connection._ws = {send: function() {}}
|
||||
connection._send(message1)
|
||||
connection._onMessage(message2)
|
||||
connection.request(mockedRequestData)
|
||||
connection._onMessage(mockedResponse)
|
||||
assert.deepEqual(messages, expectedMessages)
|
||||
})
|
||||
|
||||
@@ -73,12 +75,31 @@ describe('Connection', function() {
|
||||
trace: (id, message) => messages.push([id, message])
|
||||
})
|
||||
connection._ws = {send: function() {}}
|
||||
connection._send(message1)
|
||||
connection._onMessage(message2)
|
||||
connection.request(mockedRequestData)
|
||||
connection._onMessage(mockedResponse)
|
||||
assert.deepEqual(messages, expectedMessages)
|
||||
})
|
||||
})
|
||||
|
||||
it('ledger methods work as expected', async function() {
|
||||
assert.strictEqual(await this.api.connection.getLedgerVersion(), 8819951)
|
||||
assert.strictEqual(
|
||||
await this.api.connection.hasLedgerVersion(8819951),
|
||||
true
|
||||
)
|
||||
assert.strictEqual(
|
||||
await this.api.connection.hasLedgerVersions(8819951, undefined),
|
||||
true
|
||||
)
|
||||
// It would be nice to test a better range, but the mocked ledger only supports this single number
|
||||
assert.strictEqual(
|
||||
await this.api.connection.hasLedgerVersions(8819951, 8819951),
|
||||
true
|
||||
)
|
||||
assert.strictEqual(await this.api.connection.getFeeBase(), 10)
|
||||
assert.strictEqual(await this.api.connection.getFeeRef(), 10)
|
||||
})
|
||||
|
||||
it('with proxy', function(done) {
|
||||
if (isBrowser) {
|
||||
done()
|
||||
@@ -154,13 +175,11 @@ describe('Connection', function() {
|
||||
})
|
||||
})
|
||||
|
||||
it('DisconnectedError', function() {
|
||||
this.api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'config',
|
||||
data: {disconnectOnServerInfo: true}
|
||||
})
|
||||
)
|
||||
it('DisconnectedError', async function() {
|
||||
await this.api.connection.request({
|
||||
command: 'config',
|
||||
data: {disconnectOnServerInfo: true}
|
||||
})
|
||||
return this.api
|
||||
.getServerInfo()
|
||||
.then(() => {
|
||||
@@ -172,12 +191,12 @@ describe('Connection', function() {
|
||||
})
|
||||
|
||||
it('TimeoutError', function() {
|
||||
this.api.connection._send = function() {
|
||||
return Promise.resolve({})
|
||||
this.api.connection._ws.send = function(message, options, callback) {
|
||||
callback(null)
|
||||
}
|
||||
const request = {command: 'server_info'}
|
||||
return this.api.connection
|
||||
.request(request, 1)
|
||||
.request(request, 10)
|
||||
.then(() => {
|
||||
assert(false, 'Should throw TimeoutError')
|
||||
})
|
||||
@@ -201,23 +220,33 @@ describe('Connection', function() {
|
||||
})
|
||||
})
|
||||
|
||||
it('ResponseFormatError', function() {
|
||||
this.api.connection._send = function(message) {
|
||||
const parsed = JSON.parse(message)
|
||||
setTimeout(() => {
|
||||
this._ws.emit(
|
||||
'message',
|
||||
JSON.stringify({
|
||||
id: parsed.id,
|
||||
type: 'response',
|
||||
status: 'unrecognized'
|
||||
})
|
||||
)
|
||||
}, 2)
|
||||
return new Promise(() => {})
|
||||
it('DisconnectedError on initial _onOpen send', async function() {
|
||||
// _onOpen previously could throw PromiseRejectionHandledWarning: Promise rejection was handled asynchronously
|
||||
// do not rely on the api.setup hook to test this as it bypasses the case, disconnect api connection first
|
||||
await this.api.disconnect();
|
||||
|
||||
// stub _onOpen to only run logic relevant to test case
|
||||
this.api.connection._onOpen = () => {
|
||||
// overload websocket send on open when _ws exists
|
||||
this.api.connection._ws.send = function(data, options, cb) {
|
||||
// recent ws throws this error instead of calling back
|
||||
throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
|
||||
}
|
||||
const request = {command: 'subscribe', streams: ['ledger']};
|
||||
return this.api.connection.request(request);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.api.connect();
|
||||
} catch (error) {
|
||||
assert(error instanceof this.api.errors.DisconnectedError);
|
||||
assert.strictEqual(error.message, 'WebSocket is not open: readyState 0 (CONNECTING)');
|
||||
}
|
||||
});
|
||||
|
||||
it('ResponseFormatError', function() {
|
||||
return this.api
|
||||
.getServerInfo()
|
||||
.request('test_command', {data: {unrecognizedResponse: true}})
|
||||
.then(() => {
|
||||
assert(false, 'Should throw ResponseFormatError')
|
||||
})
|
||||
@@ -226,34 +255,16 @@ describe('Connection', function() {
|
||||
})
|
||||
})
|
||||
|
||||
it('reconnect on unexpected close ', function(done) {
|
||||
it('reconnect on unexpected close', function(done) {
|
||||
this.api.connection.on('connected', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
this.api.connection._ws.close()
|
||||
}, 1)
|
||||
})
|
||||
|
||||
describe('reconnection test', function() {
|
||||
beforeEach(function() {
|
||||
this.api.connection.__workingUrl = this.api.connection._url
|
||||
this.api.connection.__doReturnBad = function() {
|
||||
this._url = this.__badUrl
|
||||
const self = this
|
||||
function onReconnect(num) {
|
||||
if (num >= 2) {
|
||||
self._url = self.__workingUrl
|
||||
self.removeListener('reconnecting', onReconnect)
|
||||
}
|
||||
}
|
||||
this.on('reconnecting', onReconnect)
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(function() {})
|
||||
|
||||
it('reconnect on several unexpected close', function(done) {
|
||||
if (isBrowser) {
|
||||
const phantomTest = /PhantomJS/
|
||||
@@ -265,15 +276,13 @@ describe('Connection', function() {
|
||||
}
|
||||
this.timeout(70001)
|
||||
const self = this
|
||||
self.api.connection.__badUrl = 'ws://testripple.circleci.com:129'
|
||||
function breakConnection() {
|
||||
self.api.connection.__doReturnBad()
|
||||
self.api.connection._send(
|
||||
JSON.stringify({
|
||||
self.api.connection
|
||||
.request({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
})
|
||||
)
|
||||
.catch(ignoreWebSocketDisconnect)
|
||||
}
|
||||
|
||||
let connectsCount = 0
|
||||
@@ -304,11 +313,11 @@ describe('Connection', function() {
|
||||
' instead)'
|
||||
)
|
||||
)
|
||||
} else if (reconnectsCount !== num * 2) {
|
||||
} else if (reconnectsCount !== num) {
|
||||
done(
|
||||
new Error(
|
||||
'reconnectsCount must be equal to ' +
|
||||
num * 2 +
|
||||
num +
|
||||
' (got ' +
|
||||
reconnectsCount +
|
||||
' instead)'
|
||||
@@ -346,6 +355,36 @@ describe('Connection', function() {
|
||||
// Hook up a listener for the reconnect event
|
||||
this.api.connection.on('reconnect', () => done())
|
||||
// Trigger a heartbeat
|
||||
this.api.connection._heartbeat().catch(error => {
|
||||
/* ignore - test expects heartbeat failure */
|
||||
})
|
||||
})
|
||||
|
||||
it('heartbeat failure and reconnect failure', function(done) {
|
||||
if (isBrowser) {
|
||||
const phantomTest = /PhantomJS/
|
||||
if (phantomTest.test(navigator.userAgent)) {
|
||||
// inside PhantomJS this one just hangs, so skip as not very relevant
|
||||
done()
|
||||
return
|
||||
}
|
||||
}
|
||||
// Set the heartbeat to less than the 1 second ping response
|
||||
this.api.connection._config.timeout = 500
|
||||
// Drop the test runner timeout, since this should be a quick test
|
||||
this.timeout(5000)
|
||||
// fail on reconnect/connection
|
||||
this.api.connection.reconnect = async () => {
|
||||
throw new Error('error on reconnect')
|
||||
}
|
||||
// Hook up a listener for the reconnect error event
|
||||
this.api.on('error', (error, message) => {
|
||||
if(error === 'reconnect' && message === 'error on reconnect') {
|
||||
return done()
|
||||
}
|
||||
return done(new Error('Expected error on reconnect'))
|
||||
})
|
||||
// Trigger a heartbeat
|
||||
this.api.connection._heartbeat()
|
||||
})
|
||||
|
||||
@@ -358,19 +397,19 @@ describe('Connection', function() {
|
||||
})
|
||||
|
||||
it('should emit disconnected event with code 1006 (CLOSE_ABNORMAL)', function(done) {
|
||||
this.api.once('error', error => {
|
||||
this.api.connection.once('error', error => {
|
||||
done(new Error('should not throw error, got ' + String(error)))
|
||||
})
|
||||
this.api.once('disconnected', code => {
|
||||
this.api.connection.once('disconnected', code => {
|
||||
assert.strictEqual(code, 1006)
|
||||
done()
|
||||
})
|
||||
this.api.connection._send(
|
||||
JSON.stringify({
|
||||
this.api.connection
|
||||
.request({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
})
|
||||
)
|
||||
.catch(ignoreWebSocketDisconnect)
|
||||
})
|
||||
|
||||
it('should emit connected event on after reconnect', function(done) {
|
||||
@@ -431,7 +470,8 @@ describe('Connection', function() {
|
||||
this.api.connection.on('path_find', () => {
|
||||
pathFindCount++
|
||||
})
|
||||
this.api.connection.on('1', () => {
|
||||
this.api.connection.on('response', message => {
|
||||
assert.strictEqual(message.id, 1)
|
||||
assert.strictEqual(transactionCount, 1)
|
||||
assert.strictEqual(pathFindCount, 1)
|
||||
done()
|
||||
@@ -526,15 +566,13 @@ describe('Connection', function() {
|
||||
it(
|
||||
'should throw RippledNotInitializedError if server does not have ' +
|
||||
'validated ledgers',
|
||||
function() {
|
||||
async function() {
|
||||
this.timeout(3000)
|
||||
|
||||
this.api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'global_config',
|
||||
data: {returnEmptySubscribeRequest: 1}
|
||||
})
|
||||
)
|
||||
await this.api.connection.request({
|
||||
command: 'global_config',
|
||||
data: {returnEmptySubscribeRequest: 1}
|
||||
})
|
||||
|
||||
const api = new RippleAPI({server: this.api.connection._url})
|
||||
return api.connect().then(
|
||||
@@ -554,7 +592,6 @@ describe('Connection', function() {
|
||||
|
||||
it('should try to reconnect on empty subscribe response on reconnect', function(done) {
|
||||
this.timeout(23000)
|
||||
|
||||
this.api.on('error', error => {
|
||||
done(error || new Error('Should not emit error.'))
|
||||
})
|
||||
@@ -569,19 +606,9 @@ describe('Connection', function() {
|
||||
this.api.on('disconnected', () => {
|
||||
disconnectedCount++
|
||||
})
|
||||
|
||||
this.api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'global_config',
|
||||
data: {returnEmptySubscribeRequest: 3}
|
||||
})
|
||||
)
|
||||
|
||||
this.api.connection._send(
|
||||
JSON.stringify({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
})
|
||||
)
|
||||
this.api.connection.request({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 5}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
32
test/fixtures/responses/get-transaction-account-delete.json
vendored
Normal file
32
test/fixtures/responses/get-transaction-account-delete.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "accountDelete",
|
||||
"address": "rM5qup5BYDLMXaR5KU1hiC9HhFMuBVrnKv",
|
||||
"sequence": 3227049,
|
||||
"id": "EC2AB14028DC84DE525470AB4DAAA46358B50A8662C63804BFF38244731C0CB9",
|
||||
"specification": {
|
||||
"destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
||||
"destinationXAddress": "XV5kHfQmzDQjbFNv4jX3FX9Y7ig5QhpKGEFCq4mdLfhdxMq"
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2019-12-17T09:16:51.000Z",
|
||||
"fee": "5",
|
||||
"balanceChanges": {
|
||||
"rM5qup5BYDLMXaR5KU1hiC9HhFMuBVrnKv": [
|
||||
{
|
||||
"currency": "XRP",
|
||||
"value": "-10000"
|
||||
}
|
||||
],
|
||||
"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe": [
|
||||
{
|
||||
"currency": "XRP",
|
||||
"value": "9995"
|
||||
}
|
||||
]
|
||||
},
|
||||
"orderbookChanges": {},
|
||||
"ledgerVersion": 3232071,
|
||||
"indexInLedger": 0
|
||||
}
|
||||
}
|
||||
3
test/fixtures/responses/index.js
vendored
3
test/fixtures/responses/index.js
vendored
@@ -67,7 +67,8 @@ module.exports = {
|
||||
paymentChannelClaim:
|
||||
require('./get-transaction-payment-channel-claim.json'),
|
||||
amendment: require('./get-transaction-amendment.json'),
|
||||
feeUpdate: require('./get-transaction-fee-update.json')
|
||||
feeUpdate: require('./get-transaction-fee-update.json'),
|
||||
accountDelete: require('./get-transaction-account-delete.json')
|
||||
},
|
||||
getTransactions: {
|
||||
normal: require('./get-transactions.json'),
|
||||
|
||||
3
test/fixtures/rippled/index.js
vendored
3
test/fixtures/rippled/index.js
vendored
@@ -99,6 +99,7 @@ module.exports = {
|
||||
NoMeta: require('./tx/no-meta.json'),
|
||||
LedgerZero: require('./tx/ledger-zero.json'),
|
||||
Amendment: require('./tx/amendment.json'),
|
||||
SetFee: require('./tx/set-fee.json')
|
||||
SetFee: require('./tx/set-fee.json'),
|
||||
AccountDelete: require('./tx/account-delete.json')
|
||||
}
|
||||
};
|
||||
|
||||
66
test/fixtures/rippled/tx/account-delete.json
vendored
Normal file
66
test/fixtures/rippled/tx/account-delete.json
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"id": 0,
|
||||
"result": {
|
||||
"Account": "rM5qup5BYDLMXaR5KU1hiC9HhFMuBVrnKv",
|
||||
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
||||
"Fee": "5000000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 3232818,
|
||||
"Sequence": 3227049,
|
||||
"SigningPubKey": "022E0DBF14BC4CFF96BC839557EE6F12F6DA45DCD917376F805E65D1B1C60A8CE6",
|
||||
"TransactionType": "AccountDelete",
|
||||
"TxnSignature": "304402207BDBE1B71C8BD00363905817C9373880CE9E8F0080623D457495E2B760BBBEE402202EDEB977D1ED865C1EAB88FE28581E3F8A672097B8BB0956E977C6EC87CA668C",
|
||||
"date": 629889411,
|
||||
"hash": "EC2AB14028DC84DE525470AB4DAAA46358B50A8662C63804BFF38244731C0CB9",
|
||||
"inLedger": 3232071,
|
||||
"ledger_index": 3232071,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"DeletedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rM5qup5BYDLMXaR5KU1hiC9HhFMuBVrnKv",
|
||||
"Balance": "0",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 0,
|
||||
"PreviousTxnID": "2737BEDDDA1D7FB523CFB84B891216331CC4CC999349828D81C6C727A1115A44",
|
||||
"PreviousTxnLgrSeq": 3227049,
|
||||
"Sequence": 3227050
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "08EBF94D7BB527442E0B51F533B902DDCFBD423D8E95FAE752BE7876A29E875B",
|
||||
"PreviousFields": {
|
||||
"Balance": "10000000000",
|
||||
"Sequence": 3227049
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
||||
"Balance": "99991026734967448",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 0,
|
||||
"Sequence": 2978
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "31CCE9D28412FF973E9AB6D0FA219BACF19687D9A2456A0C2ABC3280E9D47E37",
|
||||
"PreviousFields": {
|
||||
"Balance": "99991016739967448"
|
||||
},
|
||||
"PreviousTxnID": "2737BEDDDA1D7FB523CFB84B891216331CC4CC999349828D81C6C727A1115A44",
|
||||
"PreviousTxnLgrSeq": 3227049
|
||||
}
|
||||
}
|
||||
],
|
||||
"DeliveredAmount": "9995000000",
|
||||
"TransactionIndex": 0,
|
||||
"TransactionResult": "tesSUCCESS",
|
||||
"delivered_amount": "9995000000"
|
||||
},
|
||||
"validated": true
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import Connection from '../../src/common/connection'
|
||||
import {Connection} from '../../src/common/connection'
|
||||
|
||||
const request1 = {
|
||||
command: 'server_info'
|
||||
|
||||
@@ -119,12 +119,26 @@ export function createMockRippled(port) {
|
||||
mock.on('request_config', function(request, conn) {
|
||||
assert.strictEqual(request.command, 'config')
|
||||
conn.config = _.assign(conn.config, request.data)
|
||||
conn.send(
|
||||
createResponse(request, {
|
||||
status: 'success',
|
||||
type: 'response',
|
||||
result: {}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
mock.on('request_test_command', function(request, conn) {
|
||||
assert.strictEqual(request.command, 'test_command')
|
||||
if (request.data.disconnectIn) {
|
||||
setTimeout(conn.terminate.bind(conn), request.data.disconnectIn)
|
||||
conn.send(
|
||||
createResponse(request, {
|
||||
status: 'success',
|
||||
type: 'response',
|
||||
result: {}
|
||||
})
|
||||
)
|
||||
} else if (request.data.openOnOtherPort) {
|
||||
getFreePort().then(newPort => {
|
||||
createMockRippled(newPort)
|
||||
@@ -145,12 +159,27 @@ export function createMockRippled(port) {
|
||||
}, request.data.closeServerAndReopen)
|
||||
})
|
||||
}, 10)
|
||||
} else if (request.data.unrecognizedResponse) {
|
||||
conn.send(
|
||||
createResponse(request, {
|
||||
status: 'unrecognized',
|
||||
type: 'response',
|
||||
result: {}
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
mock.on('request_global_config', function(request, conn) {
|
||||
assert.strictEqual(request.command, 'global_config')
|
||||
mock.config = _.assign(conn.config, request.data)
|
||||
conn.send(
|
||||
createResponse(request, {
|
||||
status: 'success',
|
||||
type: 'response',
|
||||
result: {}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
mock.on('request_echo', function(request, conn) {
|
||||
@@ -524,6 +553,11 @@ export function createMockRippled(port) {
|
||||
'81B9ECAE7195EB6E8034AEDF44D8415A7A803E14513FDBB34FA984AB37D59563'
|
||||
) {
|
||||
conn.send(createResponse(request, fixtures.tx.PaymentChannelClaim))
|
||||
} else if (
|
||||
request.transaction ===
|
||||
'EC2AB14028DC84DE525470AB4DAAA46358B50A8662C63804BFF38244731C0CB9'
|
||||
) {
|
||||
conn.send(createResponse(request, fixtures.tx.AccountDelete))
|
||||
} else if (
|
||||
request.transaction ===
|
||||
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11'
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('RippleAPI [Test Runner]', function() {
|
||||
})
|
||||
// Run each test with the newer, x-address style.
|
||||
if (!config.skipXAddress) {
|
||||
describe(`[X-Address]`, () => {
|
||||
describe(`[X-address]`, () => {
|
||||
for (const [testName, fn] of tests) {
|
||||
it(testName, function() {
|
||||
return fn(this.api, addresses.ACCOUNT_X)
|
||||
|
||||
@@ -33,7 +33,7 @@ interface LoadedTestSuite {
|
||||
name: string
|
||||
tests: [string, TestFn][]
|
||||
config: {
|
||||
/** Set to true to skip re-running tests with an X-Address. */
|
||||
/** Set to true to skip re-running tests with an X-address. */
|
||||
skipXAddress?: boolean
|
||||
}
|
||||
}
|
||||
@@ -139,3 +139,15 @@ export function loadTestSuites(): LoadedTestSuite[] {
|
||||
})
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore WebSocket DisconnectErrors. Useful for making requests where we don't
|
||||
* care about the response and plan to teardown the test before the response
|
||||
* has come back.
|
||||
*/
|
||||
export function ignoreWebSocketDisconnect(error: Error): void {
|
||||
if (error.message === 'websocket was closed') {
|
||||
return
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user