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 {
}
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{
evm.Context.Time,
msg.L1BlockNumber(),
uint8(msg.QueueOrigin()),
*msg.L1MessageSender(),
*msg.To(),
*l1MsgSender,
*to,
big.NewInt(int64(msg.Gas())),
msg.Data(),
}
......
......@@ -34,7 +34,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
protected chainContract: CanonicalTransactionChainContract
protected l2ChainId: number
protected syncing: boolean
private disableQueueBatchAppend: boolean
private autoFixBatchOptions: AutoFixBatchOptions
private transactionSubmitter: TransactionSubmitter
private gasThresholdInGwei: number
......@@ -55,7 +54,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
blockOffset: number,
logger: Logger,
metrics: Metrics,
disableQueueBatchAppend: boolean,
autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: false,
fixMonotonicity: false,
......@@ -78,7 +76,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
logger,
metrics
)
this.disableQueueBatchAppend = disableQueueBatchAppend
this.autoFixBatchOptions = autoFixBatchOptions
this.gasThresholdInGwei = gasThresholdInGwei
this.transactionSubmitter = transactionSubmitter
......@@ -137,11 +134,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
'Syncing mode enabled! Skipping batch submission and clearing queue elements',
{ pendingQueueElements }
)
if (!this.disableQueueBatchAppend) {
return this.submitAppendQueueBatch()
}
}
this.logger.info('Syncing mode enabled but queue is empty. Skipping...')
return
}
......@@ -229,20 +223,6 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
* 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(
batchParams: AppendSequencerBatchParams
): Promise<TransactionReceipt> {
......
......@@ -64,7 +64,6 @@ interface RequiredEnvVars {
/* Optional Env Vars
* FRAUD_SUBMISSION_ADDRESS
* DISABLE_QUEUE_BATCH_APPEND
* SEQUENCER_PRIVATE_KEY
* PROPOSER_PRIVATE_KEY
* MNEMONIC
......@@ -201,10 +200,6 @@ export const run = async () => {
'fraud-submisison-address',
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(
'min-gas-price-in-gwei',
parseInt(env.MIN_GAS_PRICE_IN_GWEI, 10) || 0
......@@ -389,7 +384,6 @@ export const run = async () => {
BLOCK_OFFSET,
logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
metrics,
DISABLE_QUEUE_BATCH_APPEND,
autoFixBatchOptions
)
......
......@@ -230,7 +230,6 @@ describe('BatchSubmitter', () => {
1,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
testMetrics,
false
)
}
......
......@@ -74,52 +74,6 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_L2CrossDomainMessenger'),
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: {
factory: getContractFactory('OVM_L2StandardBridge'),
params: [
......@@ -127,47 +81,6 @@ export const makeContractDeployConfig = async (
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: {
factory: getContractFactory('OVM_DeployerWhitelist', undefined, true),
params: [],
......@@ -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: {
factory: getContractFactory('OVM_ECDSAContractAccount', undefined, true),
},
OVM_SequencerEntrypoint: {
factory: getContractFactory('OVM_SequencerEntrypoint', undefined, true),
},
OVM_BondManager: {
factory: getContractFactory('mockOVM_BondManager'),
params: [AddressManager.address],
},
OVM_ETH: {
factory: getContractFactory('OVM_ETH'),
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: {
factory: getContractFactory('OVM_ProxyEOA', undefined, true),
},
......
......@@ -47,6 +47,7 @@
},
"dependencies": {
"@ethersproject/abstract-provider": "^5.4.1",
"@ethersproject/providers": "^5.4.5",
"ethers": "^5.4.5",
"lodash": "^4.17.21"
}
......
......@@ -6,3 +6,4 @@ export * from './events'
export * from './batches'
export * from './bcfg'
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 @@
import { ethers } from 'ethers'
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 {
provider: Provider
messengerAddress: string
blocksToFetch?: number
}
export interface WatcherOptions {
......@@ -12,6 +17,7 @@ export interface WatcherOptions {
l2: Layer
pollInterval?: number
blocksToFetch?: number
pollForPending?: boolean
}
export class Watcher {
......@@ -19,16 +25,20 @@ export class Watcher {
public l2: Layer
public pollInterval = 3000
public blocksToFetch = 1500
public pollForPending = true
constructor(opts: WatcherOptions) {
this.l1 = opts.l1
this.l2 = opts.l2
if (opts.pollInterval) {
if (typeof opts.pollInterval === 'number') {
this.pollInterval = opts.pollInterval
}
if (opts.blocksToFetch) {
if (typeof opts.blocksToFetch === 'number') {
this.blocksToFetch = opts.blocksToFetch
}
if (typeof opts.pollForPending === 'boolean') {
this.pollForPending = opts.pollForPending
}
}
public async getMessageHashesFromL1Tx(l1TxHash: string): Promise<string[]> {
......@@ -40,14 +50,14 @@ export class Watcher {
public async getL1TransactionReceipt(
l2ToL1MsgHash: string,
pollForPending = true
pollForPending?
): Promise<TransactionReceipt> {
return this.getTransactionReceipt(this.l1, l2ToL1MsgHash, pollForPending)
}
public async getL2TransactionReceipt(
l1ToL2MsgHash: string,
pollForPending = true
pollForPending?
): Promise<TransactionReceipt> {
return this.getTransactionReceipt(this.l2, l1ToL2MsgHash, pollForPending)
}
......@@ -65,7 +75,7 @@ export class Watcher {
for (const log of receipt.logs) {
if (
log.address === layer.messengerAddress &&
log.topics[0] === ethers.utils.id('SentMessage(bytes)')
log.topics[0] === SENT_MESSAGE
) {
const [message] = ethers.utils.defaultAbiCoder.decode(
['bytes'],
......@@ -80,22 +90,31 @@ export class Watcher {
public async getTransactionReceipt(
layer: Layer,
msgHash: string,
pollForPending = true
pollForPending?
): Promise<TransactionReceipt> {
if (typeof pollForPending !== 'boolean') {
pollForPending = this.pollForPending
}
let matches: ethers.providers.Log[] = []
let blocksToFetch = layer.blocksToFetch
if (typeof blocksToFetch !== 'number') {
blocksToFetch = this.blocksToFetch
}
// scan for transaction with specified message
while (matches.length === 0) {
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 = {
address: layer.messengerAddress,
topics: [ethers.utils.id(`RelayedMessage(bytes32)`)],
topics: [RELAYED_MESSAGE],
fromBlock: startingBlock,
}
const failureFilter: ethers.providers.Filter = {
address: layer.messengerAddress,
topics: [ethers.utils.id(`FailedRelayedMessage(bytes32)`)],
topics: [FAILED_RELAYED_MESSAGE],
fromBlock: startingBlock,
}
const successLogs = await layer.provider.getLogs(successFilter)
......
/* 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 { StaticJsonRpcProvider } from '@ethersproject/providers'
import { StaticJsonRpcProvider, BaseProvider } from '@ethersproject/providers'
import { LevelUp } from 'levelup'
import { ethers, constants } from 'ethers'
import { Gauge, Counter } from 'prom-client'
......@@ -80,7 +80,7 @@ const optionSettings = {
},
l1RpcProvider: {
validate: (val: any) => {
return validators.isUrl(val) || validators.isJsonRpcProvider(val)
return validators.isString(val) || validators.isJsonRpcProvider(val)
},
},
l2ChainId: {
......@@ -98,7 +98,7 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
private state: {
db: TransportDB
contracts: OptimismContracts
l1RpcProvider: StaticJsonRpcProvider
l1RpcProvider: BaseProvider
startingL1BlockNumber: number
} = {} as any
......@@ -107,10 +107,11 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
this.l1IngestionMetrics = registerMetrics(this.metrics)
this.state.l1RpcProvider =
typeof this.options.l1RpcProvider === 'string'
? new StaticJsonRpcProvider(this.options.l1RpcProvider)
: this.options.l1RpcProvider
if (typeof this.options.l1RpcProvider === 'string') {
this.state.l1RpcProvider = FallbackProvider(this.options.l1RpcProvider)
} else {
this.state.l1RpcProvider = this.options.l1RpcProvider
}
this.logger.info('Using AddressManager', {
addressManager: this.options.addressManager,
......
import { JsonRpcProvider } from '@ethersproject/providers'
import { BaseProvider } from '@ethersproject/providers'
import { BigNumber, Event } from 'ethers'
import { TransportDB } from '../db/transport-db'
......@@ -15,7 +15,7 @@ export type TypedEthersEvent<T> = Event & {
export type GetExtraDataHandler<TEventArgs, TExtraData> = (
event?: TypedEthersEvent<TEventArgs>,
l1RpcProvider?: JsonRpcProvider
l1RpcProvider?: BaseProvider
) => Promise<TExtraData>
export type ParseEventHandler<TEventArgs, TExtraData, TParsedEvent> = (
......
/* Imports: External */
import { constants, Contract, Signer } from 'ethers'
import { JsonRpcProvider } from '@ethersproject/providers'
import { BaseProvider } from '@ethersproject/providers'
import { getContractInterface } from '@eth-optimism/contracts'
export const loadContract = (
name: string,
address: string,
provider: JsonRpcProvider
provider: BaseProvider
): Contract => {
return new Contract(address, getContractInterface(name) as any, provider)
}
......@@ -15,7 +15,7 @@ export const loadProxyFromManager = async (
name: string,
proxy: string,
Lib_AddressManager: Contract,
provider: JsonRpcProvider
provider: BaseProvider
): Promise<Contract> => {
const address = await Lib_AddressManager.getAddress(proxy)
......@@ -36,7 +36,7 @@ export interface OptimismContracts {
}
export const loadOptimismContracts = async (
l1RpcProvider: JsonRpcProvider,
l1RpcProvider: BaseProvider,
addressManagerAddress: string,
signer?: Signer
): Promise<OptimismContracts> => {
......
......@@ -258,7 +258,10 @@ export const getStateRootBatchByTransactionIndex = async (
* @param index Index to generate a proof for.
* @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.
// 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).
......
......@@ -9,6 +9,7 @@ import { toPlainObject } from 'lodash'
/* Imports: Internal */
import {
getMerkleTreeProof,
getMessagesAndProofsForL2Transaction,
getStateRootBatchByTransactionIndex,
getStateBatchAppendedEventByTransactionIndex,
......@@ -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.
### Usage
We provide some basic yarn scripts to ensure consistent formatting.
After modifying any of the markdown files here, run `yarn format`.
```bash
yarn format
```
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**.
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.
......@@ -922,6 +922,31 @@
bech32 "1.1.4"
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":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16"
......@@ -10494,9 +10519,9 @@ mixin-deep@^1.2.0:
is-extendable "^1.0.1"
mixme@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.1.tgz#b3da79a563b2da46efba9519830059e4c2a9e40f"
integrity sha512-NaeZIckeBFT7i0XBEpGyFcAE0/bLcQ9MHErTpnU3bLWVE5WZbxG5Y3fDsMxYGifTo5khDA42OquXCC2ngKJB+g==
version "0.5.2"
resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.2.tgz#33c7e21d8e9b73abc2711c5197ae6c93f65fe0e4"
integrity sha512-fzzuzXSqp14Mk2eZK15yqcJHwNlLtg+EliQBO/ihYfZed9WUuDHR9ZuEUqQDD8FcW/742y0JGq8xBfL9fNsWZw==
mkdirp-classic@^0.5.2, mkdirp-classic@^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