mirror of
https://github.com/voltrevo/seed-splitter.git
synced 2026-01-09 21:18:06 -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([
|
import main from "./src/cli/main.ts";
|
||||||
"===========================================================================",
|
|
||||||
"============================== 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"));
|
|
||||||
|
|
||||||
let choice: "1" | "2" | "3" | "4";
|
await main();
|
||||||
|
|
||||||
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 });
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import RecoveryCurve from "./src/RecoveryCurve.ts";
|
import SeedSplitter from "./mod.ts";
|
||||||
|
|
||||||
// const secret =
|
// const secret =
|
||||||
// "expire gun route tornado female reflect holiday grief spring clown deliver army";
|
// "expire gun route tornado female reflect holiday grief spring clown deliver army";
|
||||||
|
|
||||||
// const curve = await RecoveryCurve.fit([
|
// const curve = await SeedSplitter.fit([
|
||||||
// {
|
// {
|
||||||
// label: "secret",
|
// label: "secret",
|
||||||
// mnemonic: secret.split(" "),
|
// mnemonic: secret.split(" "),
|
||||||
// },
|
// },
|
||||||
// await RecoveryCurve.randomPoint(),
|
// await SeedSplitter.randomPoint(),
|
||||||
// await RecoveryCurve.randomPoint(),
|
// await SeedSplitter.randomPoint(),
|
||||||
// ]);
|
// ]);
|
||||||
|
|
||||||
const recoveryData = `
|
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
|
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) => {
|
recoveryData.split("\n").map((line) => {
|
||||||
if (line.trim() === "") {
|
if (line.trim() === "") {
|
||||||
return [];
|
return [];
|
||||||
@@ -42,5 +42,5 @@ const names = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (const name of 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
|
const P = 2n ** 128n - 159n; // The largest prime below 2**128
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export default class Polynomial {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(x: FieldElement) {
|
calculate(x: FieldElement) {
|
||||||
if (this.coeffs.length === 0) {
|
if (this.coeffs.length === 0) {
|
||||||
return new FieldElement(0n);
|
return new FieldElement(0n);
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import FieldElement from "./FieldElement.ts";
|
import FieldElement from "./FieldElement.ts";
|
||||||
import Polynomial from "./Polynomial.ts";
|
import Polynomial from "./Polynomial.ts";
|
||||||
|
|
||||||
type Point = {
|
export type Point = {
|
||||||
label: string;
|
label: string;
|
||||||
mnemonic: string[];
|
mnemonic: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RecoveryCurve {
|
export default class SeedSplitter {
|
||||||
private constructor(public poly: Polynomial) {}
|
private constructor(public poly: Polynomial) {}
|
||||||
|
|
||||||
static async fit(points: Point[]) {
|
static async fit(points: Point[]) {
|
||||||
@@ -37,19 +37,19 @@ export default class RecoveryCurve {
|
|||||||
|
|
||||||
poly = poly.mul(
|
poly = poly.mul(
|
||||||
new Polynomial([
|
new Polynomial([
|
||||||
poly.eval(share.x).inverse().mul(share.y),
|
poly.calculate(share.x).inverse().mul(share.y),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
sum = sum.add(poly);
|
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 x = FieldElement.fromLabel(label);
|
||||||
const y = this.poly.eval(x);
|
const y = this.poly.calculate(x);
|
||||||
|
|
||||||
return await y.toMnemonic();
|
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