Initial commit: Upload Checkpoint project
This commit is contained in:
commit
c0e3781244
32 changed files with 6121 additions and 0 deletions
41
utils/logs.js
Normal file
41
utils/logs.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
const seenConfigs = new Set();
|
||||
|
||||
export function init(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
export function plugin(_name, msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
export function config(name, msg) {
|
||||
if (!seenConfigs.has(name)) {
|
||||
console.log(`Config ${msg} for ${name}`);
|
||||
seenConfigs.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
export function db(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
export function server(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
export function section(title) {
|
||||
console.log(`\n=== ${title.toUpperCase()} ===`);
|
||||
}
|
||||
|
||||
export function warn(_category, msg) {
|
||||
console.warn(`WARNING: ${msg}`);
|
||||
}
|
||||
|
||||
export function error(_category, msg) {
|
||||
console.error(`ERROR: ${msg}`);
|
||||
}
|
||||
|
||||
// General message function for bullet items
|
||||
export function msg(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
13
utils/network.js
Normal file
13
utils/network.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export function getRealIP(request, server) {
|
||||
let ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip');
|
||||
if (ip?.includes(',')) ip = ip.split(',')[0].trim();
|
||||
if (!ip && server) {
|
||||
ip = server.remoteAddress;
|
||||
}
|
||||
if (!ip) {
|
||||
const url = new URL(request.url);
|
||||
ip = url.hostname;
|
||||
}
|
||||
if (ip?.startsWith('::ffff:')) ip = ip.slice(7);
|
||||
return ip;
|
||||
}
|
||||
28
utils/plugins.js
Normal file
28
utils/plugins.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { resolve, extname, sep, isAbsolute } from 'path';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { rootDir } from '../index.js';
|
||||
|
||||
/**
|
||||
* Securely import a JavaScript module from within the application root.
|
||||
* Prevents path traversal and disallows non-.js extensions.
|
||||
*
|
||||
* @param {string} relPath - The relative path to the module from the application root.
|
||||
* @returns {Promise<any>} The imported module.
|
||||
*/
|
||||
export async function secureImportModule(relPath) {
|
||||
if (isAbsolute(relPath)) {
|
||||
throw new Error('Absolute paths are not allowed for module imports');
|
||||
}
|
||||
if (relPath.includes('..')) {
|
||||
throw new Error('Relative paths containing .. are not allowed for module imports');
|
||||
}
|
||||
if (extname(relPath) !== '.js') {
|
||||
throw new Error(`Only .js files can be imported: ${relPath}`);
|
||||
}
|
||||
const absPath = resolve(rootDir, relPath);
|
||||
if (!absPath.startsWith(rootDir + sep)) {
|
||||
throw new Error(`Module path outside of application root: ${relPath}`);
|
||||
}
|
||||
const url = pathToFileURL(absPath).href;
|
||||
return import(url);
|
||||
}
|
||||
72
utils/proof.js
Normal file
72
utils/proof.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import crypto from 'crypto';
|
||||
import { getRealIP } from './network.js';
|
||||
|
||||
export function generateChallenge(checkpointConfig) {
|
||||
const challenge = crypto.randomBytes(16).toString('hex');
|
||||
const salt = crypto.randomBytes(checkpointConfig.SaltLength).toString('hex');
|
||||
return { challenge, salt };
|
||||
}
|
||||
|
||||
export function calculateHash(input) {
|
||||
return crypto.createHash('sha256').update(input).digest('hex');
|
||||
}
|
||||
|
||||
export function verifyPoW(challenge, salt, nonce, difficulty) {
|
||||
const hash = calculateHash(challenge + salt + nonce);
|
||||
return hash.startsWith('0'.repeat(difficulty));
|
||||
}
|
||||
|
||||
export function checkPoSTimes(times, enableCheck, ratio) {
|
||||
if (!Array.isArray(times) || times.length !== 3) {
|
||||
throw new Error('Invalid PoS run times length');
|
||||
}
|
||||
const minT = Math.min(...times);
|
||||
const maxT = Math.max(...times);
|
||||
if (enableCheck && maxT > minT * ratio) {
|
||||
throw new Error(`PoS run times inconsistent (ratio ${maxT / minT} > ${ratio})`);
|
||||
}
|
||||
}
|
||||
|
||||
export const challengeStore = new Map();
|
||||
|
||||
export function generateRequestID(request, checkpointConfig) {
|
||||
const { challenge, salt } = generateChallenge(checkpointConfig);
|
||||
const posSeed = crypto.randomBytes(32).toString('hex');
|
||||
const requestID = crypto.randomBytes(16).toString('hex');
|
||||
const params = {
|
||||
Challenge: challenge,
|
||||
Salt: salt,
|
||||
Difficulty: checkpointConfig.Difficulty,
|
||||
ExpiresAt: Date.now() + checkpointConfig.ChallengeExpiration,
|
||||
CreatedAt: Date.now(),
|
||||
ClientIP: getRealIP(request),
|
||||
PoSSeed: posSeed,
|
||||
};
|
||||
challengeStore.set(requestID, params);
|
||||
return requestID;
|
||||
}
|
||||
|
||||
export function getChallengeParams(requestID) {
|
||||
return challengeStore.get(requestID);
|
||||
}
|
||||
|
||||
export function deleteChallenge(requestID) {
|
||||
challengeStore.delete(requestID);
|
||||
}
|
||||
|
||||
export function verifyPoS(hashes, times, checkpointConfig) {
|
||||
if (!Array.isArray(hashes) || hashes.length !== 3) {
|
||||
throw new Error('Invalid PoS hashes length');
|
||||
}
|
||||
if (!Array.isArray(times) || times.length !== 3) {
|
||||
throw new Error('Invalid PoS run times length');
|
||||
}
|
||||
if (hashes[0] !== hashes[1] || hashes[1] !== hashes[2]) {
|
||||
throw new Error('PoS hashes do not match');
|
||||
}
|
||||
if (hashes[0].length !== 64) {
|
||||
throw new Error('Invalid PoS hash length');
|
||||
}
|
||||
|
||||
checkPoSTimes(times, checkpointConfig.CheckPoSTimes, checkpointConfig.PoSTimeConsistencyRatio);
|
||||
}
|
||||
20
utils/time.js
Normal file
20
utils/time.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export function parseDuration(str) {
|
||||
if (!str) return 0;
|
||||
const m = /^([0-9]+)(ms|s|m|h|d)$/.exec(str);
|
||||
if (!m) return 0;
|
||||
const val = parseInt(m[1], 10);
|
||||
switch (m[2]) {
|
||||
case 'ms':
|
||||
return val;
|
||||
case 's':
|
||||
return val * 1000;
|
||||
case 'm':
|
||||
return val * 60 * 1000;
|
||||
case 'h':
|
||||
return val * 60 * 60 * 1000;
|
||||
case 'd':
|
||||
return val * 24 * 60 * 60 * 1000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue