diff --git a/mb-xrpl/app.js b/mb-xrpl/app.js index ddc0f06..426f219 100644 --- a/mb-xrpl/app.js +++ b/mb-xrpl/app.js @@ -43,7 +43,7 @@ async function main() { console.log(`Usage: node index.js - Run message board. node index.js version - Print version. - node index.js new [address] [secret] [registryAddress] [token] - Create new config file. + node index.js new [address] [secret] [registryAddress] [leaseAmount] - Create new config file. node index.js betagen [registryAddress] [domain or ip] [leaseAmount] - Generate beta host account and populate config. node index.js register [countryCode] [cpuMicroSec] [ramKb] [swapKb] [diskKb] [totalInstanceCount] [description] - Register the host on Evernode. node index.js deregister - Deregister the host from Evernode. diff --git a/mb-xrpl/lib/message-board.js b/mb-xrpl/lib/message-board.js index 43cde5f..ee51116 100644 --- a/mb-xrpl/lib/message-board.js +++ b/mb-xrpl/lib/message-board.js @@ -73,7 +73,7 @@ class MessageBoard { const leaseRecords = (await this.getLeaseRecords()).filter(r => (r.status === LeaseStatus.ACQUIRED || r.status === LeaseStatus.EXTENDED)); for (const lease of leaseRecords) - this.addToExpiryList(lease.tx_hash, lease.container_name, await this.getExpiryMoment(lease.created_on_ledger, lease.life_moments)); + this.addToExpiryList(lease.tx_hash, lease.container_name, lease.tenant_xrp_address, this.getExpiryLedger(lease.created_on_ledger, lease.life_moments)); this.db.close(); @@ -102,15 +102,18 @@ class MessageBoard { } // Filter out instances which needed to be expired and destroy them. - const expired = this.expiryList.filter(x => x.expiryMoment < currentMoment); + const expired = this.expiryList.filter(x => x.expiryLedger < e.ledger_index); if (expired && expired.length) { - this.expiryList = this.expiryList.filter(x => x.expiryMoment >= currentMoment); + this.expiryList = this.expiryList.filter(x => x.expiryLedger >= e.ledger_index); this.db.open(); for (const x of expired) { try { - console.log(`Moments exceeded (current:${currentMoment}, expiry:${x.expiryMoment}). Destroying ${x.containerName}`); - await this.sashiCli.destroyInstance(x.containerName); + console.log(`Moments exceeded (current ledger:${e.ledger_index}, expiry ledger:${x.expiryLedger}). Destroying ${x.containerName}`); + // Expire the current lease agreement (Burn the instance NFT) and re-minting and creating sell offer for the same lease index. + const nft = (await (new evernode.XrplAccount(x.tenant)).getNfts())?.find(n => n.TokenID == x.containerName); + const uriInfo = evernode.UtilHelpers.decodeLeaseNftUri(nft.URI); + await this.destroyInstance(x.containerName, uriInfo.leaseIndex, this.cfg.xrpl.leaseAmount); await this.updateLeaseStatus(x.txHash, LeaseStatus.EXPIRED); console.log(`Destroyed ${x.containerName}`); } @@ -185,6 +188,7 @@ class MessageBoard { console.error(`Sashimono busy timeout. Took: ${diff} ledgers. Threshold: ${threshold}`); // Update the lease status of the request to 'SashiTimeout'. await this.updateAcquireStatus(acquireRefId, LeaseStatus.SASHI_TIMEOUT); + await this.recreateLeaseOffer(nfTokenId, leaseIndex, leaseAmount); } else { const instanceRequirements = r.payload; @@ -198,8 +202,7 @@ class MessageBoard { console.error(`Instance creation timeout. Took: ${diff} ledgers. Threshold: ${threshold}`); // Update the lease status of the request to 'SashiTimeout'. await this.updateLeaseStatus(acquireRefId, LeaseStatus.SASHI_TIMEOUT); - // Destroy the instance. - await this.sashiCli.destroyInstance(createRes.content.name); + await this.destroyInstance(createRes.content.name, leaseIndex, leaseAmount); } else { console.log(`Instance created for ${tenantAddress}`); @@ -207,7 +210,7 @@ class MessageBoard { const currentLedgerIndex = this.lastValidatedLedgerIndex; // Add to in-memory expiry list, so the instance will get destroyed when the moments exceed, - this.addToExpiryList(acquireRefId, containerName, await this.getExpiryMoment(currentLedgerIndex, moments)); + this.addToExpiryList(acquireRefId, createRes.content.name, tenantAddress, this.getExpiryLedger(currentLedgerIndex, moments)); // Update the database for acquired record. await this.updateAcquiredRecord(acquireRefId, currentLedgerIndex); @@ -236,6 +239,12 @@ class MessageBoard { } } + async destroyInstance(containerName, leaseIndex, leaseAmount) { + await this.recreateLeaseOffer(containerName, leaseIndex, leaseAmount).catch(console.error); + // Destroy the instance. + await this.sashiCli.destroyInstance(containerName); + } + async handleExtendLease(r) { this.db.open(); @@ -280,8 +289,9 @@ class MessageBoard { for (const item of this.expiryList) { if (item.containerName === instance.container_name) { - item.expiryMoment += extendingMoments; - expiryMoment = item.expiryMoment; + item.expiryLedger = this.getExpiryLedger(item.expiryLedger, extendingMoments); + expiryMoment = (await this.hostClient.getMoment(instance.created_on_ledger)) + extendingMoments; + let obj = { status: LeaseStatus.EXTENDED, life_moments: (instance.life_moments + extendingMoments) @@ -308,13 +318,14 @@ class MessageBoard { } } - addToExpiryList(txHash, containerName, expiryMoment) { + addToExpiryList(txHash, containerName, tenant, expiryLedger) { this.expiryList.push({ txHash: txHash, containerName: containerName, - expiryMoment: expiryMoment, + tenant: tenant, + expiryLedger: expiryLedger, }); - console.log(`Container ${containerName} expiry set at ${expiryMoment}`); + console.log(`Container ${containerName} expiry set at ledger ${expiryLedger}`); } async createLeaseTableIfNotExists() { @@ -399,8 +410,8 @@ class MessageBoard { await this.db.updateValue(this.leaseTable, savingData, { tx_hash: txHash }); } - async getExpiryMoment(createdOnLedger, moments) { - return (await this.hostClient.getMoment(createdOnLedger)) + moments; + getExpiryLedger(ledgerIndex, moments) { + return ledgerIndex + moments * this.hostClient.config.momentSize; } readConfig() { diff --git a/mb-xrpl/package-lock.json b/mb-xrpl/package-lock.json index d295641..6ac1e4c 100644 --- a/mb-xrpl/package-lock.json +++ b/mb-xrpl/package-lock.json @@ -772,9 +772,9 @@ "dev": true }, "evernode-js-client": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.4.23.tgz", - "integrity": "sha512-elnJNvFF6VGmlgu8jkr3U2wzchMuLY1GE16JPPV3hZOlke++HUQujlI/fLkf/dBGmGCcfAsD2Y4QOxLRJ5PcqA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.4.24.tgz", + "integrity": "sha512-sRTr3bm94YyAU4R5+BpY1nwlHvXqpVgQIN0L3sjBv3XeP75IIwjshISWznvEZjUQV9DMbWIPjWlOUZit2X66ow==", "requires": { "elliptic": "6.5.4", "ripple-address-codec": "4.2.0", @@ -1248,9 +1248,9 @@ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "requires": { "has-tostringtag": "^1.0.0" } @@ -1265,9 +1265,12 @@ } }, "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } }, "is-string": { "version": "1.0.7",