diff --git a/packages/proof-of-work/.eslintrc.json b/packages/proof-of-work/.eslintrc.json new file mode 100644 index 0000000..86a3ee8 --- /dev/null +++ b/packages/proof-of-work/.eslintrc.json @@ -0,0 +1,17 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "plugins": [ + "@typescript-eslint", + "prettier" + ], + "rules": { + "prettier/prettier": "error" + } +} diff --git a/packages/proof-of-work/README.md b/packages/proof-of-work/README.md new file mode 100644 index 0000000..e69de29 diff --git a/packages/proof-of-work/package.json b/packages/proof-of-work/package.json new file mode 100644 index 0000000..0868e48 --- /dev/null +++ b/packages/proof-of-work/package.json @@ -0,0 +1,61 @@ +{ + "name": "@ralphschuler/proof-of-work", + "version": "1.0.0", + "author": { + "name": "Ralph Schuler", + "email": "ralph.schuler@nyphon.de" + }, + "bugs": { + "url": "https://github.com/ralphschuler/ts-libs/issues" + }, + "devDependencies": { + "@types/node": "^20.7.1", + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3", + "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.3", + "rimraf": "^5.0.5", + "typescript": "^5.2.2" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "homepage": "https://ralphschuler.github.io/ts-libs", + "keywords": [ + "helpers", + "library", + "tools", + "typescript", + "utils" + ], + "license": "MIT", + "main": "dist/index.js", + "module": "dist/index.js", + "repository": "https://github.com/ralphschuler/ts-libs", + "scripts": { + "build": "tsc", + "clean": "rimraf dist node_modules docs", + "docs": "typedoc --entryPointStrategy expand --json docs/docs.json --plugin typedoc-plugin-missing-exports --plugin typedoc-plugin-rename-defaults --plugin typedoc-plugin-mdn-links --readme ./README.md src/", + "fixpack": "fixpack", + "format": "prettier src/", + "format:fix": "prettier --write src/", + "lint": "eslint src/ --ext .ts", + "lint:fix": "eslint src/ --ext .ts --fix", + "lint:report": "eslint --output-file eslint_report.json --format json src/ --ext .ts", + "prepublish": "yarn build", + "publish": "yarn npm publish", + "readme:build": "pkg-to-readme -f -t ../../_readme.md", + "start": "tsx src/index.ts", + "test": "echo \"Error: no test specified\" && exit 0" + }, + "type": "module", + "types": "dist/index.d.ts" +} diff --git a/packages/proof-of-work/src/index.ts b/packages/proof-of-work/src/index.ts new file mode 100644 index 0000000..62b27e3 --- /dev/null +++ b/packages/proof-of-work/src/index.ts @@ -0,0 +1,61 @@ +import { SHA256 } from "crypto-js"; + +class ProofOfWork { + private static hash(data: string, nonce: number): string { + return SHA256(data + nonce).toString(); + } + + public static async mine( + data: string, + difficulty: number, + prefix: string = "0", + ): Promise<[number, string]> { + if (difficulty < 0) { + throw new Error("Difficulty must be a non-negative number."); + } + + let nonce = 0; + let hash: string; + const target = prefix.repeat(difficulty); + + do { + nonce++; + hash = this.hash(data, nonce); + } while (!hash.startsWith(target)); + + return [nonce, hash]; + } + + public static validate( + data: string, + nonce: number, + difficulty: number, + hash: string, + prefix: string = "0", + ): boolean { + if (difficulty < 0) { + throw new Error("Difficulty must be a non-negative number."); + } + + const validHash = this.hash(data, nonce); + return validHash === hash && hash.startsWith(prefix.repeat(difficulty)); + } +} + +// Example usage +(async () => { + try { + const data = "Hello, Proof of Work!"; + const difficulty = 4; // Number of leading zeroes required in the hash + const [nonce, hash] = await ProofOfWork.mine(data, difficulty); + + console.log(`Nonce: ${nonce}`); + console.log(`Hash: ${hash}`); + + // Validate the proof of work + const isValid = ProofOfWork.validate(data, nonce, difficulty, hash); + console.log(`Is valid: ${isValid}`); + } catch (error) { + console.error(error); + } +})(); diff --git a/packages/proof-of-work/tsconfig.json b/packages/proof-of-work/tsconfig.json new file mode 100644 index 0000000..f9a0911 --- /dev/null +++ b/packages/proof-of-work/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "rootDir": "src/", + "outDir": "./dist", + }, + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/packages/proof-of-work/typedoc.json b/packages/proof-of-work/typedoc.json new file mode 100644 index 0000000..f593f27 --- /dev/null +++ b/packages/proof-of-work/typedoc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"] +}