diff --git a/CHANGELOG.md b/CHANGELOG.md index d537278..6b552e6 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.94](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.93...v0.0.1-alpha.94) (2024-09-26) + + +### Features + +* add Environment class ([f9c467c](https://github.com/DIG-Network/dig-chia-sdk/commit/f9c467c0611bf4f865f4c9a37cd20890c6e73c4c)) + +### [0.0.1-alpha.93](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.92...v0.0.1-alpha.93) (2024-09-26) + + +### Features + +* add Environment class ([368d298](https://github.com/DIG-Network/dig-chia-sdk/commit/368d298261cc0ad061ee77647c3b3abefe404bde)) + ### [0.0.1-alpha.92](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.91...v0.0.1-alpha.92) (2024-09-26) diff --git a/package-lock.json b/package-lock.json index 487a9eb..be1868f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.92", + "version": "0.0.1-alpha.94", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.92", + "version": "0.0.1-alpha.94", "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.25", diff --git a/package.json b/package.json index 142ae6b..28b8114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.92", + "version": "0.0.1-alpha.94", "description": "", "type": "commonjs", "main": "./dist/index.js", diff --git a/src/blockchain/FullNodePeer.ts b/src/blockchain/FullNodePeer.ts index 0a9ae30..4522636 100644 --- a/src/blockchain/FullNodePeer.ts +++ b/src/blockchain/FullNodePeer.ts @@ -8,6 +8,7 @@ import net from "net"; import { memoize } from "lodash"; import { createSpinner } from "nanospinner"; import { MIN_HEIGHT, MIN_HEIGHT_HEADER_HASH } from "../utils/config"; +import { Environment } from "../utils/Environment"; const FULLNODE_PORT = 8444; const LOCALHOST = "127.0.0.1"; @@ -70,7 +71,7 @@ export class FullNodePeer { * @returns {string | null} The valid IP address or null if invalid */ private static getTrustedFullNode(): string | null { - const trustedNodeIp = process.env.TRUSTED_FULLNODE || null; + const trustedNodeIp = Environment.TRUSTED_FULLNODE || null; if (trustedNodeIp && FullNodePeer.isValidIpAddress(trustedNodeIp)) { return trustedNodeIp; @@ -235,7 +236,7 @@ export class FullNodePeer { new Tls(certFile, keyFile); const peerIPs = await FullNodePeer.getPeerIPs(); - const trustedNodeIp = process.env.TRUSTED_FULLNODE || null; + const trustedNodeIp = Environment.TRUSTED_FULLNODE || null; const peers = await Promise.all( peerIPs.map(async (ip) => { @@ -243,9 +244,9 @@ export class FullNodePeer { // Allow override of the trusted fullnode port if the override exists let port = FULLNODE_PORT; if (trustedNodeIp && ip === trustedNodeIp) { - const trustedFullNodePort = process.env.TRUSTED_FULLNODE_PORT; + const trustedFullNodePort = Environment.TRUSTED_FULLNODE_PORT; if (trustedFullNodePort) { - port = parseInt(trustedFullNodePort); + port = trustedFullNodePort; } } try { diff --git a/src/blockchain/ServerCoin.ts b/src/blockchain/ServerCoin.ts index 3bbcf1a..ccf1428 100644 --- a/src/blockchain/ServerCoin.ts +++ b/src/blockchain/ServerCoin.ts @@ -16,6 +16,7 @@ import { CoinData, ServerCoinData } from "../types"; import { DataStore } from "./DataStore"; import NodeCache from "node-cache"; import { getPublicIpAddress } from '../utils/network'; +import { Environment } from "../utils/Environment"; const serverCoinCollateral = 300_000_000; @@ -215,7 +216,7 @@ export class ServerCoin { } } - if (process.env.DIG_DEBUG === "1") { + if (Environment.DEBUG) { console.log("Server Coin Peers: ", serverCoinPeers); } @@ -255,7 +256,7 @@ export class ServerCoin { } const serverCoinPeers = await this.getAllEpochPeers(epoch, blacklist); - if (process.env.DIG_DEBUG === "1") { + if (Environment.DEBUG) { console.log("Server Coin Peers: ", serverCoinPeers); } return _.sampleSize(serverCoinPeers, sampleSize); diff --git a/src/blockchain/coins.ts b/src/blockchain/coins.ts index e5f0a19..85cb651 100644 --- a/src/blockchain/coins.ts +++ b/src/blockchain/coins.ts @@ -9,6 +9,7 @@ import { import { Wallet } from "./Wallet"; import { MIN_HEIGHT, MIN_HEIGHT_HEADER_HASH } from "../utils/config"; import { FileCache } from "../utils/FileCache"; +import { Environment } from "../utils/Environment"; export const DEFAULT_FEE_COIN_COST = 64_000_000; @@ -94,7 +95,7 @@ export const selectUnspentCoins = async ( Buffer.from(MIN_HEIGHT_HEADER_HASH, "hex") ); - if (process.env.DIG_DEBUG === "1") { + if (Environment.DEBUG) { console.log("Unspent Coins:", coinsResp); // Debugging } @@ -105,14 +106,14 @@ export const selectUnspentCoins = async ( (coin) => !omitCoinIds.includes(getCoinId(coin).toString("hex")) ); - if (process.env.DIG_DEBUG === "1") { + if (Environment.DEBUG) { console.log("Unspent Coins after filtering:", filteredUnspentCoins); // Debugging } // Select coins needed for the transaction const selectedCoins = selectCoins(filteredUnspentCoins, feeBigInt + coinAmount); - if (process.env.DIG_DEBUG === "1") { + if (Environment.DEBUG) { console.log("Selected Coins:", selectedCoins); // Debugging } diff --git a/src/utils/Environment.ts b/src/utils/Environment.ts new file mode 100644 index 0000000..728caa7 --- /dev/null +++ b/src/utils/Environment.ts @@ -0,0 +1,95 @@ +import fs from "fs"; +import path from "path"; + +export class Environment { + // Helper to validate if a string is a valid IP address (IPv4) + private static isValidIp(ip: string): boolean { + const ipPattern = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + return ipPattern.test(ip); + } + + // Helper to validate if a string is a valid hostname or IP address + private static isValidHostnameOrIp(hostname: string): boolean { + // Hostname regex (simple, allows subdomains but not special characters) + const hostnamePattern = + /^(([a-zA-Z0-9](-*[a-zA-Z0-9])*)\.)*([a-zA-Z0-9](-*[a-zA-Z0-9])*)\.?$/; + return this.isValidIp(hostname) || hostnamePattern.test(hostname); + } + + // Helper to validate if a number is a valid port (between 1 and 65535) + private static isValidPort(port: number): boolean { + return port > 0 && port <= 65535; + } + + // Helper to validate if a string is a valid file path + private static isValidFilePath(filePath: string): boolean { + return fs.existsSync(filePath) && path.isAbsolute(filePath); + } + + // Helper to get and validate number env variables + private static getNumberEnvVar(envVar: string): number | undefined { + const value = process.env[envVar]; + const parsedValue = Number(value); + return !isNaN(parsedValue) ? parsedValue : undefined; + } + + // Static getter for DIG_USERNAME (string, non-empty) + static get DIG_USERNAME(): string | undefined { + const value = process.env["DIG_USERNAME"]; + return value && value.trim().length > 0 ? value : undefined; + } + + // Static getter for DIG_PASSWORD (string, non-empty) + static get DIG_PASSWORD(): string | undefined { + const value = process.env["DIG_PASSWORD"]; + return value && value.trim().length > 0 ? value : undefined; + } + + // Static getter for TRUSTED_FULLNODE (valid IP or hostname) + static get TRUSTED_FULLNODE(): string | undefined { + const value = process.env["TRUSTED_FULLNODE"]; + return value && this.isValidHostnameOrIp(value) ? value : undefined; + } + + // Static getter for TRUSTED_FULLNODE_PORT (valid port) + static get TRUSTED_FULLNODE_PORT(): number | undefined { + const port = this.getNumberEnvVar("TRUSTED_FULLNODE_PORT"); + return port && this.isValidPort(port) ? port : undefined; + } + + // Static getter for PUBLIC_IP (valid IP) + static get PUBLIC_IP(): string | undefined { + const value = process.env["PUBLIC_IP"]; + return value && this.isValidIp(value) ? value : undefined; + } + + // Static getter for DISK_SPACE_LIMIT_BYTES (number, optional) + static get DISK_SPACE_LIMIT_BYTES(): number | undefined { + return this.getNumberEnvVar("DISK_SPACE_LIMIT_BYTES"); + } + + // Static getter for MERCENARY_MODE (boolean: "true" or "false") + static get MERCENARY_MODE(): boolean | undefined { + const value = process.env["MERCENARY_MODE"]; + return value === "true" ? true : value === "false" ? false : undefined; + } + + // Static getter for MERCENARY_MODE (boolean: "true" or "false") + static get DEBUG(): boolean | undefined { + const value = process.env["DIG_DEBUG"]; + return value === "true" ? true : value === "false" ? false : undefined; + } + + // Static getter for DIG_FOLDER_PATH (valid file path) + static get DIG_FOLDER_PATH(): string | undefined { + const value = process.env["DIG_FOLDER_PATH"]; + return value && this.isValidFilePath(value) ? value : undefined; + } + + // Static getter for REMOTE_NODE (boolean: "1" or "0", mapped to true/false) + static get REMOTE_NODE(): boolean | undefined { + const value = process.env["REMOTE_NODE"]; + return value === "1" ? true : value === "0" ? false : undefined; + } +} diff --git a/src/utils/NconfManager.ts b/src/utils/NconfManager.ts index 269a6e1..27f9897 100644 --- a/src/utils/NconfManager.ts +++ b/src/utils/NconfManager.ts @@ -1,10 +1,10 @@ import nconf from "nconf"; import fs from "fs-extra"; import path from "path"; -import os from "os"; import { USER_DIR_PATH } from "./config"; +import { Environment } from "./Environment"; -const CONF_FOLDER_PATH = process.env.DIG_FOLDER_PATH || USER_DIR_PATH; +const CONF_FOLDER_PATH = Environment.DIG_FOLDER_PATH || USER_DIR_PATH; export class NconfManager { private configFilePath: string; diff --git a/src/utils/config.ts b/src/utils/config.ts index 42b6763..33fa406 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -4,6 +4,7 @@ import { DigConfig } from "../types"; import { Config } from "../types"; import inquirer from "inquirer"; import os from "os"; +import { Environment } from "./Environment"; export const NETWORK_AGG_SIG_DATA = "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb"; @@ -15,15 +16,13 @@ export const MIN_HEIGHT_HEADER_HASH = "b29a4daac2434fd17a36e15ba1aac5d65012d4a66f99bed0bf2b5342e92e562c"; export const DIG_FOLDER_PATH = - process.env.DIG_FOLDER_PATH || path.join(process.cwd(), ".dig"); + Environment.DIG_FOLDER_PATH || path.join(process.cwd(), ".dig"); export const STORE_PATH = path.join(DIG_FOLDER_PATH, "stores"); export const USER_DIR_PATH = path.join(os.homedir(), ".dig"); export const CONFIG_FILE_PATH = path.join(DIG_FOLDER_PATH, "dig.config.json"); - - export const getHeightFilePath = (storeId: string): string => path.join(STORE_PATH, storeId, "height.json"); @@ -95,9 +94,9 @@ export const getCoinState = ( }; /** - * Retrieves the list of valid store folders. + * Retrieves the list of valid store folders. * If the STORE_PATH directory does not exist, it is created. - * + * * @returns {string[]} An array of valid store folder names. */ export const getStoresList = (): string[] => { @@ -140,7 +139,7 @@ export const getActiveStoreId = async (): Promise => { const validFolders = getStoresList(); - if (validFolders.length === 1 || process.env.REMOTE_NODE === "1") { + if (validFolders.length === 1 || Environment.REMOTE_NODE) { // If only one valid folder exists, set it as the active_store and return it config.active_store = validFolders[0]; fs.writeFileSync(configFilePath, JSON.stringify(config, null, 4)); @@ -188,7 +187,7 @@ export const ensureDigConfig = (baseDir: string): DigConfig => { fs.writeFileSync( CONFIG_FILE_PATH, JSON.stringify(defaultConfig, null, 4), - "utf-8", + "utf-8" ); console.log(`Created new dig.config.json at ${CONFIG_FILE_PATH}`); return defaultConfig; diff --git a/src/utils/network.ts b/src/utils/network.ts index c0996a1..3caa444 100644 --- a/src/utils/network.ts +++ b/src/utils/network.ts @@ -2,6 +2,7 @@ * Stopgap until better solution for finding public IPS found */ import superagent from "superagent"; +import { Environment } from "./Environment"; const MAX_RETRIES = 5; const RETRY_DELAY = 2000; // in milliseconds @@ -18,7 +19,7 @@ const isValidIp = (ip: string): boolean => { }; export const getPublicIpAddress = async (): Promise => { - const publicIp = process.env.PUBLIC_IP; + const publicIp = Environment.PUBLIC_IP; if (publicIp) { console.log("Public IP address from env:", publicIp);