Commit 89920491 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #1509 from ethereum-optimism/develop

Develop -> Master PR
parents 09e1a6c3 a7c7cd6e
---
'@eth-optimism/l2geth': patch
---
Fix execution manager run
---
'@eth-optimism/core-utils': patch
'@eth-optimism/data-transport-layer': patch
---
Add fallback provider support to DTL using helper function in core-utils
---
'@eth-optimism/batch-submitter': patch
---
Removes the call to `appendQueueBatch` from the batch submitter
---
'@eth-optimism/contracts': patch
---
contracts: remove l1 contracts from l2 state dump process
---
'@eth-optimism/message-relayer': patch
---
added coverage for getMerkleTreeProof
---
'@eth-optimism/core-utils': minor
---
Allow a configurable L1 and L2 blocks to fetch in the watcher
...@@ -23,12 +23,20 @@ type ovmTransaction struct { ...@@ -23,12 +23,20 @@ type ovmTransaction struct {
} }
func toExecutionManagerRun(evm *vm.EVM, msg Message) (Message, error) { func toExecutionManagerRun(evm *vm.EVM, msg Message) (Message, error) {
to := msg.To()
if to == nil {
to = &common.Address{}
}
l1MsgSender := msg.L1MessageSender()
if l1MsgSender == nil {
l1MsgSender = &common.Address{}
}
tx := ovmTransaction{ tx := ovmTransaction{
evm.Context.Time, evm.Context.Time,
msg.L1BlockNumber(), msg.L1BlockNumber(),
uint8(msg.QueueOrigin()), uint8(msg.QueueOrigin()),
*msg.L1MessageSender(), *l1MsgSender,
*msg.To(), *to,
big.NewInt(int64(msg.Gas())), big.NewInt(int64(msg.Gas())),
msg.Data(), msg.Data(),
} }
......
...@@ -34,7 +34,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -34,7 +34,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
protected chainContract: CanonicalTransactionChainContract protected chainContract: CanonicalTransactionChainContract
protected l2ChainId: number protected l2ChainId: number
protected syncing: boolean protected syncing: boolean
private disableQueueBatchAppend: boolean
private autoFixBatchOptions: AutoFixBatchOptions private autoFixBatchOptions: AutoFixBatchOptions
private transactionSubmitter: TransactionSubmitter private transactionSubmitter: TransactionSubmitter
private gasThresholdInGwei: number private gasThresholdInGwei: number
...@@ -55,7 +54,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -55,7 +54,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
blockOffset: number, blockOffset: number,
logger: Logger, logger: Logger,
metrics: Metrics, metrics: Metrics,
disableQueueBatchAppend: boolean,
autoFixBatchOptions: AutoFixBatchOptions = { autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: false, fixDoublePlayedDeposits: false,
fixMonotonicity: false, fixMonotonicity: false,
...@@ -78,7 +76,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -78,7 +76,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
logger, logger,
metrics metrics
) )
this.disableQueueBatchAppend = disableQueueBatchAppend
this.autoFixBatchOptions = autoFixBatchOptions this.autoFixBatchOptions = autoFixBatchOptions
this.gasThresholdInGwei = gasThresholdInGwei this.gasThresholdInGwei = gasThresholdInGwei
this.transactionSubmitter = transactionSubmitter this.transactionSubmitter = transactionSubmitter
...@@ -137,11 +134,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -137,11 +134,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
'Syncing mode enabled! Skipping batch submission and clearing queue elements', 'Syncing mode enabled! Skipping batch submission and clearing queue elements',
{ pendingQueueElements } { pendingQueueElements }
) )
if (!this.disableQueueBatchAppend) {
return this.submitAppendQueueBatch()
}
} }
this.logger.info('Syncing mode enabled but queue is empty. Skipping...') this.logger.info('Syncing mode enabled but queue is empty. Skipping...')
return return
} }
...@@ -229,20 +223,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -229,20 +223,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
* Private Functions * * Private Functions *
********************/ ********************/
private async submitAppendQueueBatch(): Promise<TransactionReceipt> {
const tx = await this.chainContract.populateTransaction.appendQueueBatch(
ethers.constants.MaxUint256 // Completely empty the queue by appending (up to) an enormous number of queue elements.
)
const submitTransaction = (): Promise<TransactionReceipt> => {
return this.transactionSubmitter.submitTransaction(
tx,
this._makeHooks('appendQueueBatch')
)
}
// Empty the queue with a huge `appendQueueBatch(..)` call
return this._submitAndLogTx(submitTransaction, 'Cleared queue!')
}
private async submitAppendSequencerBatch( private async submitAppendSequencerBatch(
batchParams: AppendSequencerBatchParams batchParams: AppendSequencerBatchParams
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
......
...@@ -64,7 +64,6 @@ interface RequiredEnvVars { ...@@ -64,7 +64,6 @@ interface RequiredEnvVars {
/* Optional Env Vars /* Optional Env Vars
* FRAUD_SUBMISSION_ADDRESS * FRAUD_SUBMISSION_ADDRESS
* DISABLE_QUEUE_BATCH_APPEND
* SEQUENCER_PRIVATE_KEY * SEQUENCER_PRIVATE_KEY
* PROPOSER_PRIVATE_KEY * PROPOSER_PRIVATE_KEY
* MNEMONIC * MNEMONIC
...@@ -201,10 +200,6 @@ export const run = async () => { ...@@ -201,10 +200,6 @@ export const run = async () => {
'fraud-submisison-address', 'fraud-submisison-address',
env.FRAUD_SUBMISSION_ADDRESS || 'no fraud' env.FRAUD_SUBMISSION_ADDRESS || 'no fraud'
) )
const DISABLE_QUEUE_BATCH_APPEND = config.bool(
'disable-queue-batch-append',
!!env.DISABLE_QUEUE_BATCH_APPEND
)
const MIN_GAS_PRICE_IN_GWEI = config.uint( const MIN_GAS_PRICE_IN_GWEI = config.uint(
'min-gas-price-in-gwei', 'min-gas-price-in-gwei',
parseInt(env.MIN_GAS_PRICE_IN_GWEI, 10) || 0 parseInt(env.MIN_GAS_PRICE_IN_GWEI, 10) || 0
...@@ -389,7 +384,6 @@ export const run = async () => { ...@@ -389,7 +384,6 @@ export const run = async () => {
BLOCK_OFFSET, BLOCK_OFFSET,
logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }), logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
metrics, metrics,
DISABLE_QUEUE_BATCH_APPEND,
autoFixBatchOptions autoFixBatchOptions
) )
......
...@@ -230,7 +230,6 @@ describe('BatchSubmitter', () => { ...@@ -230,7 +230,6 @@ describe('BatchSubmitter', () => {
1, 1,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }), new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
testMetrics, testMetrics,
false
) )
} }
......
...@@ -74,52 +74,6 @@ export const makeContractDeployConfig = async ( ...@@ -74,52 +74,6 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_L2CrossDomainMessenger'), factory: getContractFactory('OVM_L2CrossDomainMessenger'),
params: [AddressManager.address], params: [AddressManager.address],
}, },
OVM_L1CrossDomainMessenger: {
factory: getContractFactory('OVM_L1CrossDomainMessenger'),
params: [],
afterDeploy: async (contracts): Promise<void> => {
if (config.l1CrossDomainMessengerConfig.relayerAddress) {
const relayer = config.l1CrossDomainMessengerConfig.relayerAddress
const address =
typeof relayer === 'string' ? relayer : await relayer.getAddress()
await _sendTx(
AddressManager.setAddress('OVM_L2MessageRelayer', address)
)
}
},
},
Proxy__OVM_L1CrossDomainMessenger: {
factory: getContractFactory('Lib_ResolvedDelegateProxy'),
params: [AddressManager.address, 'OVM_L1CrossDomainMessenger'],
afterDeploy: async (contracts): Promise<void> => {
const xDomainMessenger = getContractFactory(
'OVM_L1CrossDomainMessenger'
)
.connect(config.deploymentSigner)
.attach(contracts.Proxy__OVM_L1CrossDomainMessenger.address)
await _sendTx(
xDomainMessenger.initialize(
AddressManager.address,
config.deployOverrides
)
)
await _sendTx(
AddressManager.setAddress(
'OVM_L2CrossDomainMessenger',
config.ovmGlobalContext.L2CrossDomainMessengerAddress,
config.deployOverrides
)
)
},
},
OVM_L1StandardBridge: {
factory: getContractFactory('OVM_L1StandardBridge'),
params: [],
},
Proxy__OVM_L1StandardBridge: {
factory: getContractFactory('Lib_ResolvedDelegateProxy'),
params: [AddressManager.address, 'OVM_L1StandardBridge'],
},
OVM_L2StandardBridge: { OVM_L2StandardBridge: {
factory: getContractFactory('OVM_L2StandardBridge'), factory: getContractFactory('OVM_L2StandardBridge'),
params: [ params: [
...@@ -127,47 +81,6 @@ export const makeContractDeployConfig = async ( ...@@ -127,47 +81,6 @@ export const makeContractDeployConfig = async (
constants.AddressZero, // we'll set this to the L1 Bridge address in genesis.go constants.AddressZero, // we'll set this to the L1 Bridge address in genesis.go
], ],
}, },
OVM_L1MultiMessageRelayer: {
factory: getContractFactory('OVM_L1MultiMessageRelayer'),
params: [AddressManager.address],
},
OVM_CanonicalTransactionChain: {
factory: getContractFactory('OVM_CanonicalTransactionChain'),
params: [
AddressManager.address,
config.transactionChainConfig.forceInclusionPeriodSeconds,
config.transactionChainConfig.forceInclusionPeriodBlocks,
config.ovmGasMeteringConfig.maxTransactionGasLimit,
],
afterDeploy: async (): Promise<void> => {
const sequencer = config.transactionChainConfig.sequencer
const sequencerAddress =
typeof sequencer === 'string'
? sequencer
: await sequencer.getAddress()
await _sendTx(
AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
predeploys.OVM_SequencerEntrypoint
)
)
await _sendTx(
AddressManager.setAddress('OVM_Sequencer', sequencerAddress)
)
await _sendTx(
AddressManager.setAddress('OVM_Proposer', sequencerAddress)
)
await _sendTx(AddressManager.setAddress('Sequencer', sequencerAddress))
},
},
OVM_StateCommitmentChain: {
factory: getContractFactory('OVM_StateCommitmentChain'),
params: [
AddressManager.address,
config.stateChainConfig.fraudProofWindowSeconds,
config.stateChainConfig.sequencerPublishWindowSeconds,
],
},
OVM_DeployerWhitelist: { OVM_DeployerWhitelist: {
factory: getContractFactory('OVM_DeployerWhitelist', undefined, true), factory: getContractFactory('OVM_DeployerWhitelist', undefined, true),
params: [], params: [],
...@@ -204,47 +117,16 @@ export const makeContractDeployConfig = async ( ...@@ -204,47 +117,16 @@ export const makeContractDeployConfig = async (
) )
}, },
}, },
OVM_StateManagerFactory: {
factory: getContractFactory('OVM_StateManagerFactory'),
params: [],
},
OVM_FraudVerifier: {
factory: getContractFactory('OVM_FraudVerifier'),
params: [AddressManager.address],
},
OVM_StateTransitionerFactory: {
factory: getContractFactory('OVM_StateTransitionerFactory'),
params: [AddressManager.address],
},
OVM_ECDSAContractAccount: { OVM_ECDSAContractAccount: {
factory: getContractFactory('OVM_ECDSAContractAccount', undefined, true), factory: getContractFactory('OVM_ECDSAContractAccount', undefined, true),
}, },
OVM_SequencerEntrypoint: { OVM_SequencerEntrypoint: {
factory: getContractFactory('OVM_SequencerEntrypoint', undefined, true), factory: getContractFactory('OVM_SequencerEntrypoint', undefined, true),
}, },
OVM_BondManager: {
factory: getContractFactory('mockOVM_BondManager'),
params: [AddressManager.address],
},
OVM_ETH: { OVM_ETH: {
factory: getContractFactory('OVM_ETH'), factory: getContractFactory('OVM_ETH'),
params: [], params: [],
}, },
'OVM_ChainStorageContainer-CTC-batches': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_CanonicalTransactionChain'],
},
'OVM_ChainStorageContainer-CTC-queue': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_CanonicalTransactionChain'],
},
'OVM_ChainStorageContainer-SCC-batches': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_StateCommitmentChain'],
},
ERC1820Registry: {
factory: getContractFactory('ERC1820Registry'),
},
OVM_ProxyEOA: { OVM_ProxyEOA: {
factory: getContractFactory('OVM_ProxyEOA', undefined, true), factory: getContractFactory('OVM_ProxyEOA', undefined, true),
}, },
...@@ -272,4 +154,4 @@ export const makeContractDeployConfig = async ( ...@@ -272,4 +154,4 @@ export const makeContractDeployConfig = async (
params: [`0x${'11'.repeat(20)}`], params: [`0x${'11'.repeat(20)}`],
}, },
} }
} }
\ No newline at end of file
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
}, },
"dependencies": { "dependencies": {
"@ethersproject/abstract-provider": "^5.4.1", "@ethersproject/abstract-provider": "^5.4.1",
"@ethersproject/providers": "^5.4.5",
"ethers": "^5.4.5", "ethers": "^5.4.5",
"lodash": "^4.17.21" "lodash": "^4.17.21"
} }
......
...@@ -6,3 +6,4 @@ export * from './events' ...@@ -6,3 +6,4 @@ export * from './events'
export * from './batches' export * from './batches'
export * from './bcfg' export * from './bcfg'
export * from './fees' export * from './fees'
export * from './provider'
/**
* Provider Utilities
*/
import { ethers } from 'ethers'
import { Provider } from '@ethersproject/providers'
// Copied from @ethersproject/providers since it is not
// currently exported
export interface FallbackProviderConfig {
// The Provider
provider: Provider
// The priority to favour this Provider; higher values are used first
priority?: number
// Timeout before also triggering the next provider; this does not stop
// this provider and if its result comes back before a quorum is reached
// it will be incorporated into the vote
// - lower values will cause more network traffic but may result in a
// faster retult.
stallTimeout?: number
// How much this provider contributes to the quorum; sometimes a specific
// provider may be more reliable or trustworthy than others, but usually
// this should be left as the default
weight?: number
}
export const FallbackProvider = (config: string | FallbackProviderConfig[]) => {
const configs = []
if (typeof config === 'string') {
const urls = config.split(',')
for (const [i, url] of urls.entries()) {
configs.push({
priority: i,
provider: new ethers.providers.StaticJsonRpcProvider(url),
})
}
return new ethers.providers.FallbackProvider(configs)
}
return new ethers.providers.FallbackProvider(config)
}
...@@ -2,9 +2,14 @@ ...@@ -2,9 +2,14 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { Provider, TransactionReceipt } from '@ethersproject/abstract-provider' import { Provider, TransactionReceipt } from '@ethersproject/abstract-provider'
const SENT_MESSAGE = ethers.utils.id('SentMessage(bytes)')
const RELAYED_MESSAGE = ethers.utils.id(`RelayedMessage(bytes32)`)
const FAILED_RELAYED_MESSAGE = ethers.utils.id(`FailedRelayedMessage(bytes32)`)
export interface Layer { export interface Layer {
provider: Provider provider: Provider
messengerAddress: string messengerAddress: string
blocksToFetch?: number
} }
export interface WatcherOptions { export interface WatcherOptions {
...@@ -12,6 +17,7 @@ export interface WatcherOptions { ...@@ -12,6 +17,7 @@ export interface WatcherOptions {
l2: Layer l2: Layer
pollInterval?: number pollInterval?: number
blocksToFetch?: number blocksToFetch?: number
pollForPending?: boolean
} }
export class Watcher { export class Watcher {
...@@ -19,16 +25,20 @@ export class Watcher { ...@@ -19,16 +25,20 @@ export class Watcher {
public l2: Layer public l2: Layer
public pollInterval = 3000 public pollInterval = 3000
public blocksToFetch = 1500 public blocksToFetch = 1500
public pollForPending = true
constructor(opts: WatcherOptions) { constructor(opts: WatcherOptions) {
this.l1 = opts.l1 this.l1 = opts.l1
this.l2 = opts.l2 this.l2 = opts.l2
if (opts.pollInterval) { if (typeof opts.pollInterval === 'number') {
this.pollInterval = opts.pollInterval this.pollInterval = opts.pollInterval
} }
if (opts.blocksToFetch) { if (typeof opts.blocksToFetch === 'number') {
this.blocksToFetch = opts.blocksToFetch this.blocksToFetch = opts.blocksToFetch
} }
if (typeof opts.pollForPending === 'boolean') {
this.pollForPending = opts.pollForPending
}
} }
public async getMessageHashesFromL1Tx(l1TxHash: string): Promise<string[]> { public async getMessageHashesFromL1Tx(l1TxHash: string): Promise<string[]> {
...@@ -40,14 +50,14 @@ export class Watcher { ...@@ -40,14 +50,14 @@ export class Watcher {
public async getL1TransactionReceipt( public async getL1TransactionReceipt(
l2ToL1MsgHash: string, l2ToL1MsgHash: string,
pollForPending = true pollForPending?
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
return this.getTransactionReceipt(this.l1, l2ToL1MsgHash, pollForPending) return this.getTransactionReceipt(this.l1, l2ToL1MsgHash, pollForPending)
} }
public async getL2TransactionReceipt( public async getL2TransactionReceipt(
l1ToL2MsgHash: string, l1ToL2MsgHash: string,
pollForPending = true pollForPending?
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
return this.getTransactionReceipt(this.l2, l1ToL2MsgHash, pollForPending) return this.getTransactionReceipt(this.l2, l1ToL2MsgHash, pollForPending)
} }
...@@ -65,7 +75,7 @@ export class Watcher { ...@@ -65,7 +75,7 @@ export class Watcher {
for (const log of receipt.logs) { for (const log of receipt.logs) {
if ( if (
log.address === layer.messengerAddress && log.address === layer.messengerAddress &&
log.topics[0] === ethers.utils.id('SentMessage(bytes)') log.topics[0] === SENT_MESSAGE
) { ) {
const [message] = ethers.utils.defaultAbiCoder.decode( const [message] = ethers.utils.defaultAbiCoder.decode(
['bytes'], ['bytes'],
...@@ -80,22 +90,31 @@ export class Watcher { ...@@ -80,22 +90,31 @@ export class Watcher {
public async getTransactionReceipt( public async getTransactionReceipt(
layer: Layer, layer: Layer,
msgHash: string, msgHash: string,
pollForPending = true pollForPending?
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
if (typeof pollForPending !== 'boolean') {
pollForPending = this.pollForPending
}
let matches: ethers.providers.Log[] = [] let matches: ethers.providers.Log[] = []
let blocksToFetch = layer.blocksToFetch
if (typeof blocksToFetch !== 'number') {
blocksToFetch = this.blocksToFetch
}
// scan for transaction with specified message // scan for transaction with specified message
while (matches.length === 0) { while (matches.length === 0) {
const blockNumber = await layer.provider.getBlockNumber() const blockNumber = await layer.provider.getBlockNumber()
const startingBlock = Math.max(blockNumber - this.blocksToFetch, 0) const startingBlock = Math.max(blockNumber - blocksToFetch, 0)
const successFilter: ethers.providers.Filter = { const successFilter: ethers.providers.Filter = {
address: layer.messengerAddress, address: layer.messengerAddress,
topics: [ethers.utils.id(`RelayedMessage(bytes32)`)], topics: [RELAYED_MESSAGE],
fromBlock: startingBlock, fromBlock: startingBlock,
} }
const failureFilter: ethers.providers.Filter = { const failureFilter: ethers.providers.Filter = {
address: layer.messengerAddress, address: layer.messengerAddress,
topics: [ethers.utils.id(`FailedRelayedMessage(bytes32)`)], topics: [FAILED_RELAYED_MESSAGE],
fromBlock: startingBlock, fromBlock: startingBlock,
} }
const successLogs = await layer.provider.getLogs(successFilter) const successLogs = await layer.provider.getLogs(successFilter)
......
/* Imports: External */ /* Imports: External */
import { fromHexString } from '@eth-optimism/core-utils' import { fromHexString, FallbackProvider } from '@eth-optimism/core-utils'
import { BaseService, Metrics } from '@eth-optimism/common-ts' import { BaseService, Metrics } from '@eth-optimism/common-ts'
import { StaticJsonRpcProvider } from '@ethersproject/providers' import { StaticJsonRpcProvider, BaseProvider } from '@ethersproject/providers'
import { LevelUp } from 'levelup' import { LevelUp } from 'levelup'
import { ethers, constants } from 'ethers' import { ethers, constants } from 'ethers'
import { Gauge, Counter } from 'prom-client' import { Gauge, Counter } from 'prom-client'
...@@ -80,7 +80,7 @@ const optionSettings = { ...@@ -80,7 +80,7 @@ const optionSettings = {
}, },
l1RpcProvider: { l1RpcProvider: {
validate: (val: any) => { validate: (val: any) => {
return validators.isUrl(val) || validators.isJsonRpcProvider(val) return validators.isString(val) || validators.isJsonRpcProvider(val)
}, },
}, },
l2ChainId: { l2ChainId: {
...@@ -98,7 +98,7 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> { ...@@ -98,7 +98,7 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
private state: { private state: {
db: TransportDB db: TransportDB
contracts: OptimismContracts contracts: OptimismContracts
l1RpcProvider: StaticJsonRpcProvider l1RpcProvider: BaseProvider
startingL1BlockNumber: number startingL1BlockNumber: number
} = {} as any } = {} as any
...@@ -107,10 +107,11 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> { ...@@ -107,10 +107,11 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
this.l1IngestionMetrics = registerMetrics(this.metrics) this.l1IngestionMetrics = registerMetrics(this.metrics)
this.state.l1RpcProvider = if (typeof this.options.l1RpcProvider === 'string') {
typeof this.options.l1RpcProvider === 'string' this.state.l1RpcProvider = FallbackProvider(this.options.l1RpcProvider)
? new StaticJsonRpcProvider(this.options.l1RpcProvider) } else {
: this.options.l1RpcProvider this.state.l1RpcProvider = this.options.l1RpcProvider
}
this.logger.info('Using AddressManager', { this.logger.info('Using AddressManager', {
addressManager: this.options.addressManager, addressManager: this.options.addressManager,
......
import { JsonRpcProvider } from '@ethersproject/providers' import { BaseProvider } from '@ethersproject/providers'
import { BigNumber, Event } from 'ethers' import { BigNumber, Event } from 'ethers'
import { TransportDB } from '../db/transport-db' import { TransportDB } from '../db/transport-db'
...@@ -15,7 +15,7 @@ export type TypedEthersEvent<T> = Event & { ...@@ -15,7 +15,7 @@ export type TypedEthersEvent<T> = Event & {
export type GetExtraDataHandler<TEventArgs, TExtraData> = ( export type GetExtraDataHandler<TEventArgs, TExtraData> = (
event?: TypedEthersEvent<TEventArgs>, event?: TypedEthersEvent<TEventArgs>,
l1RpcProvider?: JsonRpcProvider l1RpcProvider?: BaseProvider
) => Promise<TExtraData> ) => Promise<TExtraData>
export type ParseEventHandler<TEventArgs, TExtraData, TParsedEvent> = ( export type ParseEventHandler<TEventArgs, TExtraData, TParsedEvent> = (
......
/* Imports: External */ /* Imports: External */
import { constants, Contract, Signer } from 'ethers' import { constants, Contract, Signer } from 'ethers'
import { JsonRpcProvider } from '@ethersproject/providers' import { BaseProvider } from '@ethersproject/providers'
import { getContractInterface } from '@eth-optimism/contracts' import { getContractInterface } from '@eth-optimism/contracts'
export const loadContract = ( export const loadContract = (
name: string, name: string,
address: string, address: string,
provider: JsonRpcProvider provider: BaseProvider
): Contract => { ): Contract => {
return new Contract(address, getContractInterface(name) as any, provider) return new Contract(address, getContractInterface(name) as any, provider)
} }
...@@ -15,7 +15,7 @@ export const loadProxyFromManager = async ( ...@@ -15,7 +15,7 @@ export const loadProxyFromManager = async (
name: string, name: string,
proxy: string, proxy: string,
Lib_AddressManager: Contract, Lib_AddressManager: Contract,
provider: JsonRpcProvider provider: BaseProvider
): Promise<Contract> => { ): Promise<Contract> => {
const address = await Lib_AddressManager.getAddress(proxy) const address = await Lib_AddressManager.getAddress(proxy)
...@@ -36,7 +36,7 @@ export interface OptimismContracts { ...@@ -36,7 +36,7 @@ export interface OptimismContracts {
} }
export const loadOptimismContracts = async ( export const loadOptimismContracts = async (
l1RpcProvider: JsonRpcProvider, l1RpcProvider: BaseProvider,
addressManagerAddress: string, addressManagerAddress: string,
signer?: Signer signer?: Signer
): Promise<OptimismContracts> => { ): Promise<OptimismContracts> => {
......
...@@ -258,7 +258,10 @@ export const getStateRootBatchByTransactionIndex = async ( ...@@ -258,7 +258,10 @@ export const getStateRootBatchByTransactionIndex = async (
* @param index Index to generate a proof for. * @param index Index to generate a proof for.
* @returns Merkle proof sibling leaves, as hex strings. * @returns Merkle proof sibling leaves, as hex strings.
*/ */
const getMerkleTreeProof = (leaves: string[], index: number): string[] => { export const getMerkleTreeProof = (
leaves: string[],
index: number
): string[] => {
// Our specific Merkle tree implementation requires that the number of leaves is a power of 2. // Our specific Merkle tree implementation requires that the number of leaves is a power of 2.
// If the number of given leaves is less than a power of 2, we need to round up to the next // If the number of given leaves is less than a power of 2, we need to round up to the next
// available power of 2. We fill the remaining space with the hash of bytes32(0). // available power of 2. We fill the remaining space with the hash of bytes32(0).
......
...@@ -9,6 +9,7 @@ import { toPlainObject } from 'lodash' ...@@ -9,6 +9,7 @@ import { toPlainObject } from 'lodash'
/* Imports: Internal */ /* Imports: Internal */
import { import {
getMerkleTreeProof,
getMessagesAndProofsForL2Transaction, getMessagesAndProofsForL2Transaction,
getStateRootBatchByTransactionIndex, getStateRootBatchByTransactionIndex,
getStateBatchAppendedEventByTransactionIndex, getStateBatchAppendedEventByTransactionIndex,
...@@ -375,3 +376,37 @@ describe('relay transaction generation functions', () => { ...@@ -375,3 +376,37 @@ describe('relay transaction generation functions', () => {
}) })
}) })
}) })
describe('getMerkleTreeProof', () => {
let leaves: string[] = [
'the',
'quick',
'brown',
'fox',
'jumps',
'over',
'the',
'lazy',
'dog',
]
const index: number = 4
it('should generate a merkle tree proof from an odd number of leaves at the correct index', () => {
const expectedProof = [
'0x6f766572',
'0x123268ec1a3f9aac2bc68e899fe4329eefef783c76265722508b8abbfbf11440',
'0x12aaa1b2e09f26e14d86aa3b157b94cfeabe815e44b6742d00c47441a576b12d',
'0x297d90df3f77f93eefdeab4e9f6e9a074b41a3508f9d265e92e9b5449c7b11c8',
]
expect(getMerkleTreeProof(leaves, index)).to.deep.equal(expectedProof)
})
it('should generate a merkle tree proof from an even number of leaves at the correct index', () => {
const expectedProof = [
'0x6f766572',
'0x09e430fa7b513203dd9c74afd734267a73f64299d9dac61ef09e96c3b3b3fe96',
'0x12aaa1b2e09f26e14d86aa3b157b94cfeabe815e44b6742d00c47441a576b12d',
]
leaves = leaves.slice(0, leaves.length - 2)
expect(getMerkleTreeProof(leaves, index)).to.deep.equal(expectedProof)
})
})
# Optimistic Specs # The Optimistic Ethereum Spec
Welcome to Optimism's specs. ## Notice
Please refer to the README in each of the subdirectories for a table of contents. We're in the process of moving our specifications to the [optimistic-specs](https://github.com/ethereum-optimism/optimistic-specs) repository.
**The specifications within this folder may be entirely outdated**.
### Usage Please refer to the `optimistic-specs` repository for the most up-to-date system specifications.
We will remove this folder once the migration process is complete.
We provide some basic yarn scripts to ensure consistent formatting.
After modifying any of the markdown files here, run `yarn format`.
```bash
yarn format
```
...@@ -922,6 +922,31 @@ ...@@ -922,6 +922,31 @@
bech32 "1.1.4" bech32 "1.1.4"
ws "7.4.6" ws "7.4.6"
"@ethersproject/providers@^5.4.5":
version "5.4.5"
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928"
integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw==
dependencies:
"@ethersproject/abstract-provider" "^5.4.0"
"@ethersproject/abstract-signer" "^5.4.0"
"@ethersproject/address" "^5.4.0"
"@ethersproject/basex" "^5.4.0"
"@ethersproject/bignumber" "^5.4.0"
"@ethersproject/bytes" "^5.4.0"
"@ethersproject/constants" "^5.4.0"
"@ethersproject/hash" "^5.4.0"
"@ethersproject/logger" "^5.4.0"
"@ethersproject/networks" "^5.4.0"
"@ethersproject/properties" "^5.4.0"
"@ethersproject/random" "^5.4.0"
"@ethersproject/rlp" "^5.4.0"
"@ethersproject/sha2" "^5.4.0"
"@ethersproject/strings" "^5.4.0"
"@ethersproject/transactions" "^5.4.0"
"@ethersproject/web" "^5.4.0"
bech32 "1.1.4"
ws "7.4.6"
"@ethersproject/random@5.4.0", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.4.0": "@ethersproject/random@5.4.0", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.4.0":
version "5.4.0" version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16"
...@@ -10494,9 +10519,9 @@ mixin-deep@^1.2.0: ...@@ -10494,9 +10519,9 @@ mixin-deep@^1.2.0:
is-extendable "^1.0.1" is-extendable "^1.0.1"
mixme@^0.5.0: mixme@^0.5.0:
version "0.5.1" version "0.5.2"
resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.1.tgz#b3da79a563b2da46efba9519830059e4c2a9e40f" resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.2.tgz#33c7e21d8e9b73abc2711c5197ae6c93f65fe0e4"
integrity sha512-NaeZIckeBFT7i0XBEpGyFcAE0/bLcQ9MHErTpnU3bLWVE5WZbxG5Y3fDsMxYGifTo5khDA42OquXCC2ngKJB+g== integrity sha512-fzzuzXSqp14Mk2eZK15yqcJHwNlLtg+EliQBO/ihYfZed9WUuDHR9ZuEUqQDD8FcW/742y0JGq8xBfL9fNsWZw==
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
version "0.5.3" version "0.5.3"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment