-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
YAS-335 Improvements to Ethereum event endpoints #116
Changes from 8 commits
571a68a
cce8691
9d3fd63
7c1b3cc
021917a
ba3f5de
ebd81f3
ec8c861
36ee32e
b4bddc1
ee41892
96cb62a
051bbfd
7baf8d2
212f700
9b8180c
04fa273
6c4914d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* External Imports */ | ||
import { utils, ContractFactory } from 'ethers' | ||
|
||
/* Internal Imports */ | ||
import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' | ||
|
||
const EMContract = new ContractFactory( | ||
ExecutionManager.abi, | ||
ExecutionManager.bytecode | ||
) | ||
const EMEvents = EMContract.interface.events | ||
let topics = [] | ||
for (const eventKey of Object.keys(EMEvents)) { | ||
topics.push(EMEvents[eventKey].topic) | ||
} | ||
|
||
export const ALL_EXECUTION_MANAGER_EVENT_TOPICS = topics |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ import { | |
ZERO_ADDRESS, | ||
LOG_NEWLINE_STRING, | ||
BloomFilter, | ||
hexStrToNumber, | ||
} from '@eth-optimism/core-utils' | ||
import { ethers } from 'ethers' | ||
import { LogDescription } from 'ethers/utils' | ||
|
@@ -61,34 +62,64 @@ export interface OvmTransactionMetadata { | |
} | ||
|
||
/** | ||
* Convert internal logs into OVM logs. Or in other words, take the logs which | ||
* Convert internal transaction logs into OVM logs. Or in other words, take the logs which | ||
* are emitted by a normal Ganache or Geth node (this will include logs from the ExecutionManager), | ||
* parse them, and then convert them into logs which look like they would if you were running this tx | ||
* using an OVM backend. | ||
* | ||
* NOTE: The input logs MUST NOT be stripped of any Execution Manager events, or this function will break. | ||
* | ||
* @param logs an array of internal logs which we will parse and then convert. | ||
* @param logs an array of internal transaction logs which we will parse and then convert. | ||
* @return the converted logs | ||
*/ | ||
export const convertInternalLogsToOvmLogs = (logs: Log[]): Log[] => { | ||
export const convertInternalLogsToOvmLogs = ( | ||
logs: Log[], | ||
executionManagerAddress: string | ||
): Log[] => { | ||
let activeContract = logs[0] ? logs[0].address : ZERO_ADDRESS | ||
const loggerLogs = [`Parsing logs from contract ${activeContract}: `] | ||
const ovmLogs = [] | ||
let cumulativeTxEMLogIndices = 0 | ||
let prevEMLogIndex = 0 | ||
logs.forEach((log) => { | ||
const executionManagerLog = executionManagerInterface.parseLog(log) | ||
if (executionManagerLog) { | ||
if (executionManagerLog.name === 'ActiveContract') { | ||
activeContract = executionManagerLog.values['_activeContract'] | ||
if (log.address.toUpperCase() === executionManagerAddress.toUpperCase()) { | ||
const EMLogIndex = log.logIndex | ||
if (EMLogIndex < prevEMLogIndex) { | ||
logger.debug( | ||
`Detected raw EM log ${log} with lower logIndex than previously processed, must be from a new transaction. Resetting cumulative EM log indices for tx.` | ||
) | ||
cumulativeTxEMLogIndices = 0 | ||
} | ||
cumulativeTxEMLogIndices++ | ||
prevEMLogIndex = EMLogIndex | ||
const executionManagerLog = executionManagerInterface.parseLog(log) | ||
if (!executionManagerLog) { | ||
logger.debug( | ||
`execution manager log ${log} was unrecognized by the interface parser--Definitely not an activeContract event, ignoring...` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think ${log} will just print |
||
) | ||
} else { | ||
loggerLogs.push( | ||
`${executionManagerLog.name}, values: ${JSON.stringify( | ||
executionManagerLog.values | ||
)}` | ||
)} and cumulativeTxEMLogIndices: ${cumulativeTxEMLogIndices}` | ||
) | ||
if (executionManagerLog.name === 'ActiveContract') { | ||
activeContract = executionManagerLog.values['_activeContract'] | ||
logger.debug( | ||
`EM activeContract event detected, setting activeContract to ${activeContract}` | ||
) | ||
} else { | ||
logger.debug(`EM-but-non-activeContract event detected, ignoring...`) | ||
} | ||
} | ||
} else { | ||
loggerLogs.push(`Non-EM log: ${JSON.stringify(log)}`) | ||
ovmLogs.push({ ...log, address: activeContract }) | ||
const newIndex = log.logIndex - cumulativeTxEMLogIndices | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👌👌👌👌👌 |
||
loggerLogs.push( | ||
`Non-EM log: ${JSON.stringify( | ||
log | ||
)}. Using address of active contract ${activeContract} and log index ${newIndex}` | ||
) | ||
ovmLogs.push({ ...log, address: activeContract, logIndex: newIndex }) | ||
} | ||
}) | ||
logger.debug(loggerLogs.join(LOG_NEWLINE_STRING)) | ||
|
@@ -178,17 +209,21 @@ export const getSuccessfulOvmTransactionMetadata = ( | |
*/ | ||
export const internalTxReceiptToOvmTxReceipt = async ( | ||
internalTxReceipt: TransactionReceipt, | ||
executionManagerAddress: string, | ||
ovmTxHash?: string | ||
): Promise<OvmTransactionReceipt> => { | ||
const ovmTransactionMetadata = getSuccessfulOvmTransactionMetadata( | ||
internalTxReceipt | ||
) | ||
// Construct a new receipt | ||
// | ||
|
||
// Start off with the internalTxReceipt | ||
const ovmTxReceipt: OvmTransactionReceipt = internalTxReceipt | ||
// Add the converted logs | ||
ovmTxReceipt.logs = convertInternalLogsToOvmLogs(internalTxReceipt.logs) | ||
ovmTxReceipt.logs = convertInternalLogsToOvmLogs( | ||
internalTxReceipt.logs, | ||
executionManagerAddress | ||
) | ||
// Update the to and from fields if necessary | ||
if (ovmTransactionMetadata.ovmTo) { | ||
ovmTxReceipt.to = ovmTransactionMetadata.ovmTo | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import { | |
internalTxReceiptToOvmTxReceipt, | ||
l2ToL1MessagePasserInterface, | ||
OvmTransactionReceipt, | ||
ALL_EXECUTION_MANAGER_EVENT_TOPICS, | ||
} from '@eth-optimism/ovm' | ||
|
||
import AsyncLock from 'async-lock' | ||
|
@@ -42,6 +43,7 @@ import { | |
Web3RpcTypes, | ||
Web3RpcMethods, | ||
RevertError, | ||
UnsupportedFilterError, | ||
} from '../types' | ||
import { initializeL2Node, getCurrentTime, isErrorEVMRevert } from './util' | ||
import { NoOpL2ToL1MessageSubmitter } from './message-submitter' | ||
|
@@ -494,20 +496,55 @@ export class DefaultWeb3Handler | |
} | ||
|
||
public async getLogs(ovmFilter: any): Promise<any[]> { | ||
log.debug(`Requesting logs with filter [${JSON.stringify(ovmFilter)}].`) | ||
const filter = JSON.parse(JSON.stringify(ovmFilter)) | ||
// We cannot filter out execution manager events or else convertInternalLogsToOvmLogs will break. So add EM address to address filter | ||
if (filter['address']) { | ||
const codeContractAddress = await this.context.executionManager.getCodeContractAddress( | ||
filter.address | ||
) | ||
filter['address'] = codeContractAddress | ||
if (!Array.isArray(filter['address'])) { | ||
filter['address'] = [filter['address']] | ||
} | ||
const codeContractAddresses = [] | ||
for (const address of filter['address']) { | ||
codeContractAddresses.push( | ||
await this.context.executionManager.getCodeContractAddress(address) | ||
) | ||
} | ||
filter['address'] = [ | ||
...codeContractAddresses, | ||
this.context.executionManager.address, | ||
] | ||
} | ||
// We cannot filter out execution manager events or else convertInternalLogsToOvmLogs will break. So add EM topics to topics filter | ||
if (filter['topics']) { | ||
if (filter['topics'].length > 1) { | ||
// todo make this proper error | ||
const msg = `The provided filter ${filter} has multiple levels of topic filter. Multi-level topic filters are currently unsupported by the OVM.` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should you log |
||
throw new UnsupportedFilterError(msg) | ||
} | ||
if (!Array.isArray(filter['topics'][0])) { | ||
filter['topics'][0] = [JSON.parse(JSON.stringify(filter['topics'][0]))] | ||
} | ||
filter['topics'][0].push(...ALL_EXECUTION_MANAGER_EVENT_TOPICS) | ||
} | ||
log.debug( | ||
`Converted ovm filter ${JSON.stringify( | ||
ovmFilter | ||
)} to internal filter ${JSON.stringify(filter)}` | ||
) | ||
|
||
const res = await this.context.provider.send(Web3RpcMethods.getLogs, [ | ||
filter, | ||
]) | ||
|
||
let logs = JSON.parse(JSON.stringify(convertInternalLogsToOvmLogs(res))) | ||
log.debug(`Log result: [${logs}], filter: [${JSON.stringify(filter)}].`) | ||
let logs = JSON.parse( | ||
JSON.stringify( | ||
convertInternalLogsToOvmLogs(res, this.context.executionManager.address) | ||
) | ||
) | ||
log.debug( | ||
`Log result: [${JSON.stringify(logs)}], filter: [${JSON.stringify( | ||
filter | ||
)}].` | ||
) | ||
logs = await Promise.all( | ||
logs.map(async (logItem, index) => { | ||
logItem['logIndex'] = numberToHexString(index) | ||
|
@@ -608,6 +645,7 @@ export class DefaultWeb3Handler | |
) | ||
ovmTxReceipt = await internalTxReceiptToOvmTxReceipt( | ||
internalTxReceipt, | ||
this.context.executionManager.address, | ||
ovmTxHash | ||
) | ||
} else { | ||
|
@@ -807,7 +845,8 @@ export class DefaultWeb3Handler | |
|
||
try { | ||
const ovmTxReceipt: OvmTransactionReceipt = await internalTxReceiptToOvmTxReceipt( | ||
txReceipt | ||
txReceipt, | ||
this.context.executionManager.address | ||
) | ||
await this.processTransactionEvents(ovmTxReceipt) | ||
} catch (e) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should tihs be
<=
? E.g. if prevEMlogIndex is 0, and EMLogIndex is 0, it's still a new transaction, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch!