From f7858d3f3d0ce560bcba68ad788c1f989d3921a3 Mon Sep 17 00:00:00 2001 From: marie-fourier Date: Wed, 19 Apr 2023 17:16:01 +0500 Subject: [PATCH 1/5] update packUserOp --- packages/executor/src/utils/index.ts | 61 ++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/packages/executor/src/utils/index.ts b/packages/executor/src/utils/index.ts index ab9ce064..287aa60f 100644 --- a/packages/executor/src/utils/index.ts +++ b/packages/executor/src/utils/index.ts @@ -13,6 +13,10 @@ const UserOpType = ( ) as any ).inputs?.[0]; +if (UserOpType == null) { + throw new Error("unable to find method simulateValidation in EntryPoint ABI"); +} + function encode( typevalues: Array<{ type: string; val: any }>, forSignature: boolean @@ -28,10 +32,27 @@ function encode( return defaultAbiCoder.encode(types, values); } +/** + * pack the userOperation + * @param op + * @param forSignature "true" if the hash is needed to calculate the getUserOpHash() + * "false" to pack entire UserOp, for calculating the calldata cost of putting it on-chain. + */ export function packUserOp( op: UserOperationStruct, forSignature = true ): string { + const initCodeHash = keccak256(op.initCode); + const callDataHash = keccak256(op.callData); + const paymasterAndDataHash = keccak256(op.paymasterAndData); + + const userOp = { + ...op, + initCode: initCodeHash, + callData: callDataHash, + paymasterAndData: paymasterAndDataHash, + }; + if (forSignature) { // lighter signature scheme (must match UserOperation#pack): do encode a zero-length signature, but strip afterwards the appended zero-length value const userOpType = { @@ -45,11 +66,11 @@ export function packUserOp( name: "nonce", }, { - type: "bytes", + type: "bytes32", name: "initCode", }, { - type: "bytes", + type: "bytes32", name: "callData", }, { @@ -73,7 +94,7 @@ export function packUserOp( name: "maxPriorityFeePerGas", }, { - type: "bytes", + type: "bytes32", name: "paymasterAndData", }, { @@ -84,31 +105,53 @@ export function packUserOp( name: "userOp", type: "tuple", }; - + // console.log('hard-coded userOpType', userOpType) + // console.log('from ABI userOpType', UserOpType) let encoded = defaultAbiCoder.encode( [userOpType as any], [ { - ...op, + ...userOp, signature: "0x", }, ] ); - + // remove leading word (total length) and trailing word (zero-length signature) encoded = "0x" + encoded.slice(66, encoded.length - 64); return encoded; } const typevalues = (UserOpType as any).components.map( - (c: { name: keyof typeof op; type: string }) => ({ + (c: { name: keyof typeof userOp; type: string }) => ({ type: c.type, - val: op[c.name], + val: userOp[c.name], }) ); - return encode(typevalues, forSignature); } +/** + * calculate the userOpHash of a given userOperation. + * The userOpHash is a hash of all UserOperation fields, except the "signature" field. + * The entryPoint uses this value in the emitted UserOperationEvent. + * A wallet may use this value as the hash to sign (the SampleWallet uses this method) + * @param op + * @param entryPoint + * @param chainId + */ +export function getUserOpHash( + op: UserOperationStruct, + entryPoint: string, + chainId: number +): string { + const userOpHash = keccak256(packUserOp(op, true)); + const enc = defaultAbiCoder.encode( + ["bytes32", "address", "uint256"], + [userOpHash, entryPoint, chainId] + ); + return keccak256(enc); +} + /** * hexlify all members of object, recursively * @param obj From 5a1722eb1b72546285ce0b97df39d594c581c1a7 Mon Sep 17 00:00:00 2001 From: marie-fourier Date: Fri, 21 Apr 2023 14:33:15 +0500 Subject: [PATCH 2/5] feat: unsafe mode --- README.md | 8 +------- packages/cli/src/cmds/start/handler.ts | 6 ++++-- packages/cli/src/cmds/start/index.ts | 9 ++++++++- packages/executor/src/config.ts | 5 ++++- packages/executor/src/executor.ts | 3 ++- packages/executor/src/modules/eth.ts | 12 +++--------- .../executor/src/services/BundlingService.ts | 2 +- .../executor/src/services/UserOpValidation.ts | 19 ++++++++++++++++--- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f007ec45..8dd4dbb3 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,7 @@ docker run --rm -ti --name geth -p 8545:8545 ethereum/client-go:v1.10.26 \ - [x] debug_bundler_sendBundleNow ### Additional features -- [x] rocksdb -- [x] validation by opcodes (partial, need reputation management for 100% support) -- [ ] support multiple entry points -- [ ] reputation management -- [ ] e2e tests -- [ ] additional rpc methods *(which one?)* -- [ ] hooks for mev-boost +- [x] Unsafe mode - bypass opcode & stake validation (Enabled by --unsafeMode) ### Relayer Configuration diff --git a/packages/cli/src/cmds/start/handler.ts b/packages/cli/src/cmds/start/handler.ts index e0cbcde3..f8b23191 100644 --- a/packages/cli/src/cmds/start/handler.ts +++ b/packages/cli/src/cmds/start/handler.ts @@ -18,7 +18,7 @@ import { IBundlerArgs } from "./index"; export async function bundlerHandler( args: IBundlerArgs & IGlobalArgs ): Promise { - const { dataDir, networksFile, testingMode } = args; + const { dataDir, networksFile, testingMode, unsafeMode } = args; let config: Config; try { @@ -26,13 +26,15 @@ export async function bundlerHandler( const configOptions = readFile(configPath) as ConfigOptions; config = new Config({ networks: configOptions.networks, - testingMode: testingMode, + testingMode, + unsafeMode, }); } catch (err) { console.log("Config file not found. Proceeding with env vars..."); config = new Config({ networks: {}, testingMode, + unsafeMode, }); } diff --git a/packages/cli/src/cmds/start/index.ts b/packages/cli/src/cmds/start/index.ts index 6290c9b6..6dd16507 100644 --- a/packages/cli/src/cmds/start/index.ts +++ b/packages/cli/src/cmds/start/index.ts @@ -4,11 +4,18 @@ import { bundlerHandler } from "./handler"; export interface IBundlerArgs { testingMode: boolean; + unsafeMode: boolean; } export const bundlerOptions = { testingMode: { - description: "Run bundler in dev mode (For testing against test suite)", + description: "Run bundler in testing mode (For testing against test suite)", + type: "boolean", + default: false, + choices: [true, false], + }, + unsafeMode: { + description: "Run bundler in unsafe mode (Bypass opcode & stake check)", type: "boolean", default: false, choices: [true, false], diff --git a/packages/executor/src/config.ts b/packages/executor/src/config.ts index a5ae599c..17814395 100644 --- a/packages/executor/src/config.ts +++ b/packages/executor/src/config.ts @@ -27,17 +27,20 @@ export type Networks = { export interface ConfigOptions { networks: Networks; testingMode?: boolean; + unsafeMode: boolean; } export class Config { supportedNetworks: NetworkName[]; networks: Networks; testingMode: boolean; + unsafeMode: boolean; constructor(private config: ConfigOptions) { this.supportedNetworks = this.parseSupportedNetworks(); this.networks = this.parseNetworkConfigs(); - this.testingMode = config.testingMode ?? true; + this.testingMode = config.testingMode ?? false; + this.unsafeMode = config.unsafeMode ?? false; } getNetworkProvider(network: NetworkName): providers.JsonRpcProvider | null { diff --git a/packages/executor/src/executor.ts b/packages/executor/src/executor.ts index 4648f644..02b1ce0e 100644 --- a/packages/executor/src/executor.ts +++ b/packages/executor/src/executor.ts @@ -66,7 +66,8 @@ export class Executor { this.userOpValidationService = new UserOpValidationService( this.provider, this.reputationService, - this.network + this.network, + this.config ); this.mempoolService = new MempoolService( this.db, diff --git a/packages/executor/src/modules/eth.ts b/packages/executor/src/modules/eth.ts index 957d1610..72e5d6bb 100644 --- a/packages/executor/src/modules/eth.ts +++ b/packages/executor/src/modules/eth.ts @@ -44,10 +44,7 @@ export class Eth { } this.logger.debug("Validating user op before sending to mempool..."); const validationResult = - await this.userOpValidationService.simulateCompleteValidation( - userOp, - entryPoint - ); + await this.userOpValidationService.simulateValidation(userOp, entryPoint); // TODO: fetch aggregator this.logger.debug("Validation successful. Saving in mempool..."); await this.mempoolService.addUserOp( @@ -90,7 +87,7 @@ export class Eth { verificationGasLimit: 10e6, }; const { returnInfo } = - await this.userOpValidationService.callSimulateValidation( + await this.userOpValidationService.simulateValidation( userOpComplemented, entryPoint ); @@ -142,10 +139,7 @@ export class Eth { RpcErrorCodes.INVALID_USEROP ); } - await this.userOpValidationService.simulateCompleteValidation( - userOp, - entryPoint - ); + await this.userOpValidationService.simulateValidation(userOp, entryPoint); return true; } diff --git a/packages/executor/src/services/BundlingService.ts b/packages/executor/src/services/BundlingService.ts index 1d5598db..b59e917d 100644 --- a/packages/executor/src/services/BundlingService.ts +++ b/packages/executor/src/services/BundlingService.ts @@ -183,7 +183,7 @@ export class BundlingService { let validationResult: UserOpValidationResult; try { validationResult = - await this.userOpValidationService.simulateCompleteValidation( + await this.userOpValidationService.simulateValidation( entry.userOp, entry.entryPoint, entry.hash diff --git a/packages/executor/src/services/UserOpValidation.ts b/packages/executor/src/services/UserOpValidation.ts index 471adb27..1bb716bf 100644 --- a/packages/executor/src/services/UserOpValidation.ts +++ b/packages/executor/src/services/UserOpValidation.ts @@ -14,6 +14,7 @@ import { BannedContracts } from "params/lib"; import { NetworkName } from "types/lib"; import { getAddr } from "../utils"; import { TracerCall } from "../interfaces"; +import { Config } from "../config"; import { ReputationService } from "./ReputationService"; import { GethTracer } from "./GethTracer"; @@ -50,14 +51,26 @@ export class UserOpValidationService { constructor( private provider: providers.Provider, private reputationService: ReputationService, - private network: NetworkName + private network: NetworkName, + private config: Config ) { this.gethTracer = new GethTracer( this.provider as providers.JsonRpcProvider ); } - async callSimulateValidation( + async simulateValidation( + userOp: UserOperationStruct, + entryPoint: string, + codehash?: string + ): Promise { + if (this.config.unsafeMode) { + return this.simulateUnsafeValidation(userOp, entryPoint); + } + return this.simulateSafeValidation(userOp, entryPoint, codehash); + } + + async simulateUnsafeValidation( userOp: UserOperationStruct, entryPoint: string ): Promise { @@ -71,7 +84,7 @@ export class UserOpValidationService { return this.parseErrorResult(userOp, errorResult); } - async simulateCompleteValidation( + async simulateSafeValidation( userOp: UserOperationStruct, entryPoint: string, codehash?: string From e54f89dd68b14fb8e83c914eab1a89fa66a299b1 Mon Sep 17 00:00:00 2001 From: marie-fourier Date: Fri, 21 Apr 2023 14:54:23 +0500 Subject: [PATCH 3/5] mention unsafe in clientVersion --- packages/executor/src/executor.ts | 2 +- packages/executor/src/modules/web3.ts | 6 ++++-- packages/executor/tsconfig.build.json | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/executor/src/executor.ts b/packages/executor/src/executor.ts index 02b1ce0e..090b79ec 100644 --- a/packages/executor/src/executor.ts +++ b/packages/executor/src/executor.ts @@ -83,7 +83,7 @@ export class Executor { this.config, this.logger ); - this.web3 = new Web3(); + this.web3 = new Web3(this.config); this.debug = new Debug( this.provider, this.bundlingService, diff --git a/packages/executor/src/modules/web3.ts b/packages/executor/src/modules/web3.ts index c780e9bd..1bba0184 100644 --- a/packages/executor/src/modules/web3.ts +++ b/packages/executor/src/modules/web3.ts @@ -1,6 +1,8 @@ +import { Config } from "../config"; export class Web3 { + constructor(private config: Config) {} + clientVersion(): string { - // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires - return require("../package.json").version; + return `skandha/${this.config.unsafeMode ? "unsafe/" : ""}0.0.1`; // TODO: get version based on commit hash } } diff --git a/packages/executor/tsconfig.build.json b/packages/executor/tsconfig.build.json index 94e75736..d0f62210 100644 --- a/packages/executor/tsconfig.build.json +++ b/packages/executor/tsconfig.build.json @@ -3,7 +3,7 @@ "include": ["src"], "compilerOptions": { "outDir": "lib", - "typeRoots": ["../../node_modules/@types", "./node_modules/@types", "../../types"] + "typeRoots": ["../../node_modules/@types", "./node_modules/@types", "../../types"], }, "noImplicitAny": false, } \ No newline at end of file From 0e425a9ba3b8b26df3d1cbec3231c3d261643c3a Mon Sep 17 00:00:00 2001 From: marie-fourier Date: Fri, 21 Apr 2023 14:55:02 +0500 Subject: [PATCH 4/5] fix typo --- packages/executor/tsconfig.build.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/executor/tsconfig.build.json b/packages/executor/tsconfig.build.json index d0f62210..94e75736 100644 --- a/packages/executor/tsconfig.build.json +++ b/packages/executor/tsconfig.build.json @@ -3,7 +3,7 @@ "include": ["src"], "compilerOptions": { "outDir": "lib", - "typeRoots": ["../../node_modules/@types", "./node_modules/@types", "../../types"], + "typeRoots": ["../../node_modules/@types", "./node_modules/@types", "../../types"] }, "noImplicitAny": false, } \ No newline at end of file From e22779cba48cd835026bf0137e0048cbe3cac20a Mon Sep 17 00:00:00 2001 From: marie-fourier Date: Tue, 25 Apr 2023 17:19:48 +0500 Subject: [PATCH 5/5] add sepolia --- packages/types/src/networks/networks.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/types/src/networks/networks.ts b/packages/types/src/networks/networks.ts index 64b252c0..bb3fbcc3 100644 --- a/packages/types/src/networks/networks.ts +++ b/packages/types/src/networks/networks.ts @@ -25,7 +25,8 @@ export type NetworkName = | "neonDevnet" | "optimismGoerli" | "dev" - | "baseGoerli"; + | "baseGoerli" + | "sepolia"; export const networkNames: NetworkName[] = [ "mainnet", @@ -55,6 +56,7 @@ export const networkNames: NetworkName[] = [ "optimismGoerli", "dev", "baseGoerli", + "sepolia", ]; export const NETWORK_NAME_TO_CHAIN_ID: { @@ -87,4 +89,5 @@ export const NETWORK_NAME_TO_CHAIN_ID: { optimismGoerli: 420, dev: 1337, baseGoerli: 84531, + sepolia: 11155111, };