Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
lagrangeInterpolation,
} from "./lagrangeInterpolatePolynomial";
import Metadata from "./metadata";
import { eqSet, isSome } from "./util";

// TODO: handle errors for get and set with retries

Expand Down Expand Up @@ -1216,7 +1217,41 @@ class ThresholdKey implements ITKey {
} else {
throw CoreError.default("can only add type ShareStore into shares");
}

// get current metadata poly id
// const existingPolyShares = this.metadata.polyIDList;
// const existingPolyIDs = [];
// existingPolyShares.map((polyList) => existingPolyIDs.push(polyList[0]));

// // get input metadata's poly id
// const newShareMetadata = await this.storageLayer.getMetadata({ privKey: ss.share.share as BN });
// const parseNewMeta = Metadata.fromJSON(JSON.parse(stringify((newShareMetadata as any).data))) as Metadata;
// const newPolyIDs = [];
// const newShareIndexes = new Set<string>();
// parseNewMeta.polyIDList.map((polyList) => {
// newPolyIDs.push(polyList[0]);
// return polyList[1].forEach((item) => newShareIndexes.add(item));
// });

// // check if poly id list is outdated
// if (existingPolyIDs.length !== newPolyIDs.length && isSome(existingPolyIDs, newPolyIDs)) {
// // catchup to latest tKey
// await this.updateSDK();
// } else {
// const existingShareIndexes = new Set<string>();
// this.metadata.polyIDList.map((polyList) => polyList[1].forEach((item) => existingShareIndexes.add(item)));

// // make sure share indexes of input share's metadata and existing are same
// if (!eqSet(newShareIndexes, existingShareIndexes)) {
// // throw error if share index is random
// throw CoreError.fromCode(1307);
// }
// }

const latestShareRes = await this.catchupToLatestShare({ shareStore: ss, includeLocalMetadataTransitions: true });
if (!latestShareRes.shareMetadata.polyIDList.find((tuple) => tuple[0] === this.metadata.getLatestPublicPolynomial().getPolynomialID())) {
throw CoreError.fromCode(1307);
}
// if not in poly id list, metadata is probably outdated
// is !this.metadata.polyIDList.includes(latestShareRes.latestShare.polynomialID)
if (!this.metadata.polyIDList.find((tuple) => tuple[0] === latestShareRes.latestShare.polynomialID)) {
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compare two arrays of string without order
export function isSome(array1: string[], array2: string[]) {
return array1.some(function (element, index) {
return element === array2[index];
});
}

export function eqSet(xs: Set<string>, ys: Set<string>) {
return xs.size === ys.size && [...xs].every((x) => ys.has(x));
}
122 changes: 102 additions & 20 deletions packages/default/test/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: sp, storageLayer, manualSync: mode });
await tb2.initialize({ useTSS: true, factorPub });
tb2.inputShareStore(newShare.newShareStores[newShare.newShareIndex.toString("hex")]);
await tb2.inputShareStoreSafe(newShare.newShareStores[newShare.newShareIndex.toString("hex")]);
await tb2.reconstructKey();
const { tssShare: retrievedTSS, tssIndex: retrievedTSSIndex } = await tb2.getTSSShare(factorKey);
const tssCommits = tb2.getTSSCommits();
Expand Down Expand Up @@ -739,7 +739,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize({ neverInitializeNewKey: true });
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -753,7 +753,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize();
tb2.inputShareStore(resp1.userShare);
await tb2.inputShareStoreSafe(resp1.userShare);
const reconstructedKey = await tb2.reconstructKey();
// compareBNArray(resp1.privKey, reconstructedKey, "key should be able to be reconstructed");
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
Expand All @@ -767,12 +767,13 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
if (importedKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
}
});

it(`#should be able to reconstruct key when initializing a with a share, manualSync=${mode}`, async function () {
let userInput = new BN(keccak256("user answer blublu").slice(2), "hex");
userInput = userInput.umod(ecCurve.curve.n);
Expand All @@ -781,7 +782,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize({ withShare: resp1.userShare });
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -796,7 +797,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize({ withShare: resp1.userShare });
tb2.inputShareStore(newShares.newShareStores[newShares.newShareIndex.toString("hex")]);
await tb2.inputShareStoreSafe(newShares.newShareStores[newShares.newShareIndex.toString("hex")]);
const reconstructedKey = await tb2.reconstructKey();
// compareBNArray(resp1.privKey, reconstructedKey, "key should be able to be reconstructed");
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
Expand All @@ -812,7 +813,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize();
tb2.inputShareStore(newShares.newShareStores[newShares.newShareIndex.toString("hex")]);
await tb2.inputShareStoreSafe(newShares.newShareStores[newShares.newShareIndex.toString("hex")]);
const reconstructedKey = await tb2.reconstructKey();
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -828,7 +829,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
await tb.generateNewShare(); // generate new share to update metadata
await tb.syncLocalMetadataTransitions();

tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare, true);
const reconstructedKey = await tb2.reconstructKey(); // reconstruct key with old metadata should work to poly
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -848,7 +849,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize({ neverInitializeNewKey: true });
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
const shareStore = tb2.outputShareStore(newShareIndex);
strictEqual(newShareStores[newShareIndex.toString("hex")].share.share.toString("hex"), shareStore.share.share.toString("hex"));
Expand All @@ -857,14 +858,96 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
fail("key should be able to be reconstructed");
}
});
it(`#should be able to insert shares from existing tkey using _initializeNewKey, manualSync=${mode}`, async function () {
const resp1 = await tb._initializeNewKey({ initializeModules: true });
const { newShareStores: tbShareStore, newShareIndex: tbShareIndex } = await tb.generateNewShare();
await tb.syncLocalMetadataTransitions();

const tb3 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });

await tb3.initialize({ neverInitializeNewKey: true });
try {
await tb3.inputShareStoreSafe(resp1.deviceShare, true);
} catch (err) {
throw new Error(err);
}
const reconstructedKey = await tb3.reconstructKey();

if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
}
const shareStore = tb3.outputShareStore(tbShareIndex);
strictEqual(tbShareStore[tbShareIndex.toString("hex")].share.share.toString("hex"), shareStore.share.share.toString("hex"));
});
it(`#should be able to insert shares from existing tkey using new TKey Instance, manualSync=${mode}`, async function () {
const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
const resp2 = await tb2._initializeNewKey({ initializeModules: true });
await tb2.syncLocalMetadataTransitions();

const tb3 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });

await tb3.initialize({ neverInitializeNewKey: true });
await tb3.inputShareStoreSafe(resp2.deviceShare, true);
const reconstructedKey = await tb3.reconstructKey();
if (resp2.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
}
});
it(`#shouldn't be able to insert shares from random threshold key, manualSync=${mode}`, async function () {
// wrong tkey instance
const resp1 = await tb._initializeNewKey({ initializeModules: true });
const { newShareStores: tbShareStore, newShareIndex: tbShareIndex } = await tb.generateNewShare();
await tb.syncLocalMetadataTransitions();

// tkey instance with correct share stores and index
const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
const resp2 = await tb2._initializeNewKey({ initializeModules: true });
await tb2.syncLocalMetadataTransitions();

const tb3 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb3.initialize({ neverInitializeNewKey: true });
await tb3.syncLocalMetadataTransitions();

// throws since share doesn't
await rejects(
async () => {
await tb3.inputShareStoreSafe(tbShareStore[tbShareIndex.toString("hex")], true);
},
(err) => {
strictEqual(err.code, 1307, "CoreError: Share doesn't exist");
return true;
}
);
await rejects(
async () => {
await tb3.inputShareStoreSafe(resp1.deviceShare, true);
},
(err) => {
strictEqual(err.code, 1307, "CoreError: Share doesn't exist");
return true;
}
);
// should be able to insert if correct share store and index
await tb3.inputShareStoreSafe(resp2.deviceShare, true);
await tb3.reconstructKey();
const { newShareStores: tb3ShareStore, newShareIndex: tb3ShareIndex } = await tb3.generateNewShare();
await tb3.syncLocalMetadataTransitions();
await tb3.reconstructKey();

await tb2.inputShareStoreSafe(tb3ShareStore[tb3ShareIndex.toString("hex")], true);
const reconstructedKey2 = await tb2.reconstructKey();
if (resp2.privKey.cmp(reconstructedKey2.privKey) !== 0) {
fail("key should be able to be reconstructed");
}
});
it(`#should be able to update metadata, manualSync=${mode}`, async function () {
const resp1 = await tb._initializeNewKey({ initializeModules: true });
await tb.syncLocalMetadataTransitions();
// nonce 0

const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
await tb2.reconstructKey();

// try creating new shares
Expand Down Expand Up @@ -1066,7 +1149,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
await tb.syncLocalMetadataTransitions();
const tb3 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb3.initialize();
tb3.inputShareStore(newShareStores[newShareIndex.toString("hex")]);
await tb3.inputShareStoreSafe(newShareStores[newShareIndex.toString("hex")]);

const stringified = JSON.stringify(tb3);
const tb4 = await ThresholdKey.fromJSON(JSON.parse(stringified), { serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
Expand All @@ -1085,7 +1168,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
customSL2.serviceProvider = customSP3;
const tb2 = new ThresholdKey({ serviceProvider: customSP2, storageLayer: customSL2, manualSync: mode });
await tb2.initialize({ withShare: resp1.deviceShare });
tb2.inputShareStore(newShareStores1[newShareIndex1.toString("hex")]);
await tb2.inputShareStoreSafe(newShareStores1[newShareIndex1.toString("hex")]);
await tb2.reconstructKey();
const stringified = JSON.stringify(tb2);

Expand Down Expand Up @@ -1335,6 +1418,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
});
it(`#should be able to transfer share via the module, manualSync=${mode}`, async function () {
const resp1 = await tb._initializeNewKey({ initializeModules: true });
const result = await tb.generateNewShare();
await tb.syncLocalMetadataTransitions();

const tb2 = new ThresholdKey({
Expand All @@ -1348,8 +1432,6 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
// usually should be called in callback, but mocha does not allow
const pubkey = await tb2.modules.shareTransfer.requestNewShare();

const result = await tb.generateNewShare();

await tb.modules.shareTransfer.approveRequest(pubkey, result.newShareStores[result.newShareIndex.toString("hex")]);
await tb.syncLocalMetadataTransitions();

Expand Down Expand Up @@ -1544,7 +1626,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
modules: { seedPhrase: new SeedPhraseModule([metamaskSeedPhraseFormat2]) },
});
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstuctedKey = await tb2.reconstructKey();
await tb.modules.seedPhrase.getSeedPhrasesWithAccounts();

Expand Down Expand Up @@ -1699,7 +1781,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
modules: { seedPhrase: new SeedPhraseModule([metamaskSeedPhraseFormat2]), privateKeyModule: new PrivateKeyModule([secp256k1Format]) },
});
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();

compareReconstructedKeys(reconstructedKey, {
Expand Down Expand Up @@ -1737,7 +1819,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, manualSync: mode, storageLayer: customSL });
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -1764,7 +1846,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {

const tb2 = new ThresholdKey({ serviceProvider: customSP, manualSync: mode, storageLayer: customSL });
await tb2.initialize();
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
const reconstructedKey = await tb2.reconstructKey();
if (resp1.privKey.cmp(reconstructedKey.privKey) !== 0) {
fail("key should be able to be reconstructed");
Expand All @@ -1774,7 +1856,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
for (let i = 0; i < 5; i += 1) {
const temp = new ThresholdKey({ serviceProvider: customSP, manualSync: mode, storageLayer: customSL });
await temp.initialize();
temp.inputShareStore(resp1.deviceShare);
await temp.inputShareStoreSafe(resp1.deviceShare);
await temp.reconstructKey();
alltbs.push(temp);
}
Expand Down Expand Up @@ -1919,7 +2001,7 @@ export const sharedTestCases = (mode, torusSP, storageLayer) => {
it(`#should throw error code 1103 if metadata post failed, in manualSync: ${mode}`, async function () {
const tb2 = new ThresholdKey({ serviceProvider: customSP, storageLayer: customSL, manualSync: mode });
await tb2.initialize({ neverInitializeNewKey: true });
tb2.inputShareStore(resp1.deviceShare);
await tb2.inputShareStoreSafe(resp1.deviceShare);
await tb2.reconstructKey();
await tb2.syncLocalMetadataTransitions();
sandbox.stub(tb2.storageLayer, "setMetadataStream").throws(new Error("failed to set metadata"));
Expand Down