Skip to content

Commit

Permalink
PR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
drortirosh committed Jul 4, 2023
1 parent 9ade4d1 commit 633fb4d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 30 deletions.
17 changes: 7 additions & 10 deletions packages/bundler/src/BundlerCollectorTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export interface BundlerCollectorReturn {
/**
* storage and opcode info, collected on top-level calls from EntryPoint
*/
topLevelCalls: TopLevelCallInfo[]
callsFromEntryPoint: TopLevelCallInfo[]

/**
* values passed into KECCAK opcode
*/
Expand Down Expand Up @@ -94,7 +95,7 @@ interface BundlerCollectorTracer extends LogTracer, BundlerCollectorReturn {
*/
export function bundlerCollectorTracer (): BundlerCollectorTracer {
return {
topLevelCalls: [],
callsFromEntryPoint: [],
currentLevel: null as any,
keccak: [],
calls: [],
Expand All @@ -112,7 +113,7 @@ export function bundlerCollectorTracer (): BundlerCollectorTracer {

result (ctx: LogContext, db: LogDb): BundlerCollectorReturn {
return {
topLevelCalls: this.topLevelCalls,
callsFromEntryPoint: this.callsFromEntryPoint,
keccak: this.keccak,
logs: this.logs,
calls: this.calls,
Expand Down Expand Up @@ -182,11 +183,10 @@ export function bundlerCollectorTracer (): BundlerCollectorTracer {
const topLevelTargetAddress = toHex(addr)
// stack.peek(2) - value
const ofs = parseInt(log.stack.peek(3).toString())
let len = parseInt(log.stack.peek(4).toString())
if (len > 4) len = 4
const topLevelMethodSig = toHex(log.memory.slice(ofs, ofs + len))
// stack.peek(4) - len
const topLevelMethodSig = toHex(log.memory.slice(ofs, ofs + 4))

this.currentLevel = this.topLevelCalls[this.topLevelCallCounter] = {
this.currentLevel = this.callsFromEntryPoint[this.topLevelCallCounter] = {
topLevelMethodSig,
topLevelTargetAddress,
access: {},
Expand All @@ -212,9 +212,6 @@ export function bundlerCollectorTracer (): BundlerCollectorTracer {
const addrHex = toHex(addr)
if ((this.currentLevel.contractSize[addrHex] ?? 0) === 0 && !isPrecompiled(addr)) {
this.currentLevel.contractSize[addrHex] = db.getCode(addr).length
if (this.currentLevel.contractSize[addrHex] === 0) {
this.debug.push(`empty addr: ${opcode} ${addrHex}`)
}
}
}

Expand Down
28 changes: 13 additions & 15 deletions packages/bundler/src/parseScannerResult.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
EntryPoint,
EntryPoint, IAccount__factory,
IEntryPoint__factory,
IPaymaster__factory, SenderCreator__factory
} from '@account-abstraction/contracts'
Expand Down Expand Up @@ -153,6 +153,13 @@ function parseEntitySlots (stakeInfoEntities: { [addr: string]: StakeInfo | unde
return entitySlots
}

// method-signature for calls from entryPoint
const callsFromEntryPointMethodSigs: {[key: string]: string} = {
factory: SenderCreator__factory.createInterface().getSighash('createSender'),
account: IAccount__factory.createInterface().getSighash('validateUserOp'),
paymaster: IPaymaster__factory.createInterface().getSighash('validatePaymasterUserOp')
}

/**
* parse collected simulation traces and revert if they break our rules
* @param userOp the userOperation that was used in this simulation
Expand All @@ -170,10 +177,8 @@ export function parseScannerResult (userOp: UserOperation, tracerResults: Bundle
const bannedOpCodes = new Set(['GASPRICE', 'GASLIMIT', 'DIFFICULTY', 'TIMESTAMP', 'BASEFEE', 'BLOCKHASH', 'NUMBER', 'SELFBALANCE', 'BALANCE', 'ORIGIN', 'GAS', 'CREATE', 'COINBASE', 'SELFDESTRUCT', 'RANDOM', 'PREVRANDAO'])

// eslint-disable-next-line @typescript-eslint/no-base-to-string
if (Object.values(tracerResults.topLevelCalls).length < 1) {
// console.log('calls=', result.calls.map(x=>JSON.stringify(x)).join('\n'))
// console.log('debug=', result.debug)
throw new Error('Unexpected traceCall result: no NUMBER opcodes, and not REVERT')
if (Object.values(tracerResults.callsFromEntryPoint).length < 1) {
throw new Error('Unexpected traceCall result: no calls from entrypoint.')
}
const callStack = parseCallStack(tracerResults)

Expand All @@ -200,18 +205,11 @@ export function parseScannerResult (userOp: UserOperation, tracerResults: Bundle
paymaster: validationResult.paymasterInfo
}

// method-signature for entity calls.
const topLevelMethodSigs: {[key: string]: string} = {
factory: '0x570e1a36', // createSender
account: '0x3a871cdd', // validateUserOp'
paymaster: '0xf465c77e' // validatePaymasterUserOp
}

const entitySlots: { [addr: string]: Set<string> } = parseEntitySlots(stakeInfoEntities, tracerResults.keccak)

Object.entries(stakeInfoEntities).forEach(([entityTitle, entStakes]) => {
const entityAddr = entStakes?.addr ?? ''
const currentNumLevel = tracerResults.topLevelCalls.find(info => info.topLevelMethodSig === topLevelMethodSigs[entityTitle])
const currentNumLevel = tracerResults.callsFromEntryPoint.find(info => info.topLevelMethodSig === callsFromEntryPointMethodSigs[entityTitle])
if (currentNumLevel == null) {
if (entityTitle === 'account') {
// should never happen... only factory, paymaster are optional.
Expand Down Expand Up @@ -345,9 +343,9 @@ export function parseScannerResult (userOp: UserOperation, tracerResults: Bundle
})

// return list of contract addresses by this UserOp. already known not to contain zero-sized addresses.
const addresses = tracerResults.topLevelCalls.flatMap(level => Object.keys(level.contractSize))
const addresses = tracerResults.callsFromEntryPoint.flatMap(level => Object.keys(level.contractSize))
const storageMap: StorageMap = {}
tracerResults.topLevelCalls.forEach(level => {
tracerResults.callsFromEntryPoint.forEach(level => {
Object.keys(level.access).forEach(addr => {
storageMap[addr] = storageMap[addr] ?? level.access[addr].reads
})
Expand Down
10 changes: 5 additions & 5 deletions packages/bundler/test/tracer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ describe('#bundlerCollectorTracer', () => {
const ret = await traceExecSelf(tester.interface.encodeFunctionData('callTimeStamp'), false, true)
const execEvent = tester.interface.decodeEventLog('ExecSelfResult', ret.logs[0].data, ret.logs[0].topics)
expect(execEvent.success).to.equal(true)
expect(ret.topLevelCalls[0].opcodes.TIMESTAMP).to.equal(1)
expect(ret.callsFromEntryPoint[0].opcodes.TIMESTAMP).to.equal(1)
})

it('should not count opcodes on depth==1', async () => {
const ret = await traceCall(tester.interface.encodeFunctionData('callTimeStamp'))
expect(ret.topLevelCalls[0]?.opcodes.TIMESTAMP).to.be.undefined
expect(ret.callsFromEntryPoint[0]?.opcodes.TIMESTAMP).to.be.undefined
// verify no error..
expect(ret.debug.toString()).to.not.match(/REVERT/)
})
Expand Down Expand Up @@ -79,21 +79,21 @@ describe('#bundlerCollectorTracer', () => {

it('should report direct use of GAS opcode', async () => {
const ret = await traceExecSelf(tester.interface.encodeFunctionData('testCallGas'), false)
expect(ret.topLevelCalls['0'].opcodes.GAS).to.eq(1)
expect(ret.callsFromEntryPoint['0'].opcodes.GAS).to.eq(1)
})

it('should ignore gas used as part of "call"', async () => {
// call the "testKeccak" function as a sample inner function
const doNothing = tester.interface.encodeFunctionData('doNothing')
const callDoNothing = tester.interface.encodeFunctionData('execSelf', [doNothing, false])
const ret = await traceExecSelf(callDoNothing, false)
expect(ret.topLevelCalls['0'].opcodes.GAS).to.be.undefined
expect(ret.callsFromEntryPoint['0'].opcodes.GAS).to.be.undefined
})

it('should collect traces only until BeginExecution event', async () => {
// the method calls "callTimeStamp" 3 times, but should stop tracing after 2 times..
const callStopTracing = tester.interface.encodeFunctionData('testStopTracing')
const ret = await traceCall(callStopTracing)
expect(ret.topLevelCalls.length).to.eql(2)
expect(ret.callsFromEntryPoint.length).to.eql(2)
})
})

0 comments on commit 633fb4d

Please sign in to comment.