Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix: now passing spec test_ban_user_op_access_other_ops_sender_in_bundle #79

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/bundler-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"build": "npx tsc -p ./tsconfig.json && cp ../shared/sim/tracer.js dist/shared/sim",
"dev": "nodemon",
"start": "node --import ./register-hook.js ./src/index.ts --txMode base | pino-pretty",
"start:unsafe": "node --import ./register-hook.js ./src/index.ts --txMode --unsafe | pino-pretty",
"start:unsafe": "node --import ./register-hook.js ./src/index.ts --txMode base --unsafe | pino-pretty",
"start:prod": "yarn build && node dist/bundler-relayer/src/index.js --txMode base| pino-pretty",
"test": "vitest --config ./vitest.config.ts",
"lint": "eslint . --ext ts",
Expand Down
12 changes: 8 additions & 4 deletions packages/bundler-builder/src/bundle/bundle-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const createBundleBuilder = (
}

// TODO: Include entries based off highest fee
// if isAuto is true, send the all pending UserOps in the mempool as a bundle
// if force is true, send the all pending UserOps in the mempool as a bundle
const entries: MempoolEntry[] = force
? await mempoolManager.getAllPending()
: await mempoolManager.getNextPending()
Expand Down Expand Up @@ -75,11 +75,12 @@ export const createBundleBuilder = (
const paymasterStatus = await reputationManager.getStatus(paymaster)
const deployerStatus = await reputationManager.getStatus(factory)

// Remove UserOps from mempool if paymaster or deployer is banned
if (
paymasterStatus === ReputationStatus.BANNED ||
deployerStatus === ReputationStatus.BANNED
) {
await mempoolManager.removeUserOp(entry.userOp)
await mempoolManager.removeUserOp(entry.userOpHash)
continue
}

Expand All @@ -96,6 +97,7 @@ export const createBundleBuilder = (
notIncludedUserOpsHashes.push(entry.userOpHash)
continue
}

// [SREP-030]
if (
factory != null &&
Expand All @@ -109,8 +111,9 @@ export const createBundleBuilder = (
notIncludedUserOpsHashes.push(entry.userOpHash)
continue
}

// allow only a single UserOp per sender per bundle
if (senders.has(entry.userOp.sender)) {
// allow only a single UserOp per sender per bundle
Logger.debug(
{ sender: entry.userOp.sender, nonce: entry.userOp.nonce },
'skipping already included sender',
Expand Down Expand Up @@ -138,7 +141,7 @@ export const createBundleBuilder = (
continue
}

// Check if the UserOp accesses a storage of another known sender
// [STO-041] Check if the UserOp accesses a storage of another known sender and ban the sender if so
for (const storageAddress of Object.keys(validationResult.storageMap)) {
if (
storageAddress.toLowerCase() !==
Expand All @@ -148,6 +151,7 @@ export const createBundleBuilder = (
Logger.debug(
`UserOperation from ${entry.userOp.sender} sender accessed a storage of another known sender ${storageAddress}`,
)
notIncludedUserOpsHashes.push(entry.userOpHash)
continue mainLoop
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/bundler-builder/src/bundle/bundle-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SendBundleReturn } from '../../../shared/types/index.js'

import { BundleBuilder } from './bundle-builder.js'
import { BundleProcessor } from './bundle-processor.js'
import { EventManagerWithListener } from '../event/index.js'

export type BundleManager = {
/**
Expand All @@ -27,6 +28,7 @@ export type BundleManager = {
export const createBundleManager = (
bundleProcessor: BundleProcessor,
bundleBuilder: BundleBuilder,
eventsManager: EventManagerWithListener,
mp: MempoolStateService,
isAutoBundle: boolean,
autoBundleInterval: number,
Expand All @@ -37,7 +39,8 @@ export const createBundleManager = (
const doAttemptBundle = async (
force?: boolean,
): Promise<SendBundleReturn> => {
// TODO: Flush the mempool to remove bundled and failed userOps
// Flush the mempool to remove succeful userOps update failed userOps status
await eventsManager.handlePastEvents()

const [bundle, storageMap] = await bundleBuilder.createBundle(force)
Logger.debug({ length: bundle.length }, 'bundle created(ready to send)')
Expand Down
56 changes: 34 additions & 22 deletions packages/bundler-builder/src/bundle/bundle-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,37 +98,49 @@ export const createBundleProcessor = (
const beneficiary = await selectBeneficiary(signer)

try {
const feeData = await providerService.getFeeData()
const [feeData, chainId, nonce] = await Promise.all([
providerService.getFeeData(),
providerService.getChainId(),
ss.getTransactionCount(signer),
])

// populate the transaction (e.g to, data, and value)
const tx = await entryPointContract.populateTransaction.handleOps(
packUserOps(userOps),
beneficiary,
)

const signedTx = await ss.signTransaction(
{
...tx,
chainId,
type: 2,
nonce: await ss.getTransactionCount(signer),
gasLimit: 10e6,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0,
maxFeePerGas: feeData.maxFeePerGas ?? 0,
nonce,
gasLimit: BigNumber.from(10e6),
maxPriorityFeePerGas:
feeData.maxPriorityFeePerGas ?? BigNumber.from(0),
maxFeePerGas: feeData.maxFeePerGas ?? BigNumber.from(0),
},
signer,
)
tx.chainId = await providerService.getChainId()
const signedTx = await ss.signTransaction(tx, signer)

let ret: string
if (txMode === 'conditional') {
ret = await providerService.send(
'eth_sendRawTransactionConditional',
[signedTx, { knownAccounts: storageMap }],
)
Logger.debug(
{ ret, length: userOps.length },
'eth_sendRawTransactionConditional ret=',
)
} else {
ret = await providerService.send('eth_sendRawTransaction', [signedTx])
Logger.debug(
{ ret, length: userOps.length },
'eth_sendRawTransaction ret=',
)
switch (txMode) {
case 'conditional':
ret = await providerService.send(
'eth_sendRawTransactionConditional',
[signedTx, { knownAccounts: storageMap }],
)
break
case 'base':
ret = await providerService.send('eth_sendRawTransaction', [
signedTx,
])
break
case 'searcher':
throw new Error('searcher txMode is not supported')
default:
throw new Error(`unknown txMode: ${txMode}`)
}

// TODO: parse ret, and revert if needed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,20 @@ export const createEventManagerWithListener = (
const handleUserOperationEvent = async (ev: any): Promise<void> => {
const userOpHash = ev.args.userOpHash
const sucess = ev.args.success
await mempoolManager.updateEntryStatus(
userOpHash,
sucess ? 'bundled' : 'failed',
)

if (sucess) {
Logger.debug(
{ userOpHash },
'UserOperationEvent success. Removing from mempool',
)
await mempoolManager.removeUserOp(userOpHash)
} else {
Logger.debug(
{ userOpHash },
'UserOperationEvent failed. Updating status in mempool',
)
await mempoolManager.updateEntryStatus(userOpHash, 'failed')
}

// TODO: Make this a batch operation
includedAddress(ev.args.sender)
Expand Down
4 changes: 4 additions & 0 deletions packages/bundler-builder/src/handler/apis/Debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export const createDebugAPI = (

sendBundleNow: async (): Promise<SendBundleReturn> => {
const result = await bundleManager.doAttemptBundle(true)

// handlePastEvents is called before building the next bundle.
// However in debug mode, we are interested in the side effects
// (on the mempool) of this "sendBundle" operation
await eventsManager.handlePastEvents()
return result
},
Expand Down
4 changes: 2 additions & 2 deletions packages/bundler-builder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const runBundlerBuilder = async () => {
config.txMode,
config.entryPointContract,
),
eventManager,
mempoolState,
config.isAutoBundle,
config.autoBundleInterval,
Expand Down Expand Up @@ -120,9 +121,8 @@ const runBundlerBuilder = async () => {
}
}

// full validation requires (debug_traceCall) method on eth node geth and can only be run in private and conditional txMode
// safe mode: full validation requires (debug_traceCall) method on eth node geth
if (
config.txMode === 'searcher' &&
!config.isUnsafeMode &&
!(await ps.supportsRpcMethod('debug_traceCall'))
) {
Expand Down
4 changes: 4 additions & 0 deletions packages/bundler-relayer/src/handler/apis/Eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ export const createEthAPI = (
'Missing/invalid userOpHash',
ValidationErrors.InvalidFields,
)

// TODO: First check if the userOp is pending in the mempool
// if so the UserOperation is pending in the bundler’s mempool:
// MAY return null, or: a full UserOperation, with the addition of the entryPoint field and a null value for blockNumber, blockHash and transactionHash.
const event = await commonEventsManager.getUserOperationEvent(userOpHash)
if (event == null) {
return null
Expand Down
Loading