mirror of
https://github.com/voltrevo/seed-splitter.git
synced 2026-01-08 20:48:08 -05:00
Cleanup, improve cli
This commit is contained in:
7
.vscode/deno.jsonc
vendored
Normal file
7
.vscode/deno.jsonc
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"lint": {
|
||||
"rules": {
|
||||
"exclude": ["require-await"]
|
||||
}
|
||||
}
|
||||
}
|
||||
44
cli.ts
44
cli.ts
@@ -1,43 +1,3 @@
|
||||
console.log([
|
||||
"===========================================================================",
|
||||
"============================== Seed Splitter ==============================",
|
||||
"===========================================================================",
|
||||
"",
|
||||
"This is a tool which splits 12-word seed phrases.",
|
||||
"",
|
||||
"It does this by fitting a polynomial to K points of your choosing. Once",
|
||||
"this polynomial has been found, you can then calculate any other points you",
|
||||
"like.",
|
||||
"",
|
||||
"Later, you can recalculate the same polynomial using any K of these points,",
|
||||
"not just the ones that originally generated the polynomial. With this same",
|
||||
"polynomial, you can recalculate any of the other points.",
|
||||
"",
|
||||
" 1. Split an existing seed phrase",
|
||||
" 2. Generate and split a seed phrase",
|
||||
" 3. Recover a seed phrase",
|
||||
" 4. Manual mode",
|
||||
"",
|
||||
].join("\n"));
|
||||
import main from "./src/cli/main.ts";
|
||||
|
||||
let choice: "1" | "2" | "3" | "4";
|
||||
|
||||
const exitInputs: (string | null)[] = ["exit", "quit", "stop"];
|
||||
|
||||
while (true) {
|
||||
const input = prompt("Choice:");
|
||||
|
||||
if (exitInputs.includes(input)) {
|
||||
Deno.exit(0);
|
||||
}
|
||||
|
||||
if (input !== "1" && input !== "2" && input !== "3" && input !== "4") {
|
||||
console.log("Please choose 1, 2, 3, or 4");
|
||||
continue;
|
||||
} else {
|
||||
choice = input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log({ choice });
|
||||
await main();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import RecoveryCurve from "./src/RecoveryCurve.ts";
|
||||
import SeedSplitter from "./mod.ts";
|
||||
|
||||
// const secret =
|
||||
// "expire gun route tornado female reflect holiday grief spring clown deliver army";
|
||||
|
||||
// const curve = await RecoveryCurve.fit([
|
||||
// const curve = await SeedSplitter.fit([
|
||||
// {
|
||||
// label: "secret",
|
||||
// mnemonic: secret.split(" "),
|
||||
// },
|
||||
// await RecoveryCurve.randomPoint(),
|
||||
// await RecoveryCurve.randomPoint(),
|
||||
// await SeedSplitter.randomPoint(),
|
||||
// await SeedSplitter.randomPoint(),
|
||||
// ]);
|
||||
|
||||
const recoveryData = `
|
||||
@@ -18,7 +18,7 @@ daniel: long neutral reject ahead cart proud shoulder auction april same solar m
|
||||
emily: come foam country senior awkward task possible auction spice hidden sphere giraffe
|
||||
`;
|
||||
|
||||
const curve = await RecoveryCurve.fit(
|
||||
const curve = await SeedSplitter.fit(
|
||||
recoveryData.split("\n").map((line) => {
|
||||
if (line.trim() === "") {
|
||||
return [];
|
||||
@@ -42,5 +42,5 @@ const names = [
|
||||
];
|
||||
|
||||
for (const name of names) {
|
||||
console.log(`${name}:`, (await curve.eval(name)).join(" "));
|
||||
console.log(`${name}:`, (await curve.calculate(name)).join(" "));
|
||||
}
|
||||
|
||||
2
mod.ts
Normal file
2
mod.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default } from "./src/seed-splitter/SeedSplitter.ts";
|
||||
export * from "./src/seed-splitter/SeedSplitter.ts";
|
||||
32
src/cli/main.ts
Normal file
32
src/cli/main.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import promptChoices from "./promptChoices.ts";
|
||||
import todo from "./todo.ts";
|
||||
|
||||
export default async function main() {
|
||||
console.log([
|
||||
"=========================================================================",
|
||||
"============================= Seed Splitter =============================",
|
||||
"=========================================================================",
|
||||
"",
|
||||
"This is a tool which splits 12-word seed phrases.",
|
||||
"",
|
||||
"It does this by fitting a polynomial to K points of your choosing. Once",
|
||||
"this polynomial has been found, you can then calculate any other points",
|
||||
"you like.",
|
||||
"",
|
||||
"Later, you can recalculate the same polynomial using any K of these",
|
||||
"points, not just the ones that originally generated the polynomial. With",
|
||||
"this same polynomial, you can recalculate any of the other points.",
|
||||
"",
|
||||
].join("\n"));
|
||||
|
||||
const choice = promptChoices([
|
||||
["Split an existing seed phrase", todo("splitExisting")],
|
||||
["Generate and split a seed phrase", todo("generateAndSplit")],
|
||||
["Recover a seed phrase", todo("recover")],
|
||||
["Manual mode", todo("manual")],
|
||||
]);
|
||||
|
||||
console.clear();
|
||||
|
||||
await choice();
|
||||
}
|
||||
38
src/cli/promptChoices.ts
Normal file
38
src/cli/promptChoices.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
type Choice<T> = [
|
||||
description: string,
|
||||
value: T,
|
||||
];
|
||||
|
||||
export default function promptChoices<T>(choices: Choice<T>[]) {
|
||||
for (const [i, [description]] of choices.entries()) {
|
||||
console.log(` ${i + 1}. ${description}`);
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
while (true) {
|
||||
const input = prompt("Choice:");
|
||||
|
||||
if (input !== null && ["exit", "quit", "stop"].includes(input)) {
|
||||
Deno.exit(0);
|
||||
}
|
||||
|
||||
const inputNum = parseInt(input ?? "x", 10);
|
||||
|
||||
const choice = choices.at(inputNum - 1);
|
||||
|
||||
if (
|
||||
input === null ||
|
||||
!/^ *[1-9][0-9]* *$/.test(input) ||
|
||||
choice === undefined
|
||||
) {
|
||||
console.log(
|
||||
`Please enter a number from 1 to ${choices.length} (or 'exit')`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return choice[1];
|
||||
}
|
||||
}
|
||||
5
src/cli/todo.ts
Normal file
5
src/cli/todo.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function todo(screenName: string) {
|
||||
return async () => {
|
||||
console.log(`TODO: ${screenName} screen`);
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import bip39WordList from "./bip39Wordlist.ts";
|
||||
import bip39WordList from "./bip39WordList.ts";
|
||||
|
||||
const P = 2n ** 128n - 159n; // The largest prime below 2**128
|
||||
|
||||
@@ -35,7 +35,7 @@ export default class Polynomial {
|
||||
return res;
|
||||
}
|
||||
|
||||
eval(x: FieldElement) {
|
||||
calculate(x: FieldElement) {
|
||||
if (this.coeffs.length === 0) {
|
||||
return new FieldElement(0n);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import FieldElement from "./FieldElement.ts";
|
||||
import Polynomial from "./Polynomial.ts";
|
||||
|
||||
type Point = {
|
||||
export type Point = {
|
||||
label: string;
|
||||
mnemonic: string[];
|
||||
};
|
||||
|
||||
export default class RecoveryCurve {
|
||||
export default class SeedSplitter {
|
||||
private constructor(public poly: Polynomial) {}
|
||||
|
||||
static async fit(points: Point[]) {
|
||||
@@ -37,19 +37,19 @@ export default class RecoveryCurve {
|
||||
|
||||
poly = poly.mul(
|
||||
new Polynomial([
|
||||
poly.eval(share.x).inverse().mul(share.y),
|
||||
poly.calculate(share.x).inverse().mul(share.y),
|
||||
]),
|
||||
);
|
||||
|
||||
sum = sum.add(poly);
|
||||
}
|
||||
|
||||
return new RecoveryCurve(sum);
|
||||
return new SeedSplitter(sum);
|
||||
}
|
||||
|
||||
async eval(label: string) {
|
||||
async calculate(label: string) {
|
||||
const x = FieldElement.fromLabel(label);
|
||||
const y = this.poly.eval(x);
|
||||
const y = this.poly.calculate(x);
|
||||
|
||||
return await y.toMnemonic();
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import FieldElement from "./FieldElement.ts";
|
||||
import Polynomial from "./Polynomial.ts";
|
||||
|
||||
type Point = { x: FieldElement; y: FieldElement };
|
||||
|
||||
export function generate(
|
||||
secret: FieldElement,
|
||||
k: number,
|
||||
n: number,
|
||||
): Point[] {
|
||||
const poly = new Polynomial([
|
||||
secret,
|
||||
...[...new Array(k - 1)].map(() => FieldElement.random()),
|
||||
]);
|
||||
|
||||
const shares = [...new Array(n)].map((_, i) => {
|
||||
const x = new FieldElement(BigInt(i) + 1n);
|
||||
return { x, y: poly.eval(x) };
|
||||
});
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
export function recover(shares: Point[]) {
|
||||
let sum = new Polynomial([]);
|
||||
|
||||
for (const share of shares) {
|
||||
let poly = new Polynomial([new FieldElement(1n)]);
|
||||
|
||||
for (const otherShare of shares) {
|
||||
if (otherShare === share) {
|
||||
continue;
|
||||
}
|
||||
|
||||
poly = poly.mul(
|
||||
new Polynomial([
|
||||
otherShare.x,
|
||||
new FieldElement(1n).negate(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
poly = poly.mul(
|
||||
new Polynomial([
|
||||
poly.eval(share.x).inverse().mul(share.y),
|
||||
]),
|
||||
);
|
||||
|
||||
sum = sum.add(poly);
|
||||
}
|
||||
|
||||
return sum.eval(new FieldElement(0n));
|
||||
}
|
||||
Reference in New Issue
Block a user