diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1de5a..6f1a954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.0.1-alpha.121](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.120...v0.0.1-alpha.121) (2024-10-03) + + +### Features + +* add store monitor ([8a0997d](https://github.com/DIG-Network/dig-chia-sdk/commit/8a0997d0079c62db4bdd8f6c04eb94b749b66572)) + ### [0.0.1-alpha.120](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.119...v0.0.1-alpha.120) (2024-10-03) ### [0.0.1-alpha.119](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.118...v0.0.1-alpha.119) (2024-10-03) diff --git a/package-lock.json b/package-lock.json index 7ef0b91..134f74b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.120", + "version": "0.0.1-alpha.121", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.120", + "version": "0.0.1-alpha.121", "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.29", diff --git a/package.json b/package.json index c8b09d5..fd23cc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.120", + "version": "0.0.1-alpha.121", "description": "", "type": "commonjs", "main": "./dist/index.js", diff --git a/src/blockchain/DataStore.ts b/src/blockchain/DataStore.ts index 3828283..f5aa67d 100644 --- a/src/blockchain/DataStore.ts +++ b/src/blockchain/DataStore.ts @@ -40,6 +40,7 @@ import NodeCache from "node-cache"; import { MAIN_NET_GENISES_CHALLENGE } from "../utils/config"; import { StoreInfoCacheUpdater } from "./StoreInfoCacheUpdater"; import { Environment } from "../utils"; +import { get } from "lodash"; // Initialize the cache with a TTL of 180 seconds (3 minutes) const rootHistoryCache = new NodeCache({ stdTTL: 180 }); @@ -50,6 +51,7 @@ const readdir = promisify(fs.readdir); export class DataStore { private storeId: string; private tree: DataIntegrityTree; + private static activeMonitors: Map = new Map(); constructor(storeId: string, options?: DataIntegrityTreeOptions) { this.storeId = storeId; @@ -66,6 +68,10 @@ export class DataStore { } this.tree = new DataIntegrityTree(storeId, _options); + + if (!Environment.CLI_MODE) { + DataStore.monitorStoreIndefinitely(this.storeId); + } } public get StoreId(): string { @@ -308,6 +314,131 @@ export class DataStore { return storIds.map((storeId) => DataStore.from(storeId)); } + /** + * Monitors the store indefinitely by syncing it with a peer. + * Logs store retrieval and caching operations. If an error occurs, logs the error and restarts the process after a short delay. + * Only one monitor can be active per storeId. + * @param {string} storeId - The store identifier to monitor. + * @returns {Promise} Never resolves; runs indefinitely. + */ + public static async monitorStoreIndefinitely(storeId: string): Promise { + // Check if a monitor is already running for this storeId + if (this.activeMonitors.get(storeId)) { + console.log(`Monitor already running for storeId: ${storeId}`); + return; + } + + // Set the monitor as active + this.activeMonitors.set(storeId, true); + + const storeCoinCache = new FileCache<{ + latestStore: ReturnType; + latestHeight: number; + latestHash: string; + }>(`stores`); + + // Clear the cache at the start + console.log(`Clearing cache for storeId: ${storeId}`); + storeCoinCache.delete(storeId); + + while (true) { + try { + console.log(`Connecting to peer for storeId: ${storeId}`); + const peer = await FullNodePeer.connect(); + const cachedInfo = storeCoinCache.get(storeId); + + if (cachedInfo) { + // Log cached store info retrieval + console.log( + `Cached store info found for storeId: ${storeId}, syncing...` + ); + + // Deserialize cached info and wait for the coin to be spent + const previousStore = DataStoreSerializer.deserialize({ + latestStore: cachedInfo.latestStore, + latestHeight: cachedInfo.latestHeight.toString(), + latestHash: cachedInfo.latestHash, + }); + + console.log( + `Waiting for coin to be spent for storeId: ${storeId}...` + ); + await peer.waitForCoinToBeSpent( + getCoinId(previousStore.latestStore.coin), + previousStore.latestHeight, + previousStore.latestHash + ); + + // Sync store and get updated details + console.log(`Syncing store for storeId: ${storeId}`); + const { latestStore, latestHeight } = await peer.syncStore( + previousStore.latestStore, + previousStore.latestHeight, + previousStore.latestHash, + false + ); + const latestHash = await peer.getHeaderHash(latestHeight); + + // Serialize and cache the updated store info + const serializedLatestStore = new DataStoreSerializer( + latestStore, + latestHeight, + latestHash + ).serialize(); + + console.log(`Caching updated store info for storeId: ${storeId}`); + storeCoinCache.set(storeId, { + latestStore: serializedLatestStore, + latestHeight, + latestHash: latestHash.toString("hex"), + }); + + continue; // Continue monitoring + } + + // If no cached info exists, log and sync from the creation height + console.log( + `No cached info found for storeId: ${storeId}. Retrieving creation height.` + ); + + const dataStore = DataStore.from(storeId); + const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight(); + + // Sync store from the peer using launcher ID + console.log(`Syncing store from launcher ID for storeId: ${storeId}`); + const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId( + Buffer.from(storeId, "hex"), + createdAtHeight, + createdAtHash, + false + ); + + const latestHash = await peer.getHeaderHash(latestHeight); + + // Serialize and cache the new store info + const serializedLatestStore = new DataStoreSerializer( + latestStore, + latestHeight, + latestHash + ).serialize(); + + console.log(`Caching new store info for storeId: ${storeId}`); + storeCoinCache.set(storeId, { + latestStore: serializedLatestStore, + latestHeight, + latestHash: latestHash.toString("hex"), + }); + } catch (error: any) { + console.error( + `Error in monitorStoreIndefinitely for storeId: ${storeId} - ${error.message}` + ); + + // Delay before restarting to avoid rapid retries + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + public async fetchCoinInfo(): Promise<{ latestStore: DataStoreDriver; latestHeight: number;