diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eab93c..66a318b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ 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.97](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.96...v0.0.1-alpha.97) (2024-09-26) + + +### Features + +* add enhanced server coin management ([2a968b6](https://github.com/DIG-Network/dig-chia-sdk/commit/2a968b64ac58acb54e00b5c289c7057be253ce5f)) + +### [0.0.1-alpha.96](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.95...v0.0.1-alpha.96) (2024-09-26) + + +### Features + +* add enhanced server coin management ([05010d1](https://github.com/DIG-Network/dig-chia-sdk/commit/05010d142955f2d5bef25c85dc23949077c83ae6)) + ### [0.0.1-alpha.95](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.94...v0.0.1-alpha.95) (2024-09-26) diff --git a/package-lock.json b/package-lock.json index ac0133b..fe3d9e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.95", + "version": "0.0.1-alpha.97", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.95", + "version": "0.0.1-alpha.97", "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.25", diff --git a/package.json b/package.json index e0d1c00..1372858 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.95", + "version": "0.0.1-alpha.97", "description": "", "type": "commonjs", "main": "./dist/index.js", diff --git a/src/blockchain/ServerCoin.ts b/src/blockchain/ServerCoin.ts index ccf1428..016de0d 100644 --- a/src/blockchain/ServerCoin.ts +++ b/src/blockchain/ServerCoin.ts @@ -15,7 +15,7 @@ import { NconfManager } from "../utils/NconfManager"; import { CoinData, ServerCoinData } from "../types"; import { DataStore } from "./DataStore"; import NodeCache from "node-cache"; -import { getPublicIpAddress } from '../utils/network'; +import { getPublicIpAddress } from "../utils/network"; import { Environment } from "../utils/Environment"; const serverCoinCollateral = 300_000_000; @@ -34,7 +34,7 @@ export class ServerCoin { } // Create a new server coin for the current epoch - public async createForEpoch(peerIp: string): Promise { + public async createForEpoch(peerIp: string, rootHash: string): Promise { try { const peer = await FullNodePeer.connect(); const wallet = await Wallet.load("default"); @@ -45,7 +45,7 @@ export class ServerCoin { BigInt(1000000) ); - const { epoch: currentEpoch} = ServerCoin.getCurrentEpoch(); + const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch(); const epochBasedHint = morphLauncherId( Buffer.from(this.storeId, "hex"), BigInt(currentEpoch) @@ -74,7 +74,7 @@ export class ServerCoin { if (err.includes("no spendable coins")) { console.log("No coins available. Will try again in 5 seconds..."); await new Promise((resolve) => setTimeout(resolve, 5000)); - return this.createForEpoch(peerIp); + return this.createForEpoch(peerIp, rootHash); } throw new Error(err); } @@ -83,7 +83,8 @@ export class ServerCoin { await this.saveServerCoinData( newServerCoin.serverCoin, currentEpoch, - peerIp + peerIp, + rootHash ); return newServerCoin.serverCoin; @@ -96,7 +97,8 @@ export class ServerCoin { public async saveServerCoinData( serverCoin: ServerCoinDriver, epoch: number, - peerIp: string + peerIp: string, + rootHash: string, ): Promise { const newServerCoinData: ServerCoinData = { coin: { @@ -105,7 +107,8 @@ export class ServerCoin { parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"), }, createdAt: new Date().toISOString(), - epoch: epoch, + epoch, + rootHash, }; const serverCoins = await this.getServerCoinsForStore(peerIp); @@ -185,7 +188,10 @@ export class ServerCoin { ); } - public async getAllEpochPeers(epoch: number, blacklist: string[] = []): Promise { + public async getAllEpochPeers( + epoch: number, + blacklist: string[] = [] + ): Promise { const cacheKey = `serverCoinPeers-${this.storeId}-${epoch}`; // Check if the result is already cached @@ -194,12 +200,18 @@ export class ServerCoin { return cachedPeers; } - const epochBasedHint = morphLauncherId(Buffer.from(this.storeId, "hex"), BigInt(epoch)); + const epochBasedHint = morphLauncherId( + Buffer.from(this.storeId, "hex"), + BigInt(epoch) + ); const peer = await FullNodePeer.connect(); const maxClvmCost = BigInt(11_000_000_000); - const hintedCoinStates = await peer.getHintedCoinStates(epochBasedHint, false); + const hintedCoinStates = await peer.getHintedCoinStates( + epochBasedHint, + false + ); const filteredCoinStates = hintedCoinStates.filter( (coinState) => coinState.coin.amount >= serverCoinCollateral @@ -254,7 +266,7 @@ export class ServerCoin { if (myIp) { blacklist.push(myIp); } - + const serverCoinPeers = await this.getAllEpochPeers(epoch, blacklist); if (Environment.DEBUG) { console.log("Server Coin Peers: ", serverCoinPeers); @@ -279,14 +291,45 @@ export class ServerCoin { (coin) => coin.epoch === currentEpoch ); + const dataStore = DataStore.from(this.storeId); + const rootHistory = await dataStore.getRootHistory(true); + const lastRoot = _.last(rootHistory); + + if (!lastRoot) { + throw new Error('Cant get the last root on chain, something is wrong.'); + } + if (existingCoin) { + // nothing to do + if (lastRoot?.synced && lastRoot?.root_hash === existingCoin.rootHash) { + return; + } + + // everything is fine, lets just update the roothash for tracking + if (lastRoot?.synced && lastRoot?.root_hash !== existingCoin.rootHash) { + existingCoin.rootHash = lastRoot.root_hash; + // Update the conf with the modified server coin + await ServerCoin.serverCoinManager.setConfigValue( + `${this.storeId}:${peerIp}`, + serverCoins + ); + return; + } + + // If not synced, melt the coin, a new one will be created when synced up + // this helps prevent penalties for not having a valid peer registered + await this.melt(currentEpoch, peerIp); + } + + // Don't create a server coin until you're synced up + if (!_.last(rootHistory)?.synced) { return; } console.log( `No server coin found for epoch ${currentEpoch}. Creating new server coin...` ); - const serverCoin = await this.createForEpoch(peerIp); + const serverCoin = await this.createForEpoch(peerIp, lastRoot?.root_hash); const newServerCoinData: ServerCoinData = { coin: { @@ -296,6 +339,7 @@ export class ServerCoin { }, createdAt: new Date().toISOString(), epoch: currentEpoch, + rootHash: _.last(rootHistory)?.root_hash || "", }; await FullNodePeer.waitForConfirmation(serverCoin.coin.parentCoinInfo); diff --git a/src/types.ts b/src/types.ts index ebae3f0..4f6ad1d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,6 +58,7 @@ export interface ServerCoinData { coin: CoinData; createdAt: string; // ISO date string epoch: number; + rootHash: string; } export interface IncentiveProgramData {