diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..711f4c4 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 11 +} diff --git a/package-lock.json b/package-lock.json index 7b3933b..b8ef86e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "climate-tokenization-engine", - "version": "1.3.10", + "version": "1.3.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "climate-tokenization-engine", - "version": "1.3.10", - "license": "Apache", + "version": "1.3.11", + "license": "Apache-2.0", "dependencies": { "@chia-carbon/core-registry-config": "^1.0.2", "@chia-carbon/core-registry-logger": "^1.0.11", + "async-mutex": "^0.4.0", "body-parser": "^1.20.2", "chia-datalayer": "^2.0.0", "chia-root-resolver": "^1.0.0", @@ -1506,6 +1507,14 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "node_modules/async-mutex": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz", + "integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6560,6 +6569,11 @@ "node": ">= 14.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index d90c56a..6e578bf 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "dependencies": { "@chia-carbon/core-registry-config": "^1.0.2", "@chia-carbon/core-registry-logger": "^1.0.11", + "async-mutex": "^0.4.0", "body-parser": "^1.20.2", "chia-datalayer": "^2.0.0", "chia-root-resolver": "^1.0.0", diff --git a/src/api/registry.js b/src/api/registry.js index f2ee3b0..9085d10 100644 --- a/src/api/registry.js +++ b/src/api/registry.js @@ -4,6 +4,10 @@ const { CONFIG } = require("../config"); const { logger } = require("../logger"); const wallet = require("../chia/wallet"); const utils = require("../utils"); +const constants = require("../constants.js"); +const { Mutex } = require("async-mutex"); + +const mutex = new Mutex(); const registryUri = utils.generateUriForHostAndPort( CONFIG().CADT.PROTOCOL, @@ -520,74 +524,81 @@ const getOrgMetaData = async (orgUid) => { /** * Waits for the registry data to synchronize. * + * @param {object} [options] - Function options. + * @param {boolean} [options.throwOnEmptyRegistry=false] - Flag to throw error on empty registry. * @returns {Promise} */ -const waitForRegistryDataSync = async () => { - if (process.env.NODE_ENV === "test") { - return; - } - - await utils.waitFor(5000); - const dataLayerConfig = {}; - - if (CONFIG().CHIA.DATALAYER_HOST) { - dataLayerConfig.datalayer_host = CONFIG().CHIA.DATALAYER_HOST; - } - - if (CONFIG().CHIA.WALLET_HOST) { - dataLayerConfig.wallet_host = CONFIG().CHIA.WALLET_HOST; - } - - if (CONFIG().CHIA.CERTIFICATE_FOLDER_PATH) { - dataLayerConfig.certificate_folder_path = - CONFIG().CHIA.CERTIFICATE_FOLDER_PATH; - } - - if (CONFIG().CHIA.ALLOW_SELF_SIGNED_CERTIFICATES) { - dataLayerConfig.allowUnverifiedCert = - CONFIG().CHIA.ALLOW_SELF_SIGNED_CERTIFICATES; - } - - const datalayer = new Datalayer(dataLayerConfig); - const homeOrg = await getHomeOrg(); - - if (!homeOrg) { - logger.warn( - "Can not find the home org from the Registry. Please verify your Registry is running and you have created a Home Organization." - ); - return waitForRegistryDataSync(); - } - - const onChainRegistryRoot = await datalayer.getRoot({ - id: homeOrg.registryId, - }); - - if (!onChainRegistryRoot.confirmed) { - console.log("Waiting for Registry root to confirm"); - return waitForRegistryDataSync(); - } - - if (onChainRegistryRoot.hash !== homeOrg.registryHash) { - console.log("Waiting for Registry to sync with latest regisry root.", { - onChainRoot: onChainRegistryRoot.hash, - homeOrgRegistryRoot: homeOrg.registryHash, - }); - return waitForRegistryDataSync(); - } +const waitForRegistryDataSync = async (options = {}) => { + await mutex.waitForUnlock(); - const onChainOrgRoot = await datalayer.getRoot({ id: homeOrg.orgUid }); + if (!mutex.isLocked()) { + const releaseMutex = await mutex.acquire(); + try { + const opts = { throwOnEmptyRegistry: false, ...options }; - if (!onChainOrgRoot.confirmed) { - console.log("Waiting for Organization root to confirm"); - return waitForRegistryDataSync(); - } + if (process.env.NODE_ENV === "test") { + return; + } - if (onChainOrgRoot.hash !== homeOrg.orgHash) { - console.log("Waiting for Registry to sync with latest organization root.", { - onChainRoot: onChainOrgRoot.hash, - homeOrgRoot: homeOrg.orgHash, - }); - return waitForRegistryDataSync(); + while (true) { + await utils.waitFor(5000); + + const config = CONFIG().CHIA; + const dataLayerConfig = { + datalayer_host: config.DATALAYER_HOST, + wallet_host: config.WALLET_HOST, + certificate_folder_path: config.CERTIFICATE_FOLDER_PATH, + allowUnverifiedCert: config.ALLOW_SELF_SIGNED_CERTIFICATES, + }; + + const datalayer = new Datalayer(dataLayerConfig); + const homeOrg = await getHomeOrg(); + + if (!homeOrg) { + logger.warn("Cannot find the home org from the Registry. Please verify your Registry is running and you have created a Home Organization."); + continue; + } + + const onChainRegistryRoot = await datalayer.getRoot({ id: homeOrg.registryId }); + + if (!onChainRegistryRoot.confirmed) { + console.log("Waiting for Registry root to confirm"); + continue; + } + + if (onChainRegistryRoot.hash === constants.emptySingletonHash && opts.throwOnEmptyRegistry) { + throw new Error("Registry is empty. Please add some data to run auto retirement task."); + } + + if (onChainRegistryRoot.hash !== homeOrg.registryHash) { + console.log("Waiting for Registry to sync with latest registry root.", { + onChainRoot: onChainRegistryRoot.hash, + homeOrgRegistryRoot: homeOrg.registryHash, + }); + continue; + } + + const onChainOrgRoot = await datalayer.getRoot({ id: homeOrg.orgUid }); + + if (!onChainOrgRoot.confirmed) { + console.log("Waiting for Organization root to confirm"); + continue; + } + + if (onChainOrgRoot.hash !== homeOrg.orgHash) { + console.log("Waiting for Registry to sync with latest organization root.", { + onChainRoot: onChainOrgRoot.hash, + homeOrgRoot: homeOrg.orgHash, + }); + continue; + } + + // Exit the loop if all conditions are met + break; + } + } finally { + releaseMutex(); + } } }; diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..0ba4879 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,4 @@ +module.exports = { + emptySingletonHash: + "0x0000000000000000000000000000000000000000000000000000000000000000", +};