Cleanup, improve cli

This commit is contained in:
Andrew Morris
2023-05-22 14:37:28 +10:00
parent 7ae8e59747
commit 07095b45b5
12 changed files with 100 additions and 109 deletions

7
.vscode/deno.jsonc vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"lint": {
"rules": {
"exclude": ["require-await"]
}
}
}

44
cli.ts
View File

@@ -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();

View File

@@ -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
View 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
View 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
View 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
View File

@@ -0,0 +1,5 @@
export default function todo(screenName: string) {
return async () => {
console.log(`TODO: ${screenName} screen`);
};
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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));
}