Skip to content

Commit

Permalink
Merge pull request #167 from torusresearch/feat/getOrSetTssPubkey
Browse files Browse the repository at this point in the history
added get or set tss pubkey
  • Loading branch information
himanshuchawla009 authored Oct 7, 2024
2 parents 3d58fd5 + c701538 commit ad5477c
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./keyUtils";
export * from "./langrangeInterpolatePoly";
export * from "./metadataUtils";
export * from "./nodeUtils";
export * from "./tssPubKeyUtils";
112 changes: 112 additions & 0 deletions src/helpers/tssPubKeyUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Note: Endpoints should be the sss node endpoints along with path
import { JRPCResponse, KEY_TYPE } from "@toruslabs/constants";
import { generateJsonRPCObject, post } from "@toruslabs/http-helpers";
import log from "loglevel";

import { JRPC_METHODS } from "../constants";
import { GetORSetKeyResponse, KeyType } from "../interfaces";
import { Some } from "../some";
import { normalizeKeysResult, thresholdSame } from "./common";

// for ex: [https://node-1.node.web3auth.io/sss/jrpc, https://node-2.node.web3auth.io/sss/jrpc ....]
export const GetOrSetTssDKGPubKey = async (params: {
endpoints: string[];
verifier: string;
verifierId: string;
tssVerifierId: string;
keyType?: KeyType;
}): Promise<{
key: {
pubKeyX: string;
pubKeyY: string;
address: string;
createdAt?: number;
};
isNewKey: boolean;
nodeIndexes: number[];
}> => {
const { endpoints, verifier, verifierId, tssVerifierId, keyType = KEY_TYPE.SECP256K1 } = params;
const minThreshold = ~~(endpoints.length / 2) + 1;
const lookupPromises = endpoints.map((x) =>
post<JRPCResponse<GetORSetKeyResponse>>(
x,
generateJsonRPCObject(JRPC_METHODS.GET_OR_SET_KEY, {
distributed_metadata: true,
verifier,
verifier_id: verifierId,
extended_verifier_id: tssVerifierId,
one_key_flow: true,
key_type: keyType,
fetch_node_index: true,
client_time: Math.floor(Date.now() / 1000).toString(),
}),
{},
{
logTracingHeader: false,
}
).catch((err) => log.error(`${JRPC_METHODS.GET_OR_SET_KEY} request failed`, err))
);

const nodeIndexes: number[] = [];
const result = await Some<
void | JRPCResponse<GetORSetKeyResponse>,
{
keyResult: Pick<GetORSetKeyResponse, "keys" | "is_new_key">;
nodeIndexes: number[];
errorResult: JRPCResponse<GetORSetKeyResponse>["error"];
}
>(lookupPromises, async (lookupResults) => {
const lookupPubKeys = lookupResults.filter((x1) => {
if (x1 && !x1.error) {
return x1;
}
return false;
});

const errorResult = thresholdSame(
lookupResults.map((x2) => x2 && x2.error),
minThreshold
);

const keyResult = thresholdSame(
lookupPubKeys.map((x3) => x3 && normalizeKeysResult(x3.result)),
minThreshold
);

if (keyResult || errorResult) {
if (keyResult) {
lookupResults.forEach((x1) => {
if (x1 && x1.result) {
const currentNodePubKey = x1.result.keys[0].pub_key_X.toLowerCase();
const thresholdPubKey = keyResult.keys[0].pub_key_X.toLowerCase();
// push only those indexes for nodes who are returning pub key matching with threshold pub key.
// this check is important when different nodes have different keys assigned to a user.
if (currentNodePubKey === thresholdPubKey) {
const nodeIndex = Number.parseInt(x1.result.node_index);
if (nodeIndex) nodeIndexes.push(nodeIndex);
}
}
});
}

return Promise.resolve({ keyResult, nodeIndexes, errorResult });
}
return Promise.reject(new Error(`invalid public key result: ${JSON.stringify(lookupResults)} for tssVerifierId: ${tssVerifierId} `));
});

if (result.errorResult) {
throw new Error(`invalid public key result,errorResult: ${JSON.stringify(result.errorResult)}`);
}

const key = result.keyResult.keys[0];
return {
key: {
pubKeyX: key.pub_key_X,
pubKeyY: key.pub_key_Y,
address: key.address,
createdAt: key.created_at,
},
nodeIndexes: result.nodeIndexes,
isNewKey: result.keyResult.is_new_key,
};
};
88 changes: 88 additions & 0 deletions test/tssPubKey.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { TORUS_SAPPHIRE_NETWORK } from "@toruslabs/constants";
import { NodeDetailManager } from "@toruslabs/fetch-node-details";
import { expect } from "chai";
import faker from "faker";

import { GetOrSetTssDKGPubKey } from "../src";

describe("setTssKey", function () {
const TORUS_EXTENDED_VERIFIER_EMAIL = "[email protected]";
const TORUS_TEST_VERIFIER = "torus-test-health";

let TORUS_NODE_MANAGER: NodeDetailManager;

beforeEach("one time execution before all tests", async function () {
TORUS_NODE_MANAGER = new NodeDetailManager({ network: TORUS_SAPPHIRE_NETWORK.SAPPHIRE_DEVNET });
});

it("should assign key to tss verifier id", async function () {
const email = faker.internet.email();
const nonce = 0;
const tssTag = "default";
const tssVerifierId = `${email}\u0015${tssTag}\u0016${nonce}`;
const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: email };

const { torusNodeSSSEndpoints: torusNodeEndpoints } = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails);

const result = await GetOrSetTssDKGPubKey({
endpoints: torusNodeEndpoints,
verifier: TORUS_TEST_VERIFIER,
verifierId: email,
tssVerifierId,
});
expect(result.key.pubKeyX).to.not.equal(null);
});

it("should fetch pub address of tss verifier id", async function () {
const email = TORUS_EXTENDED_VERIFIER_EMAIL;
const nonce = 0;
const tssTag = "default";
const tssVerifierId = `${email}\u0015${tssTag}\u0016${nonce}`;
const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: email };

const { torusNodeSSSEndpoints: torusNodeEndpoints } = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails);

const result = await GetOrSetTssDKGPubKey({
endpoints: torusNodeEndpoints,
verifier: TORUS_TEST_VERIFIER,
verifierId: email,
tssVerifierId,
});
delete result.key.createdAt;
expect(result).eql({
key: {
pubKeyX: "d45d4ad45ec643f9eccd9090c0a2c753b1c991e361388e769c0dfa90c210348c",
pubKeyY: "fdc151b136aa7df94e97cc7d7007e2b45873c4b0656147ec70aad46e178bce1e",
address: "0xBd6Bc8aDC5f2A0526078Fd2016C4335f64eD3a30",
},
isNewKey: false,
nodeIndexes: result.nodeIndexes,
});
});

it("should fail if more than one endpoints are invalid", async function () {
const email = TORUS_EXTENDED_VERIFIER_EMAIL;
const nonce = 0;
const tssTag = "default";
const tssVerifierId = `${email}\u0015${tssTag}\u0016${nonce}`;
const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: email };

const { torusNodeSSSEndpoints: torusNodeEndpoints } = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails);
torusNodeEndpoints[2] = "https://invalid.torus.com";
torusNodeEndpoints[3] = "https://invalid.torus.com";
torusNodeEndpoints[4] = "https://invalid.torus.com";
try {
await GetOrSetTssDKGPubKey({
endpoints: torusNodeEndpoints,
verifier: TORUS_TEST_VERIFIER,
verifierId: email,
tssVerifierId,
});
// If the function doesn't throw an error, fail the test
expect.fail("Expected an error to be thrown");
} catch (error) {
// Test passes if an error is thrown
expect(error).to.be.instanceOf(Error);
}
});
});

0 comments on commit ad5477c

Please sign in to comment.