Skip to content

Instantly share code, notes, and snippets.

@tundachef
Created October 23, 2025 13:13
Show Gist options
  • Select an option

  • Save tundachef/485c26d61641fc88cf0dad5ca9d28be3 to your computer and use it in GitHub Desktop.

Select an option

Save tundachef/485c26d61641fc88cf0dad5ca9d28be3 to your computer and use it in GitHub Desktop.
// searchPublicKeysFromDir.ts
// Usage:
// ts-node searchPublicKeysFromDir.ts --dir ./data
// # or compile first: tsc searchPublicKeysFromDir.ts && node dist/searchPublicKeysFromDir.js --dir ./data
//
// What it does:
// - Reads all .json files in the given folder
// - Searches for any record with a `publicKey` matching one from publicKeysToFind
// - Intentionally slow (configurable delays + jitter)
// - Writes results to ./search-results.json
import { promises as fs } from "fs";
import * as path from "path";
// ====== 1) PUT YOUR PUBLIC KEYS HERE (exact matches) ======
const publicKeysToFind: string[] = [
"3NRuvg1SghhNbQHTMmFVPCGX88zvS41YuuchFu5LpFm2",
// "9kf7oyNPHZB7TWcZZRewFMFwGNDKSEZKSSumMdaRYiuv",
// "BfrgGRQHaY7jXmGQUNa5zC3xAidJ9rEZ8jr32CyP2hXQ",
];
// ====== 2) SLOWDOWN SETTINGS (tweak as you like) ======
const delayPerFileMs = 800; // wait this long before processing each file
const perItemDelayMs = 120; // wait this long between checking each record
const jitterMs = 50; // +/- jitter to avoid uniform timing
const recursive = false; // set true to scan subfolders too
// ====== Types ======
type KeyRecord = { publicKey?: string; privateKey?: string; [k: string]: any };
type SearchResult = { file: string; index: number; record: KeyRecord };
// ====== Helpers ======
function sleep(ms: number) {
return new Promise((res) => setTimeout(res, Math.max(0, Math.floor(ms))));
}
function randomJitter() {
return Math.floor((Math.random() * 2 - 1) * jitterMs);
}
function getArgValue(flag: string): string | null {
const idx = process.argv.indexOf(flag);
if (idx >= 0 && process.argv[idx + 1]) return process.argv[idx + 1];
return null;
}
// ====== Resolve directory to scan ======
const ROOT_DIR = getArgValue("--dir") ?? "./data";
// Build a Set for O(1) membership checks
const targets = new Set(publicKeysToFind.filter(Boolean));
// ====== File discovery ======
async function listJsonFiles(dir: string): Promise<string[]> {
const out: string[] = [];
async function walk(current: string) {
let entries: any[];
try {
entries = await fs.readdir(current, { withFileTypes: true });
} catch (err) {
console.error(`[ERR] cannot read dir ${current}: ${(err as Error).message}`);
return;
}
for (const entry of entries) {
const full = path.join(current, entry.name);
if (entry.isDirectory()) {
if (recursive) await walk(full);
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".json")) {
out.push(full);
}
}
}
await walk(path.resolve(dir));
return out;
}
// ====== Inspect one JSON file ======
async function inspectFile(filePath: string): Promise<SearchResult[]> {
const results: SearchResult[] = [];
let raw: string;
try {
raw = await fs.readFile(filePath, "utf8");
} catch (err) {
console.error(`[ERR] cannot read ${filePath}: ${(err as Error).message}`);
return results;
}
// Slow before-parse delay per file
await sleep(delayPerFileMs + randomJitter());
let json: any;
try {
json = JSON.parse(raw);
} catch (err) {
console.error(`[ERR] invalid JSON in ${filePath}: ${(err as Error).message}`);
return results;
}
// Accept either an array or a common wrapper object
const candidates: KeyRecord[] = Array.isArray(json) ? json : (json.records || json.keys || json.items || []);
if (!Array.isArray(candidates)) {
console.warn(`[WARN] ${filePath} has no array at root/records/keys/items; skipping.`);
return results;
}
for (let i = 0; i < candidates.length; i++) {
const item = candidates[i];
// Slow per-item delay
await sleep(perItemDelayMs + randomJitter());
if (!item || typeof item !== "object") continue;
// Try common field names
const pub =
(item.publicKey ??
item.pub ??
item.address ??
item.owner) as string | undefined;
if (!pub) continue;
if (targets.has(pub)) {
results.push({ file: filePath, index: i, record: item });
// If you want to stop on first match per file, uncomment next line:
// break;
}
}
return results;
}
// ====== Main ======
async function main() {
if (targets.size === 0) {
console.error("No public keys in publicKeysToFind. Add at least one and retry.");
process.exit(2);
}
console.log(`Dir: ${ROOT_DIR}`);
console.log(`Scanning for ${targets.size} publicKey(s):`);
for (const k of targets) console.log(` - ${k}`);
console.log("");
const files = await listJsonFiles(ROOT_DIR);
if (files.length === 0) {
console.log("No .json files found.");
return;
}
console.log(`Found ${files.length} JSON file(s). Starting slow search...\n`);
const allResults: SearchResult[] = [];
// Serial on purpose (slow). Change to Promise.all for concurrent (still per-item slow).
for (const f of files) {
const res = await inspectFile(f);
if (res.length) {
console.log(`[FOUND] ${res.length} match(es) in ${f}`);
for (const r of res) {
console.log(` - index ${r.index}: ${JSON.stringify(r.record)}`);
}
} else {
console.log(`[OK] no matches in ${f}`);
}
allResults.push(...res);
}
console.log("\nSearch complete.");
console.log(`Total matches: ${allResults.length}`);
try {
await fs.writeFile("./search-results.json", JSON.stringify(allResults, null, 2), "utf8");
console.log("Wrote ./search-results.json");
} catch (err) {
console.warn("Could not write results file:", (err as Error).message);
}
}
main().catch((err) => {
console.error("Fatal:", (err as Error).message);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment