Skip to content

Commit

Permalink
Merge pull request #21 from etherspot/develop
Browse files Browse the repository at this point in the history
Release 24.04.2023
  • Loading branch information
0xSulpiride authored Apr 25, 2023
2 parents 97086df + e22779c commit 2010ddc
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 38 deletions.
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/cmds/start/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,23 @@ import { IBundlerArgs } from "./index";
export async function bundlerHandler(
args: IBundlerArgs & IGlobalArgs
): Promise<void> {
const { dataDir, networksFile, testingMode } = args;
const { dataDir, networksFile, testingMode, unsafeMode } = args;

let config: Config;
try {
const configPath = path.resolve(dataDir, networksFile);
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,
});
}

Expand Down
9 changes: 8 additions & 1 deletion packages/cli/src/cmds/start/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
5 changes: 4 additions & 1 deletion packages/executor/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions packages/executor/src/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -82,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,
Expand Down
12 changes: 3 additions & 9 deletions packages/executor/src/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -90,7 +87,7 @@ export class Eth {
verificationGasLimit: 10e6,
};
const { returnInfo } =
await this.userOpValidationService.callSimulateValidation(
await this.userOpValidationService.simulateValidation(
userOpComplemented,
entryPoint
);
Expand Down Expand Up @@ -142,10 +139,7 @@ export class Eth {
RpcErrorCodes.INVALID_USEROP
);
}
await this.userOpValidationService.simulateCompleteValidation(
userOp,
entryPoint
);
await this.userOpValidationService.simulateValidation(userOp, entryPoint);
return true;
}

Expand Down
6 changes: 4 additions & 2 deletions packages/executor/src/modules/web3.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 1 addition & 1 deletion packages/executor/src/services/BundlingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 16 additions & 3 deletions packages/executor/src/services/UserOpValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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<UserOpValidationResult> {
if (this.config.unsafeMode) {
return this.simulateUnsafeValidation(userOp, entryPoint);
}
return this.simulateSafeValidation(userOp, entryPoint, codehash);
}

async simulateUnsafeValidation(
userOp: UserOperationStruct,
entryPoint: string
): Promise<UserOpValidationResult> {
Expand All @@ -71,7 +84,7 @@ export class UserOpValidationService {
return this.parseErrorResult(userOp, errorResult);
}

async simulateCompleteValidation(
async simulateSafeValidation(
userOp: UserOperationStruct,
entryPoint: string,
codehash?: string
Expand Down
61 changes: 52 additions & 9 deletions packages/executor/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = {
Expand All @@ -45,11 +66,11 @@ export function packUserOp(
name: "nonce",
},
{
type: "bytes",
type: "bytes32",
name: "initCode",
},
{
type: "bytes",
type: "bytes32",
name: "callData",
},
{
Expand All @@ -73,7 +94,7 @@ export function packUserOp(
name: "maxPriorityFeePerGas",
},
{
type: "bytes",
type: "bytes32",
name: "paymasterAndData",
},
{
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion packages/types/src/networks/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export type NetworkName =
| "neonDevnet"
| "optimismGoerli"
| "dev"
| "baseGoerli";
| "baseGoerli"
| "sepolia";

export const networkNames: NetworkName[] = [
"mainnet",
Expand Down Expand Up @@ -55,6 +56,7 @@ export const networkNames: NetworkName[] = [
"optimismGoerli",
"dev",
"baseGoerli",
"sepolia",
];

export const NETWORK_NAME_TO_CHAIN_ID: {
Expand Down Expand Up @@ -87,4 +89,5 @@ export const NETWORK_NAME_TO_CHAIN_ID: {
optimismGoerli: 420,
dev: 1337,
baseGoerli: 84531,
sepolia: 11155111,
};

0 comments on commit 2010ddc

Please sign in to comment.