Skip to content

Commit

Permalink
feat: add merge command
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrlc03 committed Sep 10, 2024
1 parent c08cc74 commit 0ba6890
Show file tree
Hide file tree
Showing 23 changed files with 685 additions and 67 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/coordinator-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,7 @@ jobs:
- name: Test
run: pnpm run test
working-directory: packages/coordinator

- name: Stop Hardhat
if: always()
run: kill $(lsof -t -i:8545)
66 changes: 66 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Nightly

on:
schedule:
- cron: 0 0 * * *

jobs:
e2e-coordinator:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4
with:
ref: dev
- uses: pnpm/action-setup@v4
with:
version: 9

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes \
build-essential \
libgmp-dev \
libsodium-dev \
nasm \
nlohmann-json3-dev
- name: Install
run: |
pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: |
pnpm run build
- name: Download rapidsnark (1c137)
run: |
mkdir -p ~/rapidsnark/build
wget -qO ~/rapidsnark/build/prover https://maci-devops-zkeys.s3.ap-northeast-2.amazonaws.com/rapidsnark-linux-amd64-1c137
chmod +x ~/rapidsnark/build/prover
- name: Download circom Binary v2.1.6
run: |
wget -qO ${{ github.workspace }}/circom https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64
chmod +x ${{ github.workspace }}/circom
sudo mv ${{ github.workspace }}/circom /bin/circom
- name: Download zkeys
run: |
pnpm download-zkeys:test
- name: Generate keypair
run: |
pnpm generate-keypair
working-directory: packages/coordinator

- name: E2E tests
run: |
pnpm run test:e2e
3 changes: 3 additions & 0 deletions packages/coordinator/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ SUBGRAPH_FOLDER=./node_modules/maci-subgraph

# API Key for Pimlico RPC Bundler
PIMLICO_API_KEY="pim_"

# A private key for e2e tests
TEST_PRIVATE_KEY=""
9 changes: 5 additions & 4 deletions packages/coordinator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
"run:node": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));'",
"start": "pnpm run run:node ./ts/main.ts",
"start:prod": "pnpm run run:node build/ts/main.js",
"test": "jest --forceExit",
"test:coverage": "jest --coverage --forceExit",
"test": "jest --testPathIgnorePatterns=e2e.aa.test.ts --forceExit",
"test:coverage": "jest --coverage --testPathIgnorePatterns=e2e.aa.test.ts --forceExit",
"test:e2e": "jest --forceExit ./tests/e2e.aa.test.ts",
"types": "tsc -p tsconfig.json --noEmit",
"generate-keypair": "pnpm run run:node ./scripts/generateKeypair.ts",
"download-zkeys:test": "pnpm run run:node ./scripts/downloadZKeys.ts test ./zkeys",
Expand Down Expand Up @@ -54,13 +55,13 @@
"maci-domainobjs": "^2.0.0",
"maci-subgraph": "^2.3.0",
"mustache": "^4.2.0",
"permissionless": "^0.1.44",
"permissionless": ">=0.1.18 <=0.1.29",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"socket.io": "^4.7.5",
"tar": "^7.4.1",
"ts-node": "^10.9.1",
"viem": "^2.7.15"
"viem": "^2.16.3"
},
"devDependencies": {
"@nestjs/cli": "^10.4.2",
Expand Down
85 changes: 85 additions & 0 deletions packages/coordinator/tests/e2e.aa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator";
import { serializePermissionAccount, toPermissionValidator } from "@zerodev/permissions";
import { toSudoPolicy } from "@zerodev/permissions/policies";
import { toECDSASigner } from "@zerodev/permissions/signers";
import { addressToEmptyAccount, createKernelAccount } from "@zerodev/sdk";
import { KERNEL_V3_1 } from "@zerodev/sdk/constants";
import dotenv from "dotenv";
import { ENTRYPOINT_ADDRESS_V07 } from "permissionless";
import { Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";

import { ESupportedNetworks } from "../ts/common";
import { getPublicClient } from "../ts/common/accountAbstraction";
import { CryptoService } from "../ts/crypto/crypto.service";
import { FileService } from "../ts/file/file.service";
import { ProofGeneratorService } from "../ts/proof/proof.service";
import { SessionKeysService } from "../ts/sessionKeys/sessionKeys.service";

dotenv.config();

const entryPoint = ENTRYPOINT_ADDRESS_V07;
const kernelVersion = KERNEL_V3_1;

/**
* Generate an approval for a session key
*
* @returns - the approval
*/
export const generateApproval = async (sessionKeyAddress: Hex): Promise<string> => {
const publicClient = getPublicClient(ESupportedNetworks.OPTIMISM_SEPOLIA);

const sessionKeySigner = privateKeyToAccount(process.env.TEST_PRIVATE_KEY! as `0x${string}`);
const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
signer: sessionKeySigner,
entryPoint,
kernelVersion,
});

const emptyAccount = addressToEmptyAccount(sessionKeyAddress);
const emptySessionKeySigner = toECDSASigner({ signer: emptyAccount });

const permissionPlugin = await toPermissionValidator(publicClient, {
entryPoint,
kernelVersion,
signer: emptySessionKeySigner,
policies: [toSudoPolicy({})],
});

const sessionKeyAccount = await createKernelAccount(publicClient, {
entryPoint,
kernelVersion,
plugins: {
sudo: ecdsaValidator,
regular: permissionPlugin,
},
});

return serializePermissionAccount(sessionKeyAccount);
};

describe("E2E Account Abstraction Tests", () => {
const fileService = new FileService();
const sessionKeyService = new SessionKeysService(fileService);
const cryptoService = new CryptoService();
const proofService = new ProofGeneratorService(cryptoService, fileService, sessionKeyService);

// using an already deployed maci contract
const maciContract = "0xf281870519822f302B13c07252d0f6A71E8D023e";
const pollId = 2;

test("should return true when there are no errors", async () => {
const sessionKey = sessionKeyService.generateSessionKey().sessionKeyAddress;
const approval = await generateApproval(sessionKey);

const merged = await proofService.merge({
maciContractAddress: maciContract,
pollId,
sessionKeyAddress: sessionKey,
approval,
chain: ESupportedNetworks.OPTIMISM_SEPOLIA,
});

expect(merged).toBe(true);
});
});
54 changes: 53 additions & 1 deletion packages/coordinator/ts/common/accountAbstraction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { deserializePermissionAccount } from "@zerodev/permissions";
import { toECDSASigner } from "@zerodev/permissions/signers";
import { createKernelAccountClient, KernelAccountClient, KernelSmartAccount } from "@zerodev/sdk";
import { KERNEL_V3_1 } from "@zerodev/sdk/constants";
import dotenv from "dotenv";
import { type Chain, createPublicClient, http, type HttpTransport, type PublicClient } from "viem";
import { ENTRYPOINT_ADDRESS_V07 } from "permissionless";
import { ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types";
import { createPublicClient, http, type HttpTransport, type Transport, type Hex, type PublicClient, Chain } from "viem";
import { privateKeyToAccount } from "viem/accounts";

import { ErrorCodes } from "./errors";
import { ESupportedNetworks, viemChain } from "./networks";
Expand Down Expand Up @@ -38,6 +45,8 @@ export const genAlchemyRPCUrl = (network: ESupportedNetworks): string => {
return `https://opt-sepolia.g.alchemy.com/v2/${rpcAPIKey}`;
case ESupportedNetworks.ETHEREUM_SEPOLIA:
return `https://eth-sepolia.g.alchemy.com/v2/${rpcAPIKey}`;
case ESupportedNetworks.LOCALHOST:
return "http://localhost:8545";
default:
throw new Error(ErrorCodes.UNSUPPORTED_NETWORK);
}
Expand All @@ -53,3 +62,46 @@ export const getPublicClient = (chainName: ESupportedNetworks): PublicClient<Htt
transport: http(genAlchemyRPCUrl(chainName)),
chain: viemChain(chainName),
});

/**
* Get a Kernel account handle given a session key
* @param sessionKey
* @param chainId
*/
export const getKernelClient = async (
sessionKey: Hex,
approval: string,
chain: ESupportedNetworks,
): Promise<
KernelAccountClient<
ENTRYPOINT_ADDRESS_V07_TYPE,
Transport,
Chain,
KernelSmartAccount<ENTRYPOINT_ADDRESS_V07_TYPE, HttpTransport, Chain>
>
> => {
const bundlerUrl = genPimlicoRPCUrl(chain);
const publicClient = getPublicClient(chain);

// Using a stored private key
const sessionKeySigner = toECDSASigner({
signer: privateKeyToAccount(sessionKey),
});

const sessionKeyAccount = await deserializePermissionAccount(
publicClient,
ENTRYPOINT_ADDRESS_V07,
KERNEL_V3_1,
approval,
sessionKeySigner,
);

const kernelClient = createKernelAccountClient({
bundlerTransport: http(bundlerUrl),
entryPoint: ENTRYPOINT_ADDRESS_V07,
account: sessionKeyAccount,
chain: viemChain(chain),
});

return kernelClient;
};
3 changes: 3 additions & 0 deletions packages/coordinator/ts/common/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ export enum ErrorCodes {
INVALID_APPROVAL = "10",
UNSUPPORTED_NETWORK = "11",
RPC_API_KEY_NOT_SET = "12",
FAILED_TO_MERGE_STATE_TREE = "13",
FAILED_TO_MERGE_MESSAGE_SUBTREES = "14",
FAILED_TO_MERGE_MESSAGE_TREE = "15",
}
1 change: 1 addition & 0 deletions packages/coordinator/ts/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { ErrorCodes } from "./errors";
export { ESupportedNetworks } from "./networks";
export { transformToString } from "./utils";
4 changes: 4 additions & 0 deletions packages/coordinator/ts/common/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
holesky,
linea,
lineaSepolia,
localhost,
mainnet,
optimism,
optimismSepolia,
Expand Down Expand Up @@ -36,6 +37,7 @@ export enum ESupportedNetworks {
BASE = "base",
SCROLL_SEPOLIA = "scroll-sepolia",
SCROLL = "scroll",
LOCALHOST = "localhost",
}

/**
Expand Down Expand Up @@ -78,6 +80,8 @@ export const viemChain = (network: ESupportedNetworks): Chain => {
return optimism;
case ESupportedNetworks.OPTIMISM_SEPOLIA:
return optimismSepolia;
case ESupportedNetworks.LOCALHOST:
return localhost;
default:
throw new Error(`Unsupported network: ${network}`);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/coordinator/ts/common/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Transform string to lowercase
*
* @param param0 - string to transform
* @returns lowercase string
*/
export const transformToString = ({ value }: { value: string }): string => value.toLowerCase();
18 changes: 17 additions & 1 deletion packages/coordinator/ts/proof/__tests__/proof.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { HttpException, HttpStatus } from "@nestjs/common";
import { Test } from "@nestjs/testing";

import type { IGetPublicKeyData } from "../../file/types";
import type { IGenerateArgs, IGenerateData } from "../types";
import type { IGenerateArgs, IGenerateData, IMergeArgs } from "../types";
import type { TallyData } from "maci-cli";

import { ESupportedNetworks } from "../../common";
import { FileService } from "../../file/file.service";
import { ProofController } from "../proof.controller";
import { ProofGeneratorService } from "../proof.service";
Expand All @@ -21,6 +22,14 @@ describe("ProofController", () => {
"siO9W/g7jNVXs9tOUv/pffrcqYdMlgdXw7nSSlqM1q1UvHGSSbhtLJpeT+nJKW7/+xrBTgI0wB866DSkg8Rgr8zD+POUMiKPrGqAO/XhrcmRDL+COURFNDRh9WGeAua6hdiNoufQYvXPl1iWyIYidSDbfmC2wR6F9vVkhg/6KDZyw8Wlr6LUh0RYT+hUHEwwGbz7MeqZJcJQSTpECPF5pnk8NTHL2W/XThaewB4n4HYqjDUbYLmBDLYWsDDMgoPo709a309rTq3uEe0YBgVF8g9aGxucTDhz+/LYYzqaeSxclUwen9Z4BGZjiDSPBZfooOEQEEwIJlViQ2kl1VeOKAmkiWEUVfItivmNbC/PNZchklmfFsGpiu4DT9UU9YVBN2OTcFYHHsslcaqrR7SuesqjluaGjG46oYEmfQlkZ4gXhavdWXw2ant+Tv6HRo4trqjoD1e3jUkN6gJMWomxOeRBTg0czBZlz/IwUtTpBHcKhi3EqGQo8OuQtWww+Ts7ySmeoONuovYUsIAppNuOubfUxvFJoTr2vKbWNAiYetw09kddkjmBe+S8A5PUiFOi262mfc7g5wJwPPP7wpTBY0Fya+2BCPzXqRLMOtNI+1tW3/UQLZYvEY8J0TxmhoAGZaRn8FKaosatRxDZTQS6QUNmKxpmUspkRKzTXN5lznM=",
};

const defaultMergeArgs: IMergeArgs = {
maciContractAddress: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
pollId: 0,
sessionKeyAddress: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
approval: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
chain: ESupportedNetworks.LOCALHOST,
};

const defaultProofGeneratorData: IGenerateData = {
tallyProofs: [],
processProofs: [],
Expand Down Expand Up @@ -83,6 +92,13 @@ describe("ProofController", () => {
});
});

describe("v1/proof/merge", () => {
test("should return true when there are no errors", async () => {
const data = await proofController.merge(defaultMergeArgs);
expect(data).toBe(true);
});
});

describe("v1/proof/publicKey", () => {
test("should return public key properly", async () => {
const data = await proofController.getPublicKey();
Expand Down
Loading

0 comments on commit 0ba6890

Please sign in to comment.