evernode-js-client 0.4.45 → 0.4.48

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 +81 -21
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -11716,6 +11716,7 @@ const { EventEmitter } = __nccwpck_require__(6170);
11716
11716
  const { UtilHelpers } = __nccwpck_require__(6687);
11717
11717
  const { FirestoreHandler } = __nccwpck_require__(9718);
11718
11718
  const { XflHelpers } = __nccwpck_require__(3243);
11719
+ const { StateHelpers } = __nccwpck_require__(3860);
11719
11720
 
11720
11721
  class BaseEvernodeClient {
11721
11722
 
@@ -11802,13 +11803,11 @@ class BaseEvernodeClient {
11802
11803
 
11803
11804
  async getHookStates() {
11804
11805
  const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
11805
- const hookNamespaces = (await regAcc.getInfo())?.HookNamespaces;
11806
- if (hookNamespaces) {
11807
- const configs = await regAcc.getNamespaceEntries(hookNamespaces[0]);
11806
+ const configs = await regAcc.getNamespaceEntries(EvernodeConstants.HOOK_NAMESPACE);
11807
+
11808
+ if (configs)
11808
11809
  return configs.filter(c => c.LedgerEntryType === 'HookState').map(c => { return { key: c.HookStateKey, data: c.HookStateData } });
11809
- }
11810
11810
  return [];
11811
-
11812
11811
  }
11813
11812
 
11814
11813
  async getMoment(ledgerIndex = null) {
@@ -12060,6 +12059,40 @@ class BaseEvernodeClient {
12060
12059
  return null;
12061
12060
  }
12062
12061
 
12062
+ // To get Host details from Hook States.
12063
+ async getHostInfo(hostAddress = this.xrplAcc.address) {
12064
+ try {
12065
+ const addrStateKey = StateHelpers.generateHostAddrStateKey(hostAddress);
12066
+ const addrStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, addrStateKey);
12067
+ const addrLedgerEntry = await this.xrplApi.getLedgerEntry(addrStateIndex);
12068
+ const addrStateData = addrLedgerEntry?.HookStateData;
12069
+ if (addrStateData) {
12070
+ const addrStateDecoded = StateHelpers.decodeHostAddressState(Buffer.from(addrStateKey, 'hex'), Buffer.from(addrStateData, 'hex'));
12071
+ const curMomentStartIdx = await this.getMomentStartIndex();
12072
+ addrStateDecoded.active = (addrStateDecoded.lastHeartbeatLedger > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12073
+ (addrStateDecoded.lastHeartbeatLedger >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12074
+ (addrStateDecoded.lastHeartbeatLedger > 0))
12075
+
12076
+ const nftIdStatekey = StateHelpers.generateTokenIdStateKey(addrStateDecoded.nfTokenId);
12077
+ const nftIdStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, nftIdStatekey);
12078
+ const nftIdLedgerEntry = await this.xrplApi.getLedgerEntry(nftIdStateIndex);
12079
+
12080
+ const nftIdStateData = nftIdLedgerEntry?.HookStateData;
12081
+ if (nftIdStateData) {
12082
+ const nftIdStateDecoded = StateHelpers.decodeTokenIdState(Buffer.from(nftIdStateData, 'hex'));
12083
+ return {...addrStateDecoded, ...nftIdStateDecoded};
12084
+ }
12085
+ }
12086
+ }
12087
+ catch (e) {
12088
+ // If the exeption is entryNotFound from Rippled there's no entry for the host, So return null.
12089
+ if (e?.data?.error !== 'entryNotFound')
12090
+ throw e;
12091
+ }
12092
+
12093
+ return null;
12094
+ }
12095
+
12063
12096
  // To fetch records from host collection in Firestore database. (Pagination is applied - default page size :20)
12064
12097
  async getHosts(filters = null, pageSize = null, nextPageToken = null) {
12065
12098
  const hosts = await this.#firestoreHandler.getHosts(filters, pageSize, nextPageToken);
@@ -12165,9 +12198,8 @@ class HostClient extends BaseEvernodeClient {
12165
12198
  // Check whether we own an evernode host token.
12166
12199
  const nft = await this.getRegistrationNft();
12167
12200
  if (nft) {
12168
- const host = await this.getHosts({ nfTokenId: nft.NFTokenID });
12169
- if (host && host.length == 1)
12170
- return host[0];
12201
+ const host = await this.getHostInfo();
12202
+ return (host?.nfTokenId == nft.NFTokenID) ? host : null;
12171
12203
  }
12172
12204
 
12173
12205
  return null;
@@ -12266,10 +12298,10 @@ class HostClient extends BaseEvernodeClient {
12266
12298
  // from the client-side in order to complete the registration.
12267
12299
  const regNft = await this.getRegistrationNft();
12268
12300
  if (!regNft) {
12269
- const regInfo = await this.getHosts({ address: this.xrplAcc.address });
12270
- if (regInfo.length !== 0) {
12301
+ const regInfo = await this.getHostInfo(this.xrplAcc.address);
12302
+ if (regInfo) {
12271
12303
  const registryAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
12272
- const sellOffer = (await registryAcc.getNftOffers()).find(o => o.NFTokenID == regInfo[0].nfTokenId);
12304
+ const sellOffer = (await registryAcc.getNftOffers()).find(o => o.NFTokenID == regInfo.nfTokenId);
12273
12305
  if (sellOffer) {
12274
12306
  await this.xrplAcc.buyNft(sellOffer.index);
12275
12307
  console.log("Registration was successfully completed after acquiring the NFT.");
@@ -12516,7 +12548,7 @@ class RegistryClient extends BaseEvernodeClient {
12516
12548
  while (currentPageToken) {
12517
12549
  nextHosts = await this.getHosts(null, null, currentPageToken);
12518
12550
  fullHostList = fullHostList.concat(nextHosts.nextPageToken ? nextHosts.data : nextHosts);
12519
- currentPageToken = nextHosts.nextPageToken;
12551
+ currentPageToken = nextHosts.nextPageToken;
12520
12552
  }
12521
12553
  } else {
12522
12554
  fullHostList = fullHostList.concat(hosts);
@@ -12604,10 +12636,10 @@ class TenantClient extends BaseEvernodeClient {
12604
12636
  throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
12605
12637
 
12606
12638
  // Check whether active.
12607
- const hosts = await this.getHosts({ address: host.address });
12608
- if (!hosts || !hosts.length)
12639
+ const hostInfo = await this.getHostInfo(host.address);
12640
+ if (!hostInfo)
12609
12641
  throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
12610
- else if (!hosts[0].active)
12642
+ else if (!hostInfo.active)
12611
12643
  throw { reason: ErrorReasons.HOST_INACTIVE, error: "Host is not active." };
12612
12644
 
12613
12645
  return host;
@@ -12620,7 +12652,7 @@ class TenantClient extends BaseEvernodeClient {
12620
12652
 
12621
12653
  // Attempt to get first available offer, if offer is not specified in options.
12622
12654
  if (!selectedOfferIndex) {
12623
- const nftOffers = EvernodeHelpers.getLeaseOffers(hostAcc);
12655
+ const nftOffers = await EvernodeHelpers.getLeaseOffers(hostAcc);
12624
12656
  selectedOfferIndex = nftOffers && nftOffers[0] && nftOffers[0].index;
12625
12657
 
12626
12658
  if (!selectedOfferIndex)
@@ -12751,7 +12783,7 @@ module.exports = {
12751
12783
  /***/ ((module) => {
12752
12784
 
12753
12785
  const DefaultValues = {
12754
- registryAddress: 'rDPqJv7zu6DfeXexAYseABNM2hT2j2rpHv',
12786
+ registryAddress: 'raaFre81618XegCrzTzVotAmarBcqNSAvK',
12755
12787
  rippledServer: 'wss://hooks-testnet-v2.xrpl-labs.com',
12756
12788
  xrplApi: null,
12757
12789
  stateIndexId: 'evernodeindex'
@@ -13142,7 +13174,8 @@ module.exports = {
13142
13174
  const EvernodeConstants = {
13143
13175
  EVR: 'EVR',
13144
13176
  NFT_PREFIX_HEX: '657672686F7374', // evrhost
13145
- LEASE_NFT_PREFIX_HEX: '6576726C65617365' // evrlease
13177
+ LEASE_NFT_PREFIX_HEX: '6576726C65617365', // evrlease
13178
+ HOOK_NAMESPACE: '01EAF09326B4911554384121FF56FA8FECC215FDDE2EC35D9E59F2C53EC665A0'
13146
13179
  }
13147
13180
 
13148
13181
  const MemoTypes = {
@@ -13587,10 +13620,11 @@ module.exports = {
13587
13620
 
13588
13621
  const codec = __nccwpck_require__(597);
13589
13622
  const { Buffer } = __nccwpck_require__(4300);
13590
- const { HookStateKeys } = __nccwpck_require__(9849);
13623
+ const { HookStateKeys, EvernodeConstants } = __nccwpck_require__(9849);
13591
13624
  const { XflHelpers } = __nccwpck_require__(3243);
13625
+ const crypto = __nccwpck_require__(6113);
13592
13626
 
13593
- const NFTOKEN_PREFIX = '00080000';
13627
+ const NFTOKEN_PREFIX = '00000000';
13594
13628
 
13595
13629
  const HOST_TOKEN_ID_OFFSET = 0;
13596
13630
  const HOST_COUNTRY_CODE_OFFSET = 32;
@@ -13618,6 +13652,7 @@ const STATE_KEY_TYPES = {
13618
13652
 
13619
13653
  const EVERNODE_PREFIX = 'EVR';
13620
13654
  const HOST_ADDR_KEY_ZERO_COUNT = 8;
13655
+ const HOOK_STATE_LEDGER_TYPE_PREFIX = 118; // Decimal value of ASCII 'v'
13621
13656
 
13622
13657
  class StateHelpers {
13623
13658
  static StateTypes = {
@@ -13645,7 +13680,7 @@ class StateHelpers {
13645
13680
  static decodeTokenIdState(stateDataBuf) {
13646
13681
  return {
13647
13682
  address: codec.encodeAccountID(stateDataBuf.slice(HOST_ADDRESS_OFFSET, HOST_CPU_MODEL_NAME_OFFSET)),
13648
- cpuModelName: stateDataBuf.slice(HOST_CPU_MODEL_NAME_OFFSET, HOST_CPU_COUNT_OFFSET).toString(),
13683
+ cpuModelName: stateDataBuf.slice(HOST_CPU_MODEL_NAME_OFFSET, HOST_CPU_COUNT_OFFSET).toString().replace(/\x00+$/, ''), // Remove trailing \x00 characters.
13649
13684
  cpuCount: stateDataBuf.readUInt16BE(HOST_CPU_COUNT_OFFSET),
13650
13685
  cpuMHz: stateDataBuf.readUInt16BE(HOST_CPU_SPEED_OFFSET),
13651
13686
  cpuMicrosec: stateDataBuf.readUInt32BE(HOST_CPU_MICROSEC_OFFSET),
@@ -13795,6 +13830,26 @@ class StateHelpers {
13795
13830
  const stateKeyBuf = Buffer.concat([Buffer.from(EVERNODE_PREFIX, "utf-8"), buf, addrBuf]);
13796
13831
  return stateKeyBuf.toString('hex').toUpperCase();
13797
13832
  }
13833
+
13834
+ static getHookStateIndex(hookAccount, stateKey, hookNamespace = EvernodeConstants.HOOK_NAMESPACE) {
13835
+ const typeBuf = Buffer.allocUnsafe(2);
13836
+ typeBuf.writeInt16BE(HOOK_STATE_LEDGER_TYPE_PREFIX);
13837
+
13838
+ const accIdBuf = codec.decodeAccountID(hookAccount);
13839
+ const stateKeyBuf = Buffer.from(stateKey, 'hex');
13840
+ const namespaceBuf = Buffer.from(hookNamespace, 'hex');
13841
+
13842
+ let hash = crypto.createHash('sha512');
13843
+
13844
+ let data = hash.update(typeBuf);
13845
+ data = hash.update(accIdBuf);
13846
+ data = hash.update(stateKeyBuf);
13847
+ data = hash.update(namespaceBuf);
13848
+
13849
+ const digest = data.digest('hex');
13850
+ // Get the first 32 bytes of hash.
13851
+ return digest.substring(0, 64).toUpperCase();
13852
+ }
13798
13853
  }
13799
13854
 
13800
13855
  module.exports = {
@@ -14757,6 +14812,11 @@ class XrplApi {
14757
14812
  return [];
14758
14813
  }
14759
14814
 
14815
+ async getLedgerEntry(index, options) {
14816
+ const resp = (await this.#client.request({ command: 'ledger_entry', index: index, ledger_index: "validated", ...options }));
14817
+ return resp?.result?.node;
14818
+ }
14819
+
14760
14820
  async submitAndVerify(tx, options) {
14761
14821
  return await this.#client.submitAndWait(tx, options);
14762
14822
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evernode-js-client",
3
- "version": "0.4.45",
3
+ "version": "0.4.48",
4
4
  "dependencies": {
5
5
  "elliptic": "6.5.4",
6
6
  "ripple-address-codec": "4.2.0",