Commit e0be02e1 authored by Kelvin Fichter's avatar Kelvin Fichter Committed by Mark Tyneway

provider: use fallback provider helper

Allow the DTL to sync from multiple web3 backends using
the `ethers.providers.FallbackProvider`. Multiple URLs
can be passed through as a comma delimited string to
configure multiple providers.

A helper function is added to `core-utils` to allow
easy configuration between services. This PR only introduces
the usage of the fallback provider into the DTL. In the
future, it should be added to other services that query L1
as well.
parent 34927928
---
'@eth-optimism/core-utils': patch
'@eth-optimism/data-transport-layer': patch
---
Add fallback provider support to DTL using helper function in core-utils
...@@ -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)
}
/* 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> => {
......
...@@ -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"
......
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