Skip to content

Commit

Permalink
fix(coordinator): cleanup session key service and use plaintext appro…
Browse files Browse the repository at this point in the history
…vals
  • Loading branch information
ctrlc03 committed Sep 9, 2024
1 parent 5e6efc8 commit 3618c04
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 97 deletions.
1 change: 1 addition & 0 deletions .github/workflows/coordinator-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ env:
COORDINATOR_ZKEY_PATH: "./zkeys/"
COORDINATOR_RAPIDSNARK_EXE: "~/rapidsnark/build/prover"
PIMLICO_API_KEY: "pim_"
RPC_API_KEY: "rpc_"
SUBGRAPH_FOLDER: "./node_modules/maci-subgraph"
SUBGRAPH_NAME: ${{ vars.SUBGRAPH_NAME }}
SUBGRAPH_PROVIDER_URL: ${{ vars.SUBGRAPH_PROVIDER_URL }}
Expand Down
35 changes: 35 additions & 0 deletions packages/coordinator/ts/common/accountAbstraction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import dotenv from "dotenv";
import { type Chain, createPublicClient, http, type HttpTransport, type PublicClient } from "viem";

import { ErrorCodes } from "./errors";
import { ESupportedNetworks, viemChain } from "./networks";

dotenv.config();

Expand All @@ -18,3 +20,36 @@ export const genPimlicoRPCUrl = (network: string): string => {

return `https://api.pimlico.io/v2/${network}/rpc?apikey=${pimlicoAPIKey}`;
};

/**
* Generate the RPCUrl for Alchemy based on the chain we need to interact with
* @param network - the network we want to interact with
* @returns the RPCUrl for the network
*/
export const genAlchemyRPCUrl = (network: ESupportedNetworks): string => {
const rpcAPIKey = process.env.RPC_API_KEY;

if (!rpcAPIKey) {
throw new Error(ErrorCodes.RPC_API_KEY_NOT_SET);
}

switch (network) {
case ESupportedNetworks.OPTIMISM_SEPOLIA:
return `https://opt-sepolia.g.alchemy.com/v2/${rpcAPIKey}`;
case ESupportedNetworks.ETHEREUM_SEPOLIA:
return `https://eth-sepolia.g.alchemy.com/v2/${rpcAPIKey}`;
default:
throw new Error(ErrorCodes.UNSUPPORTED_NETWORK);
}
};

/**
* Get a public client
* @param rpcUrl - the RPC URL
* @returns the public client
*/
export const getPublicClient = (chainName: ESupportedNetworks): PublicClient<HttpTransport, Chain> =>
createPublicClient({
transport: http(genAlchemyRPCUrl(chainName)),
chain: viemChain(chainName),
});
2 changes: 2 additions & 0 deletions packages/coordinator/ts/common/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export enum ErrorCodes {
SESSION_KEY_NOT_FOUND = "8",
PIMLICO_API_KEY_NOT_SET = "9",
INVALID_APPROVAL = "10",
UNSUPPORTED_NETWORK = "11",
RPC_API_KEY_NOT_SET = "12",
}
110 changes: 65 additions & 45 deletions packages/coordinator/ts/common/networks.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,84 @@
import {
arbitrum,
arbitrumSepolia,
base,
baseSepolia,
bsc,
type Chain,
gnosis,
holesky,
linea,
lineaSepolia,
mainnet,
optimism,
optimismSepolia,
polygon,
scroll,
scrollSepolia,
sepolia,
} from "viem/chains";

export enum ESupportedNetworks {
ETHEREUM = "mainnet",
OPTIMISM = "optimism",
OPTIMISM_SEPOLIA = "optimism-sepolia",
BSC = "bsc",
BSC_CHAPEL = "chapel",
GNOSIS_CHAIN = "gnosis",
FUSE = "fuse",
POLYGON = "matic",
FANTOM_OPERA = "fantom",
ZKSYNC_ERA_TESTNET = "zksync-era-testnet",
BOBA = "boba",
MOONBEAM = "moonbeam",
MOONRIVER = "moonriver",
MOONBASE_ALPHA = "mbase",
FANTOM_TESTNET = "fantom-testnet",
ARBITRUM_ONE = "arbitrum-one",
CELO = "celo",
AVALANCHE_FUJI = "fuji",
AVALANCHE = "avalanche",
CELO_ALFAJORES = "celo-alfajores",
HOLESKY = "holesky",
AURORA = "aurora",
AURORA_TESTNET = "aurora-testnet",
HARMONY = "harmony",
LINEA_SEPOLIA = "linea-sepolia",
GNOSIS_CHIADO = "gnosis-chiado",
MODE_SEPOLIA = "mode-sepolia",
MODE = "mode-mainnet",
BASE_SEPOLIA = "base-sepolia",
ZKSYNC_ERA_SEPOLIA = "zksync-era-sepolia",
POLYGON_ZKEVM = "polygon-zkevm",
ZKSYNC_ERA = "zksync-era",
ETHEREUM_SEPOLIA = "sepolia",
ARBITRUM_SEPOLIA = "arbitrum-sepolia",
LINEA = "linea",
BASE = "base",
SCROLL_SEPOLIA = "scroll-sepolia",
SCROLL = "scroll",
BLAST_MAINNET = "blast-mainnet",
ASTAR_ZKEVM_MAINNET = "astar-zkevm-mainnet",
SEI_TESTNET = "sei-testnet",
BLAST_TESTNET = "blast-testnet",
ETHERLINK_TESTNET = "etherlink-testnet",
XLAYER_SEPOLIA = "xlayer-sepolia",
XLAYER_MAINNET = "xlayer-mainnet",
POLYGON_AMOY = "polygon-amoy",
ZKYOTO_TESTNET = "zkyoto-testnet",
POLYGON_ZKEVM_CARDONA = "polygon-zkevm-cardona",
SEI_MAINNET = "sei-mainnet",
ROOTSTOCK_MAINNET = "rootstock",
IOTEX_MAINNET = "iotex",
NEAR_MAINNET = "near-mainnet",
NEAR_TESTNET = "near-testnet",
COSMOS = "cosmoshub-4",
COSMOS_HUB = "theta-testnet-001",
OSMOSIS = "osmosis-1",
OSMO_TESTNET = "osmo-test-4",
ARWEAVE = "arweave-mainnet",
BITCOIN = "btc",
SOLANA = "solana-mainnet-beta",
INJECTIVE_MAINNET = "injective-mainnet",
INJECTIVE_TESTNET = "injective-testnet",
}

/**
* Get the Viem chain for a given network
*
* @param network - the network to get the chain for
* @returns the Viem chain
*/
export const viemChain = (network: ESupportedNetworks): Chain => {
switch (network) {
case ESupportedNetworks.ETHEREUM:
return mainnet;
case ESupportedNetworks.ETHEREUM_SEPOLIA:
return sepolia;
case ESupportedNetworks.ARBITRUM_ONE:
return arbitrum;
case ESupportedNetworks.ARBITRUM_SEPOLIA:
return arbitrumSepolia;
case ESupportedNetworks.BASE_SEPOLIA:
return baseSepolia;
case ESupportedNetworks.LINEA_SEPOLIA:
return lineaSepolia;
case ESupportedNetworks.SCROLL_SEPOLIA:
return scrollSepolia;
case ESupportedNetworks.SCROLL:
return scroll;
case ESupportedNetworks.BASE:
return base;
case ESupportedNetworks.HOLESKY:
return holesky;
case ESupportedNetworks.LINEA:
return linea;
case ESupportedNetworks.BSC:
return bsc;
case ESupportedNetworks.GNOSIS_CHAIN:
return gnosis;
case ESupportedNetworks.POLYGON:
return polygon;
case ESupportedNetworks.OPTIMISM:
return optimism;
case ESupportedNetworks.OPTIMISM_SEPOLIA:
return optimismSepolia;
default:
throw new Error(`Unsupported network: ${network}`);
}
};
2 changes: 1 addition & 1 deletion packages/coordinator/ts/file/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class FileService {
*/
constructor() {
this.logger = new Logger(FileService.name);
this.db = low(new FileSync<TStorage>(path.resolve(__dirname, "..", "..", "./session-keys.json")));
this.db = low(new FileSync<TStorage>(path.resolve(process.cwd(), "./session-keys.json")));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import dotenv from "dotenv";
import { ZeroAddress } from "ethers";
import { zeroAddress } from "viem";
import { optimismSepolia } from "viem/chains";

import { KeyLike } from "crypto";

import { ErrorCodes } from "../../common";
import { CryptoService } from "../../crypto/crypto.service";
import { ErrorCodes, ESupportedNetworks } from "../../common";
import { FileService } from "../../file/file.service";
import { SessionKeysService } from "../sessionKeys.service";

Expand All @@ -15,21 +10,18 @@ import { mockSessionKeyApproval } from "./utils";
dotenv.config();

describe("SessionKeysService", () => {
const cryptoService = new CryptoService();
const fileService = new FileService();
let sessionKeysService: SessionKeysService;
let publicKey: KeyLike;

beforeAll(async () => {
publicKey = (await fileService.getPublicKey()).publicKey;
sessionKeysService = new SessionKeysService(cryptoService, fileService);
beforeAll(() => {
sessionKeysService = new SessionKeysService(fileService);
});

describe("generateSessionKey", () => {
test("should generate and store a session key", () => {
const sessionKeyAddress = sessionKeysService.generateSessionKey();
expect(sessionKeyAddress).toBeDefined();
expect(sessionKeyAddress).not.toEqual(ZeroAddress);
expect(sessionKeyAddress).not.toEqual(zeroAddress);

const sessionKey = fileService.getSessionKey(sessionKeyAddress.sessionKeyAddress);
expect(sessionKey).toBeDefined();
Expand All @@ -40,7 +32,7 @@ describe("SessionKeysService", () => {
test("should delete a session key", () => {
const sessionKeyAddress = sessionKeysService.generateSessionKey();
expect(sessionKeyAddress).toBeDefined();
expect(sessionKeyAddress).not.toEqual(ZeroAddress);
expect(sessionKeyAddress).not.toEqual(zeroAddress);

const sessionKey = fileService.getSessionKey(sessionKeyAddress.sessionKeyAddress);
expect(sessionKey).toBeDefined();
Expand All @@ -54,22 +46,19 @@ describe("SessionKeysService", () => {
describe("generateClientFromSessionKey", () => {
test("should fail to generate a client with an invalid approval", async () => {
const sessionKeyAddress = sessionKeysService.generateSessionKey();
const approval = await mockSessionKeyApproval(sessionKeyAddress.sessionKeyAddress);
const encryptedApproval = cryptoService.encrypt(publicKey, approval);
await expect(
sessionKeysService.generateClientFromSessionKey(
sessionKeyAddress.sessionKeyAddress,
encryptedApproval,
optimismSepolia,
"0xinvalid",
ESupportedNetworks.OPTIMISM_SEPOLIA,
),
).rejects.toThrow(ErrorCodes.INVALID_APPROVAL);
});

test("should throw when given a non existent session key address", async () => {
const approval = await mockSessionKeyApproval(zeroAddress);
const encryptedApproval = cryptoService.encrypt(publicKey, approval);
await expect(
sessionKeysService.generateClientFromSessionKey(zeroAddress, encryptedApproval, optimismSepolia),
sessionKeysService.generateClientFromSessionKey(zeroAddress, approval, ESupportedNetworks.OPTIMISM_SEPOLIA),
).rejects.toThrow(ErrorCodes.SESSION_KEY_NOT_FOUND);
});

Expand All @@ -86,17 +75,16 @@ describe("SessionKeysService", () => {

const sessionKeyAddress = sessionKeysService.generateSessionKey();
const approval = await mockSessionKeyApproval(sessionKeyAddress.sessionKeyAddress);
const encryptedApproval = cryptoService.encrypt(publicKey, approval);

const client = await sessionKeysService.generateClientFromSessionKey(
sessionKeyAddress.sessionKeyAddress,
encryptedApproval,
optimismSepolia,
approval,
ESupportedNetworks.OPTIMISM_SEPOLIA,
);
expect(mockGenerateClientFromSessionKey).toHaveBeenCalledWith(
sessionKeyAddress.sessionKeyAddress,
encryptedApproval,
optimismSepolia,
approval,
ESupportedNetworks.OPTIMISM_SEPOLIA,
);
expect(client).toEqual({ mockedClient: true });
});
Expand Down
7 changes: 4 additions & 3 deletions packages/coordinator/ts/sessionKeys/sessionKeys.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Module } from "@nestjs/common";

import { CryptoModule } from "../crypto/crypto.module";
import { CryptoService } from "../crypto/crypto.service";
import { FileModule } from "../file/file.module";

import { SessionKeysController } from "./sessionKeys.controller";
import { SessionKeysService } from "./sessionKeys.service";

@Module({
imports: [FileModule, CryptoModule],
imports: [FileModule],
controllers: [SessionKeysController],
providers: [SessionKeysService],
providers: [SessionKeysService, CryptoService],
exports: [SessionKeysService],
})
export class SessionKeysModule {}
Loading

0 comments on commit 3618c04

Please sign in to comment.