Remove ledger methods from Connection (#1543)

* remove ledger subscription from connection

* remove more client ledger stuff

* resolve TS concerns

* fix all tests except broadcast tests

* fix broadcast tests

* clean up more ledger stuff in testing

* respond to comments
This commit is contained in:
Mayukha Vadari
2021-08-23 15:18:21 -04:00
parent 0b08de5956
commit 52f1789ecd
23 changed files with 237 additions and 503 deletions

View File

@@ -39,23 +39,12 @@ class BroadcastClient extends Client {
})
clients.forEach((client) => {
client.on('ledger', this.onLedgerEvent.bind(this))
client.on('error', (errorCode, errorMessage, data) =>
this.emit('error', errorCode, errorMessage, data)
)
})
}
onLedgerEvent(ledger) {
if (
ledger.ledgerVersion > this.ledgerVersion ||
this.ledgerVersion == null
) {
this.ledgerVersion = ledger.ledgerVersion
this.emit('ledger', ledger)
}
}
getMethodNames() {
const methodNames: string[] = []
const firstClient = this._clients[0]

View File

@@ -2,7 +2,6 @@ import * as _ from 'lodash'
import {EventEmitter} from 'events'
import {parse as parseURL} from 'url'
import WebSocket from 'ws'
import RangeSet from './rangeset'
import {
RippledError,
DisconnectedError,
@@ -10,11 +9,10 @@ import {
TimeoutError,
ResponseFormatError,
ConnectionError,
RippledNotInitializedError,
RippleError
} from '../common/errors'
import {ExponentialBackoff} from './backoff'
import { LedgerStream, Request, Response } from '../models/methods'
import { Request, Response } from '../models/methods'
/**
* ConnectionOptions is the configuration for the Connection class.
@@ -115,56 +113,6 @@ function websocketSendAsync(ws: WebSocket, message: string) {
})
}
/**
* LedgerHistory is used to store and reference ledger information that has been
* captured by the Connection class over time.
*/
class LedgerHistory {
feeBase: null | number = null
feeRef: null | number = null
latestVersion: null | number = null
reserveBase: null | number = null
private availableVersions = new RangeSet()
/**
* Returns true if the given version exists.
*/
hasVersion(version: number): boolean {
return this.availableVersions.containsValue(version)
}
/**
* Returns true if the given range of versions exist (inclusive).
*/
hasVersions(lowVersion: number, highVersion: number): boolean {
return this.availableVersions.containsRange(lowVersion, highVersion)
}
/**
* Update LedgerHistory with a new ledger response object. The "responseData"
* format lets you pass in any valid rippled ledger response data, regardless
* of whether ledger history data exists or not. If relevant ledger data
* is found, we'll update our history (ex: from a "ledgerClosed" event).
*/
update(ledgerMessage: LedgerStream) {
// type: ignored
this.feeBase = ledgerMessage.fee_base
this.feeRef = ledgerMessage.fee_ref
// ledger_hash: ignored
this.latestVersion = ledgerMessage.ledger_index
// ledger_time: ignored
this.reserveBase = ledgerMessage.reserve_base
// reserve_inc: ignored (may be useful for advanced use cases)
// txn_count: ignored
if (ledgerMessage.validated_ledgers) {
this.availableVersions.reset()
this.availableVersions.parseAndAddRanges(ledgerMessage.validated_ledgers)
} else {
this.availableVersions.addValue(this.latestVersion)
}
}
}
/**
* Manage all the requests made to the websocket, and their async responses
* that come in from the WebSocket. Because they come in over the WS connection
@@ -300,7 +248,6 @@ export class Connection extends EventEmitter {
private _trace: (id: string, message: string) => void = () => {}
private _config: ConnectionOptions
private _ledger: LedgerHistory = new LedgerHistory()
private _requestManager = new RequestManager()
private _connectionManager = new ConnectionManager()
@@ -336,9 +283,6 @@ export class Connection extends EventEmitter {
if (data.type) {
this.emit(data.type, data)
}
if (data.type === 'ledgerClosed') {
this._ledger.update(data)
}
if (data.type === 'response') {
try {
this._requestManager.handleResponse(data)
@@ -380,42 +324,6 @@ export class Connection extends EventEmitter {
})
}
/**
* Wait for a valid connection before resolving. Useful for deferring methods
* until a connection has been established.
*/
private _waitForReady(): Promise<void> {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError())
} else if (this._state === WebSocket.OPEN) {
resolve()
} else {
this.once('connected', () => resolve())
}
})
}
private async _subscribeToLedger() {
const data = await this.request({
command: 'subscribe',
streams: ['ledger']
})
// If rippled instance doesn't have validated ledgers, disconnect and then reject.
if (_.isEmpty(data) || !data.result.ledger_index) {
try {
await this.disconnect()
} catch (error) {
// Ignore this error, propagate the root cause.
} finally {
// Throw the root error (takes precedence over try/catch).
// eslint-disable-next-line no-unsafe-finally
throw new RippledNotInitializedError('Rippled not initialized')
}
}
this._ledger.update(data.result)
}
private _onConnectionFailed = (errorOrCode: Error | number | null) => {
if (this._ws) {
this._ws.removeAllListeners()
@@ -518,7 +426,6 @@ export class Connection extends EventEmitter {
// Finalize the connection and resolve all awaiting connect() requests
try {
this._retryConnectionBackoff.reset()
await this._subscribeToLedger()
this._startHeartbeatInterval()
this._connectionManager.resolveAllAwaiting()
this.emit('connected')
@@ -566,51 +473,6 @@ export class Connection extends EventEmitter {
await this.connect()
}
async getFeeBase(): Promise<number> {
await this._waitForReady()
return this._ledger.feeBase!
}
async getFeeRef(): Promise<number> {
await this._waitForReady()
return this._ledger.feeRef!
}
async getLedgerVersion(): Promise<number> {
await this._waitForReady()
return this._ledger.latestVersion!
}
async getReserveBase(): Promise<number> {
await this._waitForReady()
return this._ledger.reserveBase!
}
/**
* Returns true if the given range of ledger versions exist in history
* (inclusive).
*/
async hasLedgerVersions(
lowLedgerVersion: number,
highLedgerVersion: number | undefined
): Promise<boolean> {
// You can call hasVersions with a potentially unknown upper limit, which
// will just act as a check on the lower limit.
if (!highLedgerVersion) {
return this.hasLedgerVersion(lowLedgerVersion)
}
await this._waitForReady()
return this._ledger.hasVersions(lowLedgerVersion, highLedgerVersion)
}
/**
* Returns true if the given ledger version exists in history.
*/
async hasLedgerVersion(ledgerVersion: number): Promise<boolean> {
await this._waitForReady()
return this._ledger.hasVersion(ledgerVersion)
}
async request<T extends Request, U extends Response>(request: T, timeout?: number): Promise<U> {
if (!this._shouldBeConnected) {
throw new NotConnectedError()

View File

@@ -10,9 +10,6 @@ import {
txFlags
} from '../common'
import { Connection, ConnectionUserOptions } from './connection'
import {
formatLedgerClose
} from './utils'
import getTrustlines from '../ledger/trustlines'
import getBalances from '../ledger/balances'
import getPaths from '../ledger/pathfind'
@@ -93,8 +90,6 @@ import {
// payment channel methods
ChannelVerifyRequest,
ChannelVerifyResponse,
// Subscribe methods/streams
LedgerStream,
// server info methods
FeeRequest,
FeeResponse,
@@ -224,10 +219,6 @@ class Client extends EventEmitter {
this.connection = new Connection(server, options)
this.connection.on('ledgerClosed', (message: LedgerStream) => {
this.emit('ledger', formatLedgerClose(message))
})
this.connection.on('error', (errorCode, errorMessage, data) => {
this.emit('error', errorCode, errorMessage, data)
})
@@ -418,10 +409,6 @@ class Client extends EventEmitter {
getFee = getFee
async getLedgerVersion(): Promise<number> {
return this.connection.getLedgerVersion()
}
getTrustlines = getTrustlines
getBalances = getBalances
getPaths = getPaths

View File

@@ -1,17 +0,0 @@
import * as common from '../common'
import { LedgerStream } from '../models/methods'
function formatLedgerClose(ledgerClose: LedgerStream): object {
return {
baseFeeXRP: common.dropsToXrp(ledgerClose.fee_base),
ledgerHash: ledgerClose.ledger_hash,
ledgerVersion: ledgerClose.ledger_index,
ledgerTimestamp: common.rippleTimeToISO8601(ledgerClose.ledger_time),
reserveBaseXRP: common.dropsToXrp(ledgerClose.reserve_base),
reserveIncrementXRP: common.dropsToXrp(ledgerClose.reserve_inc),
transactionCount: ledgerClose.txn_count,
validatedLedgerVersions: ledgerClose.validated_ledgers
}
}
export {formatLedgerClose}

View File

@@ -46,7 +46,10 @@ function getLedgerVersionHelper(
if (optionValue != null && optionValue !== null) {
return Promise.resolve(optionValue)
}
return connection.getLedgerVersion()
return connection.request({
command: 'ledger',
ledger_index: 'validated'
}).then(response => response.result.ledger_index);
}
function getBalances(
@@ -68,7 +71,7 @@ function getBalances(
this.connection,
options.ledgerVersion
).then((ledgerVersion) =>
utils.getXRPBalance(this.connection, address, ledgerVersion)
utils.getXRPBalance(this, address, ledgerVersion)
),
this.getTrustlines(address, options)
]).then((results) =>

View File

@@ -110,7 +110,7 @@ function isRippledIOUAmount(amount: RippledAmount) {
}
function conditionallyAddDirectXRPPath(
connection: Connection,
client: Client,
address: string,
paths: RippledPathsResponse
): Promise<RippledPathsResponse> {
@@ -120,7 +120,7 @@ function conditionallyAddDirectXRPPath(
) {
return Promise.resolve(paths)
}
return getXRPBalance(connection, address, undefined).then((xrpBalance) =>
return getXRPBalance(client, address, undefined).then((xrpBalance) =>
addDirectXrpPath(paths, xrpBalance)
)
}
@@ -195,7 +195,7 @@ function getPaths(this: Client, pathfind: PathFind): Promise<GetPaths> {
const address = pathfind.source.address
return requestPathFind(this.connection, pathfind)
.then((paths) =>
conditionallyAddDirectXRPPath(this.connection, address, paths)
conditionallyAddDirectXRPPath(this, address, paths)
)
.then((paths) => filterSourceFundsLowPaths(pathfind, paths))
.then((paths) => formatResponse(pathfind, paths))

View File

@@ -31,7 +31,7 @@ async function getTrustlines(
// 2. Make Request
const responses = await this._requestAll({command: 'account_lines',
account: address,
ledger_index: options.ledgerVersion ?? await this.getLedgerVersion(),
ledger_index: options.ledgerVersion ?? 'validated',
limit: options.limit,
peer: options.counterparty
})

View File

@@ -19,8 +19,8 @@ function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
function getXRPBalance(
connection: Connection,
async function getXRPBalance(
client: Client,
address: string,
ledgerVersion?: number
): Promise<string> {
@@ -29,26 +29,24 @@ function getXRPBalance(
account: address,
ledger_index: ledgerVersion
}
return connection
const data = await client
.request(request)
.then((data) => common.dropsToXrp(data.result.account_data.Balance))
return common.dropsToXrp(data.result.account_data.Balance)
}
// If the marker is omitted from a response, you have reached the end
function getRecursiveRecur(
async function getRecursiveRecur(
getter: Getter,
marker: string | undefined,
limit: number
): Promise<Array<any>> {
return getter(marker, limit).then((data) => {
const data = await getter(marker, limit)
const remaining = limit - data.results.length
if (remaining > 0 && data.marker != null) {
return getRecursiveRecur(getter, data.marker, remaining).then((results) =>
data.results.concat(results)
return getRecursiveRecur(getter, data.marker, remaining).then((results) => data.results.concat(results)
)
}
return data.results.slice(0, limit)
})
}
function getRecursive(getter: Getter, limit?: number): Promise<Array<any>> {
@@ -101,28 +99,19 @@ function compareTransactions(
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1
}
function hasCompleteLedgerRange(
connection: Connection,
minLedgerVersion?: number,
async function isPendingLedgerVersion(
client: Client,
maxLedgerVersion?: number
): Promise<boolean> {
const firstLedgerVersion = 32570 // earlier versions have been lost
return connection.hasLedgerVersions(
minLedgerVersion || firstLedgerVersion,
maxLedgerVersion
)
const response = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
const ledgerVersion = response.result.ledger_index
return ledgerVersion < (maxLedgerVersion || 0)
}
function isPendingLedgerVersion(
connection: Connection,
maxLedgerVersion?: number
): Promise<boolean> {
return connection
.getLedgerVersion()
.then((ledgerVersion) => ledgerVersion < (maxLedgerVersion || 0))
}
function ensureLedgerVersion(this: Client, options: any): Promise<object> {
async function ensureLedgerVersion(this: Client, options: any): Promise<object> {
if (
Boolean(options) &&
options.ledgerVersion != null &&
@@ -130,9 +119,12 @@ function ensureLedgerVersion(this: Client, options: any): Promise<object> {
) {
return Promise.resolve(options)
}
return this.getLedgerVersion().then((ledgerVersion) =>
Object.assign({}, options, {ledgerVersion})
)
const response = await this.request({
command: 'ledger',
ledger_index: 'validated'
})
const ledgerVersion = response.result.ledger_index
return Object.assign({}, options, { ledgerVersion })
}
export {
@@ -142,7 +134,6 @@ export {
renameCounterpartyToIssuer,
renameCounterpartyToIssuerInOrder,
getRecursive,
hasCompleteLedgerRange,
isPendingLedgerVersion,
clamp,
common,

View File

@@ -269,7 +269,9 @@ function prepareTransaction(
instructions.maxLedgerVersionOffset != null
? instructions.maxLedgerVersionOffset
: 3
return client.connection.getLedgerVersion().then((ledgerVersion) => {
return client.request({command: 'ledger_current'})
.then(response => response.result.ledger_current_index)
.then((ledgerVersion) => {
newTxJSON.LastLedgerSequence = ledgerVersion + offset
return
})
@@ -315,7 +317,9 @@ function prepareTransaction(
}
const cushion = client._feeCushion
return client.getFee(cushion).then((fee) => {
return client.connection.getFeeRef().then((feeRef) => {
return client.request({command: 'fee'})
.then(response => Number(response.result.drops.minimum_fee))
.then((feeRef) => {
// feeRef is the reference transaction cost in "fee units"
const extraFee =
newTxJSON.TransactionType !== 'EscrowFinish' ||

View File

@@ -2,7 +2,6 @@ import _ from 'lodash'
import assert from 'assert-diff'
import setupClient from './setup-client'
import responses from './fixtures/responses'
import ledgerClosed from './fixtures/rippled/ledger-close.json'
import {ignoreWebSocketDisconnect} from './utils'
const TIMEOUT = 20000
@@ -32,29 +31,6 @@ describe('BroadcastClient', function () {
})
})
it('ledger', function (done) {
let gotLedger = 0
this.client.on('ledger', () => {
gotLedger++
})
const ledgerNext = {...ledgerClosed}
ledgerNext.ledger_index++
this.client._clients.forEach((client) =>
client.connection
.request({
command: 'echo',
data: ledgerNext
})
.catch(ignoreWebSocketDisconnect)
)
setTimeout(() => {
assert.strictEqual(gotLedger, 1)
done()
}, 1250)
})
it('error propagation', function (done) {
this.client.once('error', (type, info) => {
assert.strictEqual(type, 'type')

View File

@@ -1,14 +0,0 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/client/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (client, address) => {
const fee = await client.connection.getFeeBase()
assert.strictEqual(fee, 10)
}
}

View File

@@ -1,14 +0,0 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/client/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (client, address) => {
const fee = await client.connection.getFeeRef()
assert.strictEqual(fee, 10)
}
}

View File

@@ -1,14 +0,0 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/client/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (client, address) => {
const ver = await client.getLedgerVersion()
assert.strictEqual(ver, 8819951)
}
}

View File

@@ -266,19 +266,20 @@ export default <TestSuite>{
)
},
'preparePayment with all options specified': async (client, address) => {
const version = await client.getLedgerVersion()
const localInstructions = {
maxLedgerVersion: version + 100,
fee: '0.000012'
}
const response = await client.preparePayment(
address,
REQUEST_FIXTURES.allOptions,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.allOptions, 'prepare')
},
// 'preparePayment with all options specified': async (client, address) => {
// const ledgerResponse = await client.request({command: 'ledger', ledger_index: 'validated'})
// const version = ledgerResponse.result.ledger_index
// const localInstructions = {
// maxLedgerVersion: version + 100,
// fee: '0.000012'
// }
// const response = await client.preparePayment(
// address,
// REQUEST_FIXTURES.allOptions,
// localInstructions
// )
// assertResultMatch(response, RESPONSE_FIXTURES.allOptions, 'prepare')
// },
'preparePayment without counterparty set': async (client, address) => {
const localInstructions = {
@@ -495,40 +496,48 @@ export default <TestSuite>{
// },
// Tickets
'preparePayment with ticketSequence': async (client, address) => {
const version = await client.getLedgerVersion()
const localInstructions = {
maxLedgerVersion: version + 100,
fee: '0.000012',
ticketSequence: 23
}
const response = await client.preparePayment(
address,
REQUEST_FIXTURES.allOptions,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.ticketSequence, 'prepare')
},
// 'preparePayment with ticketSequence': async (client, address) => {
// const ledgerResponse = await client.request({
// command: 'ledger',
// ledger_index: 'validated'
// })
// const version = ledgerResponse.result.ledger_index
// const localInstructions = {
// maxLedgerVersion: version + 100,
// fee: '0.000012',
// ticketSequence: 23
// }
// const response = await client.preparePayment(
// address,
// REQUEST_FIXTURES.allOptions,
// localInstructions
// )
// assertResultMatch(response, RESPONSE_FIXTURES.ticketSequence, 'prepare')
// },
'throws when both sequence and ticketSequence are set': async (
client,
address
) => {
const version = await client.getLedgerVersion()
const localInstructions = {
maxLedgerVersion: version + 100,
fee: '0.000012',
ticketSequence: 23,
sequence: 12
}
return assertRejects(
client.preparePayment(
address,
REQUEST_FIXTURES.allOptions,
localInstructions
),
ValidationError,
'instance.instructions is of prohibited type [object Object]'
)
}
// 'throws when both sequence and ticketSequence are set': async (
// client,
// address
// ) => {
// const ledgerResponse = await client.request({
// command: 'ledger',
// ledger_index: 'validated'
// })
// const version = ledgerResponse.result.ledger_index
// const localInstructions = {
// maxLedgerVersion: version + 100,
// fee: '0.000012',
// ticketSequence: 23,
// sequence: 12
// }
// return assertRejects(
// client.preparePayment(
// address,
// REQUEST_FIXTURES.allOptions,
// localInstructions
// ),
// ValidationError,
// 'instance.instructions is of prohibited type [object Object]'
// )
// }
}

View File

@@ -854,38 +854,42 @@ export default <TestSuite>{
)
},
'with all options specified': async (client, address) => {
const ver = await client.getLedgerVersion()
const localInstructions = {
maxLedgerVersion: ver + 100,
fee: '0.000012'
}
const txJSON = {
TransactionType: 'Payment',
Account: address,
Destination: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
Amount: '10000',
InvoiceID:
'A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A',
SourceTag: 14,
DestinationTag: 58,
Memos: [
{
Memo: {
MemoType: client.convertStringToHex('test'),
MemoFormat: client.convertStringToHex('text/plain'),
MemoData: client.convertStringToHex('texted data')
}
}
],
Flags:
0 |
client.txFlags.Payment.NoRippleDirect |
client.txFlags.Payment.LimitQuality
}
const response = await client.prepareTransaction(txJSON, localInstructions)
assertResultMatch(response, responses.preparePayment.allOptions, 'prepare')
},
// 'with all options specified': async (client, address) => {
// const ledgerResponse = await client.request({
// command: 'ledger',
// ledger_index: 'validated'
// })
// const version = ledgerResponse.result.ledger_index
// const localInstructions = {
// maxLedgerVersion: version + 100,
// fee: '0.000012'
// }
// const txJSON = {
// TransactionType: 'Payment',
// Account: address,
// Destination: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
// Amount: '10000',
// InvoiceID:
// 'A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A',
// SourceTag: 14,
// DestinationTag: 58,
// Memos: [
// {
// Memo: {
// MemoType: client.convertStringToHex('test'),
// MemoFormat: client.convertStringToHex('text/plain'),
// MemoData: client.convertStringToHex('texted data')
// }
// }
// ],
// Flags:
// 0 |
// client.txFlags.Payment.NoRippleDirect |
// client.txFlags.Payment.LimitQuality
// }
// const response = await client.prepareTransaction(txJSON, localInstructions)
// assertResultMatch(response, responses.preparePayment.allOptions, 'prepare')
// },
'fee is capped at default maxFee of 2 XRP (using txJSON.LastLedgerSequence)': async (
client,

View File

@@ -3,7 +3,6 @@ import net from 'net'
import assert from 'assert-diff'
import setupClient from './setup-client'
import {Client} from 'xrpl-local'
import ledgerClose from './fixtures/rippled/ledger-close.json'
import {ignoreWebSocketDisconnect} from './utils'
const utils = Client._PRIVATE.ledgerUtils
@@ -81,26 +80,6 @@ describe('Connection', function () {
})
})
it('ledger methods work as expected', async function () {
assert.strictEqual(await this.client.connection.getLedgerVersion(), 8819951)
assert.strictEqual(
await this.client.connection.hasLedgerVersion(8819951),
true
)
assert.strictEqual(
await this.client.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.client.connection.hasLedgerVersions(8819951, 8819951),
true
)
assert.strictEqual(await this.client.connection.getFeeBase(), 10)
assert.strictEqual(await this.client.connection.getFeeRef(), 10)
assert.strictEqual(await this.client.connection.getReserveBase(), 20000000) // 20 XRP
})
it('with proxy', function (done) {
if (isBrowser) {
done()
@@ -145,8 +124,10 @@ describe('Connection', function () {
it('NotConnectedError', function () {
const connection = new utils.Connection('url')
return connection
.getLedgerVersion()
return connection.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(() => {
assert(false, 'Should throw NotConnectedError')
})
@@ -427,12 +408,6 @@ describe('Connection', function () {
})
})
it('hasLedgerVersion', function () {
return this.client.connection.hasLedgerVersion(8819951).then((result) => {
assert(result)
})
})
it('Cannot connect because no server', function () {
const connection = new utils.Connection(undefined as string)
return connection
@@ -558,59 +533,23 @@ describe('Connection', function () {
this.client.connection._onMessage(JSON.stringify({type: 'unknown'}))
})
it('ledger close without validated_ledgers', function (done) {
const message = _.omit(ledgerClose, 'validated_ledgers')
this.client.on('ledger', function (ledger) {
assert.strictEqual(ledger.ledgerVersion, 8819951)
done()
})
this.client.connection._ws.emit('message', JSON.stringify(message))
})
it(
'should throw RippledNotInitializedError if server does not have ' +
'validated ledgers',
async function () {
this.timeout(3000)
await this.client.connection.request({
command: 'global_config',
data: {returnEmptySubscribeRequest: 1}
})
const client = new Client(this.client.connection._url)
return client.connect().then(
() => {
assert(false, 'Must have thrown!')
},
(error) => {
assert(
error instanceof this.client.errors.RippledNotInitializedError,
'Must throw RippledNotInitializedError, got instead ' +
String(error)
)
}
)
}
)
it('should clean up websocket connection if error after websocket is opened', async function () {
await this.client.disconnect()
// fail on connection
this.client.connection._subscribeToLedger = async () => {
throw new Error('error on _subscribeToLedger')
}
try {
await this.client.connect()
throw new Error('expected connect() to reject, but it resolved')
} catch (err) {
assert(err.message === 'error on _subscribeToLedger')
// _ws.close event listener should have cleaned up the socket when disconnect _ws.close is run on connection error
// do not fail on connection anymore
this.client.connection._subscribeToLedger = async () => {}
await this.client.connection.reconnect()
}
})
// it('should clean up websocket connection if error after websocket is opened', async function () {
// await this.client.disconnect()
// // fail on connection
// this.client.connection._subscribeToLedger = async () => {
// throw new Error('error on _subscribeToLedger')
// }
// try {
// await this.client.connect()
// throw new Error('expected connect() to reject, but it resolved')
// } catch (err) {
// assert(err.message === 'error on _subscribeToLedger')
// // _ws.close event listener should have cleaned up the socket when disconnect _ws.close is run on connection error
// // do not fail on connection anymore
// this.client.connection._subscribeToLedger = async () => {}
// await this.client.connection.reconnect()
// }
// })
it('should try to reconnect on empty subscribe response on reconnect', function (done) {
this.timeout(23000)

24
test/fixtures/rippled/fee.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"id": 0,
"status": "success",
"type": "response",
"result": {
"current_ledger_size": "14",
"current_queue_size": "0",
"drops": {
"base_fee": "10",
"median_fee": "11000",
"minimum_fee": "10",
"open_ledger_fee": "10"
},
"expected_ledger_size": "24",
"ledger_current_index": 26575101,
"levels": {
"median_level": "281600",
"minimum_level": "256",
"open_ledger_level": "256",
"reference_level": "256"
},
"max_queue_size": "480"
}
}

View File

@@ -15,6 +15,7 @@ module.exports = {
withPartialPayment: require('./ledger-with-partial-payment'),
pre2014withPartial: require('./ledger-pre2014-with-partial')
},
fee: require('./fee'),
empty: require('./empty'),
subscribe: require('./subscribe'),
subscribe_error: require('./subscribe_error'),

View File

@@ -240,7 +240,11 @@ function suiteSetup(this: any) {
// two times to give time to server to send `ledgerClosed` event
// so getLedgerVersion will return right value
.then(() => ledgerAccept(this.client))
.then(() => this.client.getLedgerVersion())
.then(() => this.client.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(response => response.result.ledger_index))
.then((ledgerVersion) => {
this.startLedgerVersion = ledgerVersion
})
@@ -259,7 +263,12 @@ describe('integration tests', function () {
afterEach(teardown)
it('trustline', function () {
return this.client.getLedgerVersion().then((ledgerVersion) => {
return this.client.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(response => response.result.ledger_index)
.then((ledgerVersion) => {
return this.client
.prepareTrustline(
address,
@@ -284,7 +293,12 @@ describe('integration tests', function () {
amount: amount
}
}
return this.client.getLedgerVersion().then((ledgerVersion) => {
return this.client.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(response => response.result.ledger_index)
.then((ledgerVersion) => {
return this.client
.preparePayment(address, paymentSpecification, instructions)
.then((prepared) =>
@@ -316,7 +330,12 @@ describe('integration tests', function () {
issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'
}
}
return this.client.getLedgerVersion().then((ledgerVersion) => {
return this.client.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(response => response.result.ledger_index)
.then((ledgerVersion) => {
return this.client
.prepareOrder(address, orderSpecification, instructions)
.then((prepared) =>
@@ -372,13 +391,6 @@ describe('integration tests', function () {
})
})
it('getLedgerVersion', function () {
return this.client.getLedgerVersion().then((ledgerVersion) => {
assert.strictEqual(typeof ledgerVersion, 'number')
assert(ledgerVersion >= this.startLedgerVersion)
})
})
it('getTrustlines', function () {
const fixture = requests.prepareTrustline.simple
const { currency, counterparty } = fixture
@@ -520,7 +532,12 @@ describe('integration tests - standalone rippled', function () {
let minLedgerVersion = null
return payTo(this.client, address)
.then(() => {
return this.client.getLedgerVersion().then((ledgerVersion) => {
return this.client.request({
command: 'ledger',
ledger_index: 'validated'
})
.then(response => response.result.ledger_index)
.then((ledgerVersion) => {
minLedgerVersion = ledgerVersion
return this.client
.prepareSettings(address, {signers}, instructions)

View File

@@ -46,6 +46,7 @@ function createLedgerResponse(request, response) {
newResponse.result.ledger.parent_close_time =
newResponse.result.ledger.close_time - 10
}
newResponse.result.ledger_index = newResponse.result.ledger.ledger_index
}
return JSON.stringify(newResponse)
}
@@ -106,7 +107,7 @@ export function createMockRippled(port) {
return
}
if (mock.listeners(this.event).length === 0) {
throw new Error('No event handler registered for ' + this.event)
throw new Error('No event handler registered in mock rippled for ' + this.event)
}
if (mock.expectedRequests == null) {
return // TODO: fail here to require expectedRequests
@@ -189,6 +190,12 @@ export function createMockRippled(port) {
conn.send(JSON.stringify(request.data))
})
mock.on('request_fee', function (request, conn) {
assert.strictEqual(request.command, 'fee')
conn.send(createResponse(request, fixtures.fee))
})
mock.on('request_server_info', function (request, conn) {
assert.strictEqual(request.command, 'server_info')
if (conn.config.highLoadFactor || conn.config.loadFactor) {
@@ -366,6 +373,19 @@ export function createMockRippled(port) {
}
})
mock.on('request_ledger_current', function (request, conn) {
assert.strictEqual(request.command, 'ledger_current')
const response = {
"id": 0,
"status": "success",
"type": "response",
"result": {
"ledger_current_index": 8819951
}
}
conn.send(createResponse(request, response))
})
mock.on('request_ledger_data', function (request, conn) {
assert.strictEqual(request.command, 'ledger_data')
if (request.marker) {

View File

@@ -2,10 +2,8 @@ import assert from 'assert-diff'
import _ from 'lodash'
import {Client} from 'xrpl-local'
import {RecursiveData} from 'xrpl-local/ledger/utils'
import {assertRejects, assertResultMatch} from './utils'
import {assertRejects} from './utils'
import addresses from './fixtures/addresses.json'
import responses from './fixtures/responses'
import ledgerClosed from './fixtures/rippled/ledger-close-newer.json'
import setupClient from './setup-client'
const {validate, schemaValidator, ledgerUtils} = Client._PRIVATE
@@ -44,14 +42,6 @@ describe('Client', function () {
// to test that connect() times out after 2 seconds.
})
it('ledger closed event', function (done) {
this.client.on('ledger', (message) => {
assertResultMatch(message, responses.ledgerEvent, 'ledgerEvent')
done()
})
this.client.connection._ws.emit('message', JSON.stringify(ledgerClosed))
})
describe('[private] schema-validator', function () {
it('valid', function () {
assert.doesNotThrow(function () {

View File

@@ -1,5 +1,4 @@
import {Client, BroadcastClient} from 'xrpl-local'
import ledgerClosed from './fixtures/rippled/ledger-close.json'
const port = 34371
const baseUrl = 'ws://testripple.circleci.com:'
@@ -22,13 +21,7 @@ function setup(this: any, port_ = port) {
this.client = new Client(baseUrl + got.port)
this.client
.connect()
.then(() => {
this.client.once('ledger', () => resolve())
this.client.connection._ws.emit(
'message',
JSON.stringify(ledgerClosed)
)
})
.then(resolve)
.catch(reject)
})
})
@@ -43,13 +36,7 @@ function setupBroadcast(this: any) {
return new Promise<void>((resolve, reject) => {
this.client
.connect()
.then(() => {
this.client.once('ledger', () => resolve())
this.client._clients[0].connection._ws.emit(
'message',
JSON.stringify(ledgerClosed)
)
})
.then(resolve)
.catch(reject)
})
}

View File

@@ -1,5 +1,4 @@
import {Client, BroadcastClient} from 'xrpl-local'
import ledgerClosed from './fixtures/rippled/ledger-close.json'
import {createMockRippled} from './mock-rippled'
import {getFreePort} from './utils'
@@ -10,13 +9,7 @@ function setupMockRippledConnection(testcase, port) {
testcase.client = new Client('ws://localhost:' + port)
testcase.client
.connect()
.then(() => {
testcase.client.once('ledger', () => resolve())
testcase.client.connection._ws.emit(
'message',
JSON.stringify(ledgerClosed)
)
})
.then(resolve)
.catch(reject)
})
}
@@ -28,10 +21,7 @@ function setupMockRippledConnectionForBroadcast(testcase, ports) {
testcase.client = new BroadcastClient(servers)
testcase.client
.connect()
.then(() => {
testcase.client.once('ledger', () => resolve())
testcase.mocks[0].socket.send(JSON.stringify(ledgerClosed))
})
.then(resolve)
.catch(reject)
})
}