evernode-js-client 0.4.46 → 0.4.50

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. package/index.js +154 -26
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -11708,7 +11708,7 @@ const codec = __nccwpck_require__(597);
11708
11708
  const { Buffer } = __nccwpck_require__(4300);
11709
11709
  const { XrplApi } = __nccwpck_require__(1850);
11710
11710
  const { XrplAccount } = __nccwpck_require__(9329);
11711
- const { XrplApiEvents } = __nccwpck_require__(3307);
11711
+ const { XrplApiEvents, XrplConstants } = __nccwpck_require__(3307);
11712
11712
  const { EvernodeEvents, MemoTypes, MemoFormats, EvernodeConstants, HookStateKeys } = __nccwpck_require__(9849);
11713
11713
  const { DefaultValues } = __nccwpck_require__(8262);
11714
11714
  const { EncryptionHelper } = __nccwpck_require__(4832);
@@ -12055,23 +12055,52 @@ class BaseEvernodeClient {
12055
12055
  }
12056
12056
  }
12057
12057
  }
12058
+ else if (tx.Memos.length >= 1 &&
12059
+ tx.Memos[0].type === MemoTypes.DEAD_HOST_PRUNE && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
12060
+
12061
+ const addrsBuf = Buffer.from(tx.Memos[0].data, 'hex');
12062
+
12063
+ return {
12064
+ name: EvernodeEvents.DeadHostPrune,
12065
+ data: {
12066
+ transaction: tx,
12067
+ host: codec.encodeAccountID(addrsBuf)
12068
+ }
12069
+ }
12070
+ }
12058
12071
 
12059
12072
  return null;
12060
12073
  }
12061
12074
 
12062
12075
  // To get Host details from Hook States.
12063
12076
  async getHostInfo(hostAddress = this.xrplAcc.address) {
12064
- const hostAddrStatekey = StateHelpers.generateHostAddrStateKey(hostAddress);
12065
- const stateLedgerIndex = StateHelpers.getHookStateIndex(this.registryAddress, hostAddrStatekey);
12066
- const ledgerEntry = await this.xrplApi.getLedgerEntry(stateLedgerIndex);
12067
- const curMomentStartIdx = await this.getMomentStartIndex();
12068
- if (ledgerEntry?.HookStateData) {
12069
- const hostAddrStateData = ledgerEntry.HookStateData;
12070
- const hostInfo = StateHelpers.decodeHostAddressState(Buffer.from(hostAddrStatekey, 'hex'), Buffer.from(hostAddrStateData, 'hex'));
12071
- hostInfo.active = (hostInfo.lastHeartbeatLedger > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12072
- (hostInfo.lastHeartbeatLedger >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12073
- (hostInfo.lastHeartbeatLedger > 0))
12074
- return hostInfo;
12077
+ try {
12078
+ const addrStateKey = StateHelpers.generateHostAddrStateKey(hostAddress);
12079
+ const addrStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, addrStateKey);
12080
+ const addrLedgerEntry = await this.xrplApi.getLedgerEntry(addrStateIndex);
12081
+ const addrStateData = addrLedgerEntry?.HookStateData;
12082
+ if (addrStateData) {
12083
+ const addrStateDecoded = StateHelpers.decodeHostAddressState(Buffer.from(addrStateKey, 'hex'), Buffer.from(addrStateData, 'hex'));
12084
+ const curMomentStartIdx = await this.getMomentStartIndex();
12085
+ addrStateDecoded.active = (addrStateDecoded.lastHeartbeatLedger > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12086
+ (addrStateDecoded.lastHeartbeatLedger >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12087
+ (addrStateDecoded.lastHeartbeatLedger > 0))
12088
+
12089
+ const nftIdStatekey = StateHelpers.generateTokenIdStateKey(addrStateDecoded.nfTokenId);
12090
+ const nftIdStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, nftIdStatekey);
12091
+ const nftIdLedgerEntry = await this.xrplApi.getLedgerEntry(nftIdStateIndex);
12092
+
12093
+ const nftIdStateData = nftIdLedgerEntry?.HookStateData;
12094
+ if (nftIdStateData) {
12095
+ const nftIdStateDecoded = StateHelpers.decodeTokenIdState(Buffer.from(nftIdStateData, 'hex'));
12096
+ return { ...addrStateDecoded, ...nftIdStateDecoded };
12097
+ }
12098
+ }
12099
+ }
12100
+ catch (e) {
12101
+ // If the exeption is entryNotFound from Rippled there's no entry for the host, So return null.
12102
+ if (e?.data?.error !== 'entryNotFound')
12103
+ throw e;
12075
12104
  }
12076
12105
 
12077
12106
  return null;
@@ -12129,6 +12158,22 @@ class BaseEvernodeClient {
12129
12158
 
12130
12159
  return fullHostList;
12131
12160
  }
12161
+
12162
+ // To prune an inactive host/
12163
+ async pruneDeadHost(hostAddress) {
12164
+ if (this.xrplAcc.address === this.registryAddress)
12165
+ throw 'Invalid function call';
12166
+
12167
+ let memoData = Buffer.allocUnsafe(20);
12168
+ codec.decodeAccountID(hostAddress).copy(memoData);
12169
+
12170
+ await this.xrplAcc.makePayment(this.registryAddress,
12171
+ XrplConstants.MIN_XRP_AMOUNT,
12172
+ XrplConstants.XRP,
12173
+ null,
12174
+ [{ type: MemoTypes.DEAD_HOST_PRUNE, format: MemoFormats.HEX, data: memoData.toString('hex') }]);
12175
+
12176
+ }
12132
12177
  }
12133
12178
 
12134
12179
  module.exports = {
@@ -12513,7 +12558,8 @@ const RegistryEvents = {
12513
12558
  HostRegUpdated: EvernodeEvents.HostRegUpdated,
12514
12559
  RegistryInitialized: EvernodeEvents.RegistryInitialized,
12515
12560
  Heartbeat: EvernodeEvents.Heartbeat,
12516
- HostPostDeregistered: EvernodeEvents.HostPostDeregistered
12561
+ HostPostDeregistered: EvernodeEvents.HostPostDeregistered,
12562
+ DeadHostPrune: EvernodeEvents.DeadHostPrune
12517
12563
  }
12518
12564
 
12519
12565
  class RegistryClient extends BaseEvernodeClient {
@@ -12621,9 +12667,9 @@ class TenantClient extends BaseEvernodeClient {
12621
12667
 
12622
12668
  // Check whether active.
12623
12669
  const hostInfo = await this.getHostInfo(host.address);
12624
- if (hostInfo)
12670
+ if (!hostInfo)
12625
12671
  throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
12626
- else if (hostInfo.active)
12672
+ else if (!hostInfo.active)
12627
12673
  throw { reason: ErrorReasons.HOST_INACTIVE, error: "Host is not active." };
12628
12674
 
12629
12675
  return host;
@@ -13178,7 +13224,8 @@ const MemoTypes = {
13178
13224
  EXTEND_REF: 'evnExtendRef',
13179
13225
  REGISTRY_INIT: 'evnInitialize',
13180
13226
  REFUND: 'evnRefund',
13181
- REFUND_REF: 'evnRefundRef'
13227
+ REFUND_REF: 'evnRefundRef',
13228
+ DEAD_HOST_PRUNE: 'evnDeadHostPrune'
13182
13229
  }
13183
13230
 
13184
13231
  const MemoFormats = {
@@ -13216,11 +13263,15 @@ const HookStateKeys = {
13216
13263
  HOST_HEARTBEAT_FREQ: "4556520100000000000000000000000000000000000000000000000000000006",
13217
13264
  PURCHASER_TARGET_PRICE: "4556520100000000000000000000000000000000000000000000000000000007",
13218
13265
  LEASE_ACQUIRE_WINDOW: "4556520100000000000000000000000000000000000000000000000000000008",
13266
+ REWARD_CONFIGURATION: "4556520100000000000000000000000000000000000000000000000000000009",
13267
+ MAX_TOLERABLE_DOWNTIME: "455652010000000000000000000000000000000000000000000000000000000A",
13268
+
13219
13269
  // Singleton
13220
13270
  HOST_COUNT: "4556523200000000000000000000000000000000000000000000000000000000",
13221
13271
  MOMENT_BASE_IDX: "4556523300000000000000000000000000000000000000000000000000000000",
13222
13272
  HOST_REG_FEE: "4556523400000000000000000000000000000000000000000000000000000000",
13223
13273
  MAX_REG: "4556523500000000000000000000000000000000000000000000000000000000",
13274
+ REWARD_INFO: "4556523600000000000000000000000000000000000000000000000000000000",
13224
13275
 
13225
13276
  // Prefixes
13226
13277
  PREFIX_HOST_TOKENID: "45565202",
@@ -13240,7 +13291,8 @@ const EvernodeEvents = {
13240
13291
  ExtendError: "ExtendError",
13241
13292
  HostRegUpdated: "HostRegUpdated",
13242
13293
  HostReRegistered: "HostReRegistered",
13243
- RegistryInitialized: "RegistryInitialized"
13294
+ RegistryInitialized: "RegistryInitialized",
13295
+ DeadHostPrune: "DeadHostPrune"
13244
13296
  }
13245
13297
 
13246
13298
  module.exports = {
@@ -13313,6 +13365,13 @@ class FirestoreHandler {
13313
13365
  case 'floatValue':
13314
13366
  parsed = parseFloat(value[type]);
13315
13367
  break;
13368
+ case 'mapValue':
13369
+ parsed = {};
13370
+ for (const [subKey, subValue] of Object.entries(value[type].fields)) {
13371
+ const field = this.#parseValue(subKey, subValue);
13372
+ parsed[field.key] = field.value;
13373
+ }
13374
+ break;
13316
13375
  default:
13317
13376
  parsed = value[type];
13318
13377
  break;
@@ -13520,9 +13579,32 @@ class FirestoreHandler {
13520
13579
  convertValue(key, value) {
13521
13580
  // Convert camelCase to snake_case.
13522
13581
  const uKey = key.replace(/([A-Z])/g, function (g) { return `_${g[0].toLocaleLowerCase()}`; });
13523
- const type = `${typeof value !== 'number' ? 'string' : (value % 1 > 0 ? 'float' : 'integer')}Value`;
13582
+ let val = {};
13583
+ let type
13584
+ switch (typeof value) {
13585
+ case 'number':
13586
+ type = (value % 1 > 0 ? 'float' : 'integer');
13587
+ val = value;
13588
+ break;
13589
+ case 'object':
13590
+ type = 'map';
13591
+ val = {
13592
+ fields: {}
13593
+ }
13594
+ // Prepare the firestore write body with the given data object.
13595
+ for (const [subKey, subValue] of Object.entries(value)) {
13596
+ const field = this.convertValue(subKey, subValue);
13597
+ val.fields[field.key] = field.value;
13598
+ }
13599
+ break;
13600
+ default:
13601
+ type = 'string';
13602
+ val = value;
13603
+ break;
13604
+ }
13605
+ type = `${type}Value`;
13524
13606
  let obj = {};
13525
- obj[type] = value;
13607
+ obj[type] = val;
13526
13608
  return { key: uKey, value: obj };
13527
13609
  }
13528
13610
 
@@ -13608,7 +13690,18 @@ const { HookStateKeys, EvernodeConstants } = __nccwpck_require__(9849);
13608
13690
  const { XflHelpers } = __nccwpck_require__(3243);
13609
13691
  const crypto = __nccwpck_require__(6113);
13610
13692
 
13611
- const NFTOKEN_PREFIX = '00080000';
13693
+ const NFTOKEN_PREFIX = '00000000';
13694
+
13695
+ const EPOCH_OFFSET = 0;
13696
+ const SAVED_MOMENT_OFFSET = 1;
13697
+ const PREV_MOMENT_ACTIVE_HOST_COUNT_OFFSET = 5;
13698
+ const CUR_MOMENT_ACTIVE_HOST_COUNT_OFFSET = 9;
13699
+ const EPOCH_POOL_OFFSET = 13;
13700
+
13701
+ const EPOCH_COUNT_OFFSET = 0;
13702
+ const FIRST_EPOCH_REWARD_QUOTA_OFFSET = 1;
13703
+ const EPOCH_REWARD_AMOUNT_OFFSET = 5;
13704
+ const REWARD_START_MOMENT_OFFSET = 9;
13612
13705
 
13613
13706
  const HOST_TOKEN_ID_OFFSET = 0;
13614
13707
  const HOST_COUNTRY_CODE_OFFSET = 32;
@@ -13664,7 +13757,7 @@ class StateHelpers {
13664
13757
  static decodeTokenIdState(stateDataBuf) {
13665
13758
  return {
13666
13759
  address: codec.encodeAccountID(stateDataBuf.slice(HOST_ADDRESS_OFFSET, HOST_CPU_MODEL_NAME_OFFSET)),
13667
- cpuModelName: stateDataBuf.slice(HOST_CPU_MODEL_NAME_OFFSET, HOST_CPU_COUNT_OFFSET).toString(),
13760
+ cpuModelName: stateDataBuf.slice(HOST_CPU_MODEL_NAME_OFFSET, HOST_CPU_COUNT_OFFSET).toString().replace(/\x00+$/, ''), // Remove trailing \x00 characters.
13668
13761
  cpuCount: stateDataBuf.readUInt16BE(HOST_CPU_COUNT_OFFSET),
13669
13762
  cpuMHz: stateDataBuf.readUInt16BE(HOST_CPU_SPEED_OFFSET),
13670
13763
  cpuMicrosec: stateDataBuf.readUInt32BE(HOST_CPU_MICROSEC_OFFSET),
@@ -13747,6 +13840,38 @@ class StateHelpers {
13747
13840
  value: val
13748
13841
  }
13749
13842
  }
13843
+ else if (Buffer.from(HookStateKeys.REWARD_CONFIGURATION, 'hex').compare(stateKey) === 0) {
13844
+ return {
13845
+ type: this.StateTypes.CONFIGURATION,
13846
+ key: hexKey,
13847
+ value: {
13848
+ epochCount: stateData.readUInt8(EPOCH_COUNT_OFFSET),
13849
+ firstEpochRewardQuota: stateData.readUInt32BE(FIRST_EPOCH_REWARD_QUOTA_OFFSET),
13850
+ epochRewardAmount: stateData.readUInt32BE(EPOCH_REWARD_AMOUNT_OFFSET),
13851
+ rewardStartMoment: stateData.readUInt32BE(REWARD_START_MOMENT_OFFSET)
13852
+ }
13853
+ }
13854
+ }
13855
+ else if (Buffer.from(HookStateKeys.REWARD_INFO, 'hex').compare(stateKey) === 0) {
13856
+ return {
13857
+ type: this.StateTypes.SIGLETON,
13858
+ key: hexKey,
13859
+ value: {
13860
+ epoch: stateData.readUInt8(EPOCH_OFFSET),
13861
+ savedMoment: stateData.readUInt32BE(SAVED_MOMENT_OFFSET),
13862
+ prevMomentActiveHostCount: stateData.readUInt32BE(PREV_MOMENT_ACTIVE_HOST_COUNT_OFFSET),
13863
+ curMomentActiveHostCount: stateData.readUInt32BE(CUR_MOMENT_ACTIVE_HOST_COUNT_OFFSET),
13864
+ epochPool: XflHelpers.toString(stateData.readBigInt64BE(EPOCH_POOL_OFFSET))
13865
+ }
13866
+ }
13867
+ }
13868
+ else if (Buffer.from(HookStateKeys.MAX_TOLERABLE_DOWNTIME, 'hex').compare(stateKey) === 0) {
13869
+ return {
13870
+ type: this.StateTypes.CONFIGURATION,
13871
+ key: hexKey,
13872
+ value: stateData.readUInt16BE()
13873
+ }
13874
+ }
13750
13875
  else
13751
13876
  throw { type: 'Validation Error', message: 'Invalid state key.' };
13752
13877
  }
@@ -13768,7 +13893,8 @@ class StateHelpers {
13768
13893
  else if (Buffer.from(HookStateKeys.HOST_COUNT, 'hex').compare(stateKey) === 0 ||
13769
13894
  Buffer.from(HookStateKeys.MOMENT_BASE_IDX, 'hex').compare(stateKey) === 0 ||
13770
13895
  Buffer.from(HookStateKeys.HOST_REG_FEE, 'hex').compare(stateKey) === 0 ||
13771
- Buffer.from(HookStateKeys.MAX_REG, 'hex').compare(stateKey) === 0) {
13896
+ Buffer.from(HookStateKeys.MAX_REG, 'hex').compare(stateKey) === 0 ||
13897
+ Buffer.from(HookStateKeys.REWARD_INFO, 'hex').compare(stateKey) === 0) {
13772
13898
  return {
13773
13899
  key: hexKey,
13774
13900
  type: this.STATE_TYPES.SIGLETON
@@ -13781,7 +13907,9 @@ class StateHelpers {
13781
13907
  Buffer.from(HookStateKeys.HOST_HEARTBEAT_FREQ, 'hex').compare(stateKey) ||
13782
13908
  Buffer.from(HookStateKeys.MINT_LIMIT, 'hex').compare(stateKey) === 0 ||
13783
13909
  Buffer.from(HookStateKeys.FIXED_REG_FEE, 'hex').compare(stateKey) === 0 ||
13784
- Buffer.from(HookStateKeys.LEASE_ACQUIRE_WINDOW, 'hex').compare(stateKey) === 0) {
13910
+ Buffer.from(HookStateKeys.LEASE_ACQUIRE_WINDOW, 'hex').compare(stateKey) === 0 ||
13911
+ Buffer.from(HookStateKeys.REWARD_CONFIGURATION, 'hex').compare(stateKey) === 0 ||
13912
+ Buffer.from(HookStateKeys.MAX_TOLERABLE_DOWNTIME, 'hex').compare(stateKey) === 0) {
13785
13913
  return {
13786
13914
  key: hexKey,
13787
13915
  type: this.STATE_TYPES.CONFIGURATION
@@ -13807,7 +13935,7 @@ class StateHelpers {
13807
13935
  let buf = Buffer.allocUnsafe(9);
13808
13936
  buf.writeUInt8(STATE_KEY_TYPES.HOST_ADDR);
13809
13937
  for (let i = 0; i < HOST_ADDR_KEY_ZERO_COUNT; i++) {
13810
- buf.writeUInt8(0, i+1);
13938
+ buf.writeUInt8(0, i + 1);
13811
13939
  }
13812
13940
 
13813
13941
  const addrBuf = Buffer.from(codec.decodeAccountID(address), "hex");
@@ -13815,7 +13943,7 @@ class StateHelpers {
13815
13943
  return stateKeyBuf.toString('hex').toUpperCase();
13816
13944
  }
13817
13945
 
13818
- static getHookStateIndex(hookAccount, stateKey, hookNamespace = EvernodeConstants.HOOK_NAMESPACE) {
13946
+ static getHookStateIndex(hookAccount, stateKey, hookNamespace = EvernodeConstants.HOOK_NAMESPACE) {
13819
13947
  const typeBuf = Buffer.allocUnsafe(2);
13820
13948
  typeBuf.writeInt16BE(HOOK_STATE_LEDGER_TYPE_PREFIX);
13821
13949
 
@@ -14798,7 +14926,7 @@ class XrplApi {
14798
14926
 
14799
14927
  async getLedgerEntry(index, options) {
14800
14928
  const resp = (await this.#client.request({ command: 'ledger_entry', index: index, ledger_index: "validated", ...options }));
14801
- return resp?.result?.node;
14929
+ return resp?.result?.node;
14802
14930
  }
14803
14931
 
14804
14932
  async submitAndVerify(tx, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evernode-js-client",
3
- "version": "0.4.46",
3
+ "version": "0.4.50",
4
4
  "dependencies": {
5
5
  "elliptic": "6.5.4",
6
6
  "ripple-address-codec": "4.2.0",