evernode-js-client 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -6,21 +6,12 @@
6
6
  ],
7
7
  "homepage": "https://github.com/HotPocketDev/evernode-js-client",
8
8
  "license": "MIT",
9
- "version": "0.5.0",
10
- "scripts": {
11
- "lint": "./node_modules/.bin/eslint src/**/*.js",
12
- "build": "npm run lint && ncc build src/index.js -e elliptic -e xrpl -e ripple-address-codec -e ripple-keypairs -o dist/",
13
- "bundle": "npm run build && ./clean-pkg.sh",
14
- "publish": "npm run bundle && npm publish ./dist"
15
- },
9
+ "version": "0.5.2",
16
10
  "dependencies": {
17
11
  "elliptic": "6.5.4",
18
12
  "ripple-address-codec": "4.2.0",
19
13
  "ripple-keypairs": "1.1.0",
20
14
  "xrpl": "2.2.1",
21
15
  "xrpl-binary-codec": "1.4.2"
22
- },
23
- "devDependencies": {
24
- "eslint": "8.3.0"
25
16
  }
26
17
  }
package/.eslintrc.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "commonjs": true,
5
- "es2021": true
6
- },
7
- "extends": "eslint:recommended",
8
- "parserOptions": {
9
- "ecmaVersion": 13
10
- },
11
- "rules": {
12
- "no-async-promise-executor": "off"
13
- }
14
- }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 HotPocketDev
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/clean-pkg.sh DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/bash
2
- # Create a clean dist/package.json for publishing.
3
- node -p "const pkg=require('./package.json'); delete pkg.scripts; delete pkg.devDependencies; JSON.stringify(pkg, null, 4)" \
4
- > dist/package.json
package/npm-readme.md DELETED
@@ -1,4 +0,0 @@
1
- # Evernode library for nodejs and browser.
2
- Evernode library contains helper methods to interact with Evernode ecosystem.
3
-
4
- - [Source code](https://github.com/HotPocketDev/evernode-js-client)
@@ -1,10 +0,0 @@
1
- #!/bin/bash
2
- # This script removes previous npm package versions. (need to be logged into npm first)
3
-
4
- for i in {0..36}
5
- do
6
- pkg="evernode-js-client@0.4.$i"
7
- echo Unpublishing $pkg
8
- npm unpublish $pkg
9
- sleep 2
10
- done
@@ -1,567 +0,0 @@
1
- const codec = require('ripple-address-codec');
2
- const { Buffer } = require('buffer');
3
- const { XrplApi } = require('../xrpl-api');
4
- const { XrplAccount } = require('../xrpl-account');
5
- const { XrplApiEvents, XrplConstants } = require('../xrpl-common');
6
- const { EvernodeEvents, MemoTypes, MemoFormats, EvernodeConstants, HookStateKeys } = require('../evernode-common');
7
- const { DefaultValues } = require('../defaults');
8
- const { EncryptionHelper } = require('../encryption-helper');
9
- const { EventEmitter } = require('../event-emitter');
10
- const { UtilHelpers } = require('../util-helpers');
11
- const { FirestoreHandler } = require('../firestore/firestore-handler');
12
- const { StateHelpers } = require('../state-helpers');
13
-
14
- class BaseEvernodeClient {
15
-
16
- #watchEvents;
17
- #autoSubscribe;
18
- #ownsXrplApi = false;
19
- #firestoreHandler;
20
-
21
- constructor(xrpAddress, xrpSecret, watchEvents, autoSubscribe = false, options = {}) {
22
-
23
- this.connected = false;
24
- this.registryAddress = options.registryAddress || DefaultValues.registryAddress;
25
-
26
- this.xrplApi = options.xrplApi || DefaultValues.xrplApi || new XrplApi(options.rippledServer);
27
- if (!options.xrplApi && !DefaultValues.xrplApi)
28
- this.#ownsXrplApi = true;
29
-
30
- this.xrplAcc = new XrplAccount(xrpAddress, xrpSecret, { xrplApi: this.xrplApi });
31
- this.accKeyPair = xrpSecret && this.xrplAcc.deriveKeypair();
32
- this.#watchEvents = watchEvents;
33
- this.#autoSubscribe = autoSubscribe;
34
- this.events = new EventEmitter();
35
- this.#firestoreHandler = new FirestoreHandler()
36
-
37
- this.xrplAcc.on(XrplApiEvents.PAYMENT, (tx, error) => this.#handleEvernodeEvent(tx, error));
38
- this.xrplAcc.on(XrplApiEvents.NFT_OFFER_CREATE, (tx, error) => this.#handleEvernodeEvent(tx, error));
39
- this.xrplAcc.on(XrplApiEvents.NFT_OFFER_ACCEPT, (tx, error) => this.#handleEvernodeEvent(tx, error));
40
-
41
- }
42
-
43
- /**
44
- * Listens to the subscribed events. This will listen for the event without detaching the handler until it's 'off'.
45
- * @param {string} event Event name.
46
- * @param {function(event)} handler Callback function to handle the event.
47
- */
48
- on(event, handler) {
49
- this.events.on(event, handler);
50
- }
51
-
52
- /**
53
- * Listens to the subscribed events. This will listen only once and detach the handler.
54
- * @param {string} event Event name.
55
- * @param {function(event)} handler Callback function to handle the event.
56
- */
57
- once(event, handler) {
58
- this.events.once(event, handler);
59
- }
60
-
61
- /**
62
- * Detach the listener event.
63
- * @param {string} event Event name.
64
- * @param {function(event)} handler (optional) Can be sent if a specific handler need to be detached. All the handlers will be detached if not specified.
65
- */
66
- off(event, handler = null) {
67
- this.events.off(event, handler);
68
- }
69
-
70
- /**
71
- * Connects the client to xrpl server and do the config loading and subscriptions. 'subscribe' is called inside this.
72
- * @returns boolean value, 'true' if success.
73
- */
74
- async connect() {
75
- if (this.connected)
76
- return true;
77
-
78
- await this.xrplApi.connect();
79
-
80
- // Invoking the info command to check the account existence. This is important to
81
- // identify a network reset from XRPL.
82
- await this.xrplAcc.getInfo();
83
-
84
- this.config = await this.#getEvernodeConfig();
85
- this.connected = true;
86
-
87
- if (this.#autoSubscribe)
88
- await this.subscribe();
89
-
90
- return true;
91
- }
92
-
93
- /**
94
- * Disconnects the client to xrpl server and do the un-subscriptions. 'unsubscribe' is called inside this.
95
- */
96
- async disconnect() {
97
- await this.unsubscribe();
98
-
99
- if (this.#ownsXrplApi)
100
- await this.xrplApi.disconnect();
101
- }
102
-
103
- /**
104
- * Subscribes to the registry client events.
105
- */
106
- async subscribe() {
107
- await this.xrplAcc.subscribe();
108
- }
109
-
110
- /**
111
- * Unsubscribes from the registry client events.
112
- */
113
- async unsubscribe() {
114
- await this.xrplAcc.unsubscribe();
115
- }
116
-
117
- /**
118
- * Get the EVR balance in the registry account.
119
- * @returns The available EVR amount as a 'string'.
120
- */
121
- async getEVRBalance() {
122
- const lines = await this.xrplAcc.getTrustLines(EvernodeConstants.EVR, this.config.evrIssuerAddress);
123
- if (lines.length > 0)
124
- return lines[0].balance;
125
- else
126
- return '0';
127
- }
128
-
129
- /**
130
- * Get all XRPL hook states in the registry account.
131
- * @returns The list of hook states including Evernode configuration and hosts.
132
- */
133
- async getHookStates() {
134
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
135
- const configs = await regAcc.getNamespaceEntries(EvernodeConstants.HOOK_NAMESPACE);
136
-
137
- if (configs)
138
- return configs.filter(c => c.LedgerEntryType === 'HookState').map(c => { return { key: c.HookStateKey, data: c.HookStateData } });
139
- return [];
140
- }
141
-
142
- /**
143
- * Get the moment from the given XRP ledger index. (1 Moment - 1190 XRP ledgers).
144
- * @param {number} ledgerIndex [Optional] Ledger index to get the moment value.
145
- * @returns The moment of the given XPR ledger index as 'number'. Returns current moment if XRP ledger index is not given.
146
- */
147
- async getMoment(ledgerIndex = null) {
148
- const lv = ledgerIndex || this.xrplApi.ledgerIndex;
149
- const m = Math.floor((lv - this.config.momentBaseIdx) / this.config.momentSize);
150
-
151
- await Promise.resolve(); // Awaiter placeholder for future async requirements.
152
- return m;
153
- }
154
-
155
- /**
156
- * Get start XRP ledger index of the moment (of the given XRPL index).
157
- * @param {number} ledgerIndex [Optional] Ledger index to get the moment value.
158
- * @returns The XRP ledger index of the moment (of the given XRPL index) as a 'number'. Returns the current moment's start XRP ledger index if ledger index parameter is not given.
159
- */
160
- async getMomentStartIndex(ledgerIndex = null) {
161
- const lv = ledgerIndex || this.xrplApi.ledgerIndex;
162
- const m = Math.floor((lv - this.config.momentBaseIdx) / this.config.momentSize);
163
-
164
- await Promise.resolve(); // Awaiter placeholder for future async requirements.
165
- return this.config.momentBaseIdx + (m * this.config.momentSize);
166
- }
167
-
168
- /**
169
- * Get Evernode configuration
170
- * @returns An object with all the configuration and their values.
171
- */
172
- async #getEvernodeConfig() {
173
- let states = await this.getHookStates();
174
- const configStateKeys = {
175
- evrIssuerAddress: HookStateKeys.EVR_ISSUER_ADDR,
176
- foundationAddress: HookStateKeys.FOUNDATION_ADDR,
177
- hostRegFee: HookStateKeys.HOST_REG_FEE,
178
- momentSize: HookStateKeys.MOMENT_SIZE,
179
- hostHeartbeatFreq: HookStateKeys.HOST_HEARTBEAT_FREQ,
180
- momentBaseIdx: HookStateKeys.MOMENT_BASE_IDX,
181
- purchaserTargetPrice: HookStateKeys.PURCHASER_TARGET_PRICE,
182
- leaseAcquireWindow: HookStateKeys.LEASE_ACQUIRE_WINDOW,
183
- rewardInfo: HookStateKeys.REWARD_INFO,
184
- rewardConfiguaration: HookStateKeys.REWARD_CONFIGURATION,
185
- hostCount: HookStateKeys.HOST_COUNT
186
- }
187
- let config = {};
188
- for (const [key, value] of Object.entries(configStateKeys)) {
189
- const stateKey = Buffer.from(value, 'hex');
190
- const stateData = Buffer.from(UtilHelpers.getStateData(states, value), 'hex');
191
- const decoded = StateHelpers.decodeStateData(stateKey, stateData);
192
- config[key] = decoded.value;
193
- }
194
- return config;
195
- }
196
-
197
- /**
198
- * Loads the configs from XRPL hook and updates the in-memory config.
199
- */
200
- async refreshConfig() {
201
- this.config = await this.#getEvernodeConfig();
202
- }
203
-
204
- /**
205
- * Extracts transaction info and emits the Evernode event.
206
- * @param {object} tx XRPL transaction to be handled.
207
- * @param {any} error Error if there's any.
208
- */
209
- async #handleEvernodeEvent(tx, error) {
210
- if (error)
211
- console.error(error);
212
- else if (!tx)
213
- console.log('handleEvernodeEvent: Invalid transaction.');
214
- else {
215
- const ev = await this.extractEvernodeEvent(tx);
216
- if (ev && this.#watchEvents.find(e => e === ev.name))
217
- this.events.emit(ev.name, ev.data);
218
- }
219
- }
220
-
221
- /**
222
- * Extracts the transaction info from a given transaction.
223
- * @param {object} tx Transaction to be deserialized and extracted.
224
- * @returns The event object in the format {name: '', data: {}}. Returns null if not handled. Note: You need to deserialize memos before passing the transaction to this function.
225
- */
226
- async extractEvernodeEvent(tx) {
227
- if (tx.TransactionType === 'NFTokenAcceptOffer' && tx.NFTokenSellOffer && tx.Memos.length >= 1 &&
228
- tx.Memos[0].type === MemoTypes.ACQUIRE_LEASE && tx.Memos[0].format === MemoFormats.BASE64 && tx.Memos[0].data) {
229
-
230
- // If our account is the destination host account, then decrypt the payload.
231
- let payload = tx.Memos[0].data;
232
- if (tx.Destination === this.xrplAcc.address) {
233
- const decrypted = this.accKeyPair && await EncryptionHelper.decrypt(this.accKeyPair.privateKey, payload);
234
- if (decrypted)
235
- payload = decrypted;
236
- else
237
- console.log('Failed to decrypt acquire data.');
238
- }
239
-
240
- return {
241
- name: EvernodeEvents.AcquireLease,
242
- data: {
243
- transaction: tx,
244
- host: tx.Destination,
245
- nfTokenId: tx.NFTokenSellOffer?.NFTokenID,
246
- leaseAmount: tx.NFTokenSellOffer?.Amount?.value,
247
- acquireRefId: tx.hash,
248
- tenant: tx.Account,
249
- payload: payload
250
- }
251
- }
252
- }
253
-
254
- else if (tx.TransactionType === 'NFTokenAcceptOffer' && tx.NFTokenBuyOffer && tx.Memos.length >= 1 &&
255
- tx.Memos[0].type === MemoTypes.HOST_POST_DEREG && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
256
- return {
257
- name: EvernodeEvents.HostPostDeregistered,
258
- data: {
259
- transaction: tx,
260
- nfTokenId: tx.NFTokenBuyOffer.NFTokenID,
261
- flags: tx.Flags,
262
- hash: tx.hash
263
- }
264
- }
265
- }
266
-
267
- else if (tx.Memos.length >= 2 &&
268
- tx.Memos[0].type === MemoTypes.ACQUIRE_SUCCESS && tx.Memos[0].data &&
269
- tx.Memos[1].type === MemoTypes.ACQUIRE_REF && tx.Memos[1].data) {
270
-
271
- let payload = tx.Memos[0].data;
272
- const acquireRefId = tx.Memos[1].data;
273
-
274
- // If our account is the destination user account, then decrypt the payload.
275
- if (tx.Memos[0].format === MemoFormats.BASE64 && tx.Destination === this.xrplAcc.address) {
276
- const decrypted = this.accKeyPair && await EncryptionHelper.decrypt(this.accKeyPair.privateKey, payload);
277
- if (decrypted)
278
- payload = decrypted;
279
- else
280
- console.log('Failed to decrypt instance data.');
281
- }
282
-
283
- return {
284
- name: EvernodeEvents.AcquireSuccess,
285
- data: {
286
- transaction: tx,
287
- acquireRefId: acquireRefId,
288
- payload: payload
289
- }
290
- }
291
-
292
- }
293
- else if (tx.Memos.length >= 2 &&
294
- tx.Memos[0].type === MemoTypes.ACQUIRE_ERROR && tx.Memos[0].data &&
295
- tx.Memos[1].type === MemoTypes.ACQUIRE_REF && tx.Memos[1].data) {
296
-
297
- let error = tx.Memos[0].data;
298
- const acquireRefId = tx.Memos[1].data;
299
-
300
- if (tx.Memos[0].format === MemoFormats.JSON)
301
- error = JSON.parse(error).reason;
302
-
303
- return {
304
- name: EvernodeEvents.AcquireError,
305
- data: {
306
- transaction: tx,
307
- acquireRefId: acquireRefId,
308
- reason: error
309
- }
310
- }
311
- }
312
- else if (tx.Memos.length >= 1 &&
313
- tx.Memos[0].type === MemoTypes.HOST_REG && tx.Memos[0].format === MemoFormats.TEXT && tx.Memos[0].data) {
314
-
315
- const parts = tx.Memos[0].data.split(';');
316
- return {
317
- name: EvernodeEvents.HostRegistered,
318
- data: {
319
- transaction: tx,
320
- host: tx.Account,
321
- token: parts[0],
322
- instanceSize: parts[1],
323
- location: parts[2]
324
- }
325
- }
326
- }
327
- else if (tx.Memos.length >= 1 && tx.Memos[0].type === MemoTypes.HOST_DEREG) {
328
- return {
329
- name: EvernodeEvents.HostDeregistered,
330
- data: {
331
- transaction: tx,
332
- host: tx.Account
333
- }
334
- }
335
- }
336
- else if (tx.Memos.length >= 1 &&
337
- tx.Memos[0].type === MemoTypes.HEARTBEAT) {
338
-
339
- return {
340
- name: EvernodeEvents.Heartbeat,
341
- data: {
342
- transaction: tx,
343
- host: tx.Account
344
- }
345
- }
346
- }
347
- else if (tx.Memos.length >= 1 &&
348
- tx.Memos[0].type === MemoTypes.EXTEND_LEASE && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
349
-
350
- let nfTokenId = tx.Memos[0].data;
351
-
352
- return {
353
- name: EvernodeEvents.ExtendLease,
354
- data: {
355
- transaction: tx,
356
- extendRefId: tx.hash,
357
- tenant: tx.Account,
358
- currency: tx.Amount.currency,
359
- payment: parseFloat(tx.Amount.value),
360
- nfTokenId: nfTokenId
361
- }
362
- }
363
- }
364
- else if (tx.Memos.length >= 2 &&
365
- tx.Memos[0].type === MemoTypes.EXTEND_SUCCESS && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data &&
366
- tx.Memos[1].type === MemoTypes.EXTEND_REF && tx.Memos[1].format === MemoFormats.HEX && tx.Memos[1].data) {
367
-
368
- const extendResBuf = Buffer.from(tx.Memos[0].data, 'hex');
369
- const extendRefId = tx.Memos[1].data;
370
-
371
- return {
372
- name: EvernodeEvents.ExtendSuccess,
373
- data: {
374
- transaction: tx,
375
- extendRefId: extendRefId,
376
- expiryMoment: extendResBuf.readUInt32BE()
377
- }
378
- }
379
-
380
- }
381
- else if (tx.Memos.length >= 2 &&
382
- tx.Memos[0].type === MemoTypes.EXTEND_ERROR && tx.Memos[0].data &&
383
- tx.Memos[1].type === MemoTypes.EXTEND_REF && tx.Memos[1].data) {
384
-
385
- let error = tx.Memos[0].data;
386
- const extendRefId = tx.Memos[1].data;
387
-
388
- if (tx.Memos[0].format === MemoFormats.JSON)
389
- error = JSON.parse(error).reason;
390
-
391
- return {
392
- name: EvernodeEvents.ExtendError,
393
- data: {
394
- transaction: tx,
395
- extendRefId: extendRefId,
396
- reason: error
397
- }
398
- }
399
- }
400
- else if (tx.Memos.length >= 1 &&
401
- tx.Memos[0].type === MemoTypes.REGISTRY_INIT && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
402
-
403
- return {
404
- name: EvernodeEvents.RegistryInitialized,
405
- data: {
406
- transaction: tx
407
- }
408
- }
409
- }
410
- else if (tx.Memos.length >= 1 &&
411
- tx.Memos[0].type === MemoTypes.HOST_UPDATE_INFO && tx.Memos[0].format === MemoFormats.TEXT && tx.Memos[0].data) {
412
-
413
- const specs = tx.Memos[0].data.split(';');
414
-
415
- return {
416
- name: EvernodeEvents.HostRegUpdated,
417
- data: {
418
- transaction: tx,
419
- host: tx.Account,
420
- version: specs[specs.length - 1],
421
- specs: specs,
422
- }
423
- }
424
- }
425
- else if (tx.Memos.length >= 1 &&
426
- tx.Memos[0].type === MemoTypes.DEAD_HOST_PRUNE && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
427
-
428
- const addrsBuf = Buffer.from(tx.Memos[0].data, 'hex');
429
-
430
- return {
431
- name: EvernodeEvents.DeadHostPrune,
432
- data: {
433
- transaction: tx,
434
- host: codec.encodeAccountID(addrsBuf)
435
- }
436
- }
437
- }
438
-
439
- return null;
440
- }
441
-
442
- /**
443
- * Get the registered host information.
444
- * @param {string} hostAddress [Optional] Address of the host.
445
- * @returns The registered host information object. Returns null is not registered.
446
- */
447
- async getHostInfo(hostAddress = this.xrplAcc.address) {
448
- try {
449
- const addrStateKey = StateHelpers.generateHostAddrStateKey(hostAddress);
450
- const addrStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, addrStateKey);
451
- const addrLedgerEntry = await this.xrplApi.getLedgerEntry(addrStateIndex);
452
- const addrStateData = addrLedgerEntry?.HookStateData;
453
- if (addrStateData) {
454
- const addrStateDecoded = StateHelpers.decodeHostAddressState(Buffer.from(addrStateKey, 'hex'), Buffer.from(addrStateData, 'hex'));
455
- const curMomentStartIdx = await this.getMomentStartIndex();
456
- addrStateDecoded.active = (addrStateDecoded.lastHeartbeatLedger > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
457
- (addrStateDecoded.lastHeartbeatLedger >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
458
- (addrStateDecoded.lastHeartbeatLedger > 0))
459
-
460
- const nftIdStatekey = StateHelpers.generateTokenIdStateKey(addrStateDecoded.nfTokenId);
461
- const nftIdStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, nftIdStatekey);
462
- const nftIdLedgerEntry = await this.xrplApi.getLedgerEntry(nftIdStateIndex);
463
-
464
- const nftIdStateData = nftIdLedgerEntry?.HookStateData;
465
- if (nftIdStateData) {
466
- const nftIdStateDecoded = StateHelpers.decodeTokenIdState(Buffer.from(nftIdStateData, 'hex'));
467
- return { ...addrStateDecoded, ...nftIdStateDecoded };
468
- }
469
- }
470
- }
471
- catch (e) {
472
- // If the exception is entryNotFound from Rippled there's no entry for the host, So return null.
473
- if (e?.data?.error !== 'entryNotFound')
474
- throw e;
475
- }
476
-
477
- return null;
478
- }
479
-
480
- /**
481
- * Get all the hosts registered in Evernode. The result's are paginated. Default page size is 20. Note: Specifying both filter and pagination does not supported.
482
- * @param {object} filters [Optional] Filter criteria to filter the hosts. The filter key can be a either property of the host.
483
- * @param {number} pageSize [Optional] Page size for the results.
484
- * @param {string} nextPageToken [Optional] Next page's token, If received by the previous result set.
485
- * @returns The list of active hosts. The response will be in '{data: [], nextPageToken: ''}' only if there are more pages. Otherwise the response will only contain the host list.
486
- */
487
- async getHosts(filters = null, pageSize = null, nextPageToken = null) {
488
- const hosts = await this.#firestoreHandler.getHosts(filters, pageSize, nextPageToken);
489
- const curMomentStartIdx = await this.getMomentStartIndex();
490
- // Populate the host active status.
491
- (hosts.nextPageToken ? hosts.data : hosts).forEach(h => {
492
- h.active = (h.lastHeartbeatLedger > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
493
- (h.lastHeartbeatLedger >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
494
- (h.lastHeartbeatLedger > 0))
495
- });
496
- return hosts;
497
- }
498
-
499
- /**
500
- * Get all Evernode configuration without paginating.
501
- * @returns The list of configuration.
502
- */
503
- async getAllConfigs() {
504
- let fullConfigList = [];
505
- const configs = await this.#firestoreHandler.getConfigs();
506
- if (configs.nextPageToken) {
507
- let currentPageToken = configs.nextPageToken;
508
- let nextConfigs = null;
509
- fullConfigList = fullConfigList.concat(configs.data);
510
- while (currentPageToken) {
511
- nextConfigs = await this.#firestoreHandler.getConfigs(null, 50, currentPageToken);
512
- fullConfigList = fullConfigList.concat(nextConfigs.nextPageToken ? nextConfigs.data : nextConfigs);
513
- currentPageToken = nextConfigs.nextPageToken;
514
- }
515
- } else {
516
- fullConfigList = fullConfigList.concat(configs);
517
- }
518
-
519
- return fullConfigList;
520
- }
521
-
522
- /**
523
- * Get all the hosts without paginating.
524
- * @returns The list of hosts.
525
- */
526
- async getAllHosts() {
527
- let fullHostList = [];
528
- const hosts = await this.#firestoreHandler.getHosts();
529
- if (hosts.nextPageToken) {
530
- let currentPageToken = hosts.nextPageToken;
531
- let nextHosts = null;
532
- fullHostList = fullHostList.concat(hosts.data);
533
- while (currentPageToken) {
534
- nextHosts = await this.#firestoreHandler.getHosts(null, 50, currentPageToken);
535
- fullHostList = fullHostList.concat(nextHosts.nextPageToken ? nextHosts.data : nextHosts);
536
- currentPageToken = nextHosts.nextPageToken;
537
- }
538
- } else {
539
- fullHostList = fullHostList.concat(hosts);
540
- }
541
-
542
- return fullHostList;
543
- }
544
-
545
- /**
546
- * Remove a host which is inactive for a long period. The inactivity is checked by Evernode it self and only pruned if inactive thresholds are met.
547
- * @param {string} hostAddress XRPL address of the host to be pruned.
548
- */
549
- async pruneDeadHost(hostAddress) {
550
- if (this.xrplAcc.address === this.registryAddress)
551
- throw 'Invalid function call';
552
-
553
- let memoData = Buffer.allocUnsafe(20);
554
- codec.decodeAccountID(hostAddress).copy(memoData);
555
-
556
- await this.xrplAcc.makePayment(this.registryAddress,
557
- XrplConstants.MIN_XRP_AMOUNT,
558
- XrplConstants.XRP,
559
- null,
560
- [{ type: MemoTypes.DEAD_HOST_PRUNE, format: MemoFormats.HEX, data: memoData.toString('hex') }]);
561
-
562
- }
563
- }
564
-
565
- module.exports = {
566
- BaseEvernodeClient
567
- }