Commit 27f32ca0 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

dtl: add query param based querying for txs (#479)

* dtl: remove stopL2SyncAtHeight config

* dtl: implement query param based backend

* dtl: remove dead comment

* dtl: add changeset

* dtl: standardize on backend query param

* changeset: update comment
parent c75a0fc8
---
"@eth-optimism/data-transport-layer": patch
---
Allow the DTL to provide data from either L1 or L2, configurable via a query param sent by the client.
The config option `default-backend` can be used to specify the backend to be
used if the query param is not specified. This allows it to be backwards
compatible with how the DTL was previously used.
...@@ -43,10 +43,6 @@ const optionSettings = { ...@@ -43,10 +43,6 @@ const optionSettings = {
default: false, default: false,
validate: validators.isBoolean, validate: validators.isBoolean,
}, },
stopL2SyncAtBlock: {
default: Infinity,
validate: validators.isInteger,
},
} }
export class L2IngestionService extends BaseService<L2IngestionServiceOptions> { export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
...@@ -80,30 +76,7 @@ export class L2IngestionService extends BaseService<L2IngestionServiceOptions> { ...@@ -80,30 +76,7 @@ export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
const highestSyncedL2BlockNumber = const highestSyncedL2BlockNumber =
(await this.state.db.getHighestSyncedUnconfirmedBlock()) || 1 (await this.state.db.getHighestSyncedUnconfirmedBlock()) || 1
// Shut down if we're at the stop block. const currentL2Block = await this.state.l2RpcProvider.getBlockNumber()
if (
this.options.stopL2SyncAtBlock !== undefined &&
this.options.stopL2SyncAtBlock !== null &&
highestSyncedL2BlockNumber >= this.options.stopL2SyncAtBlock
) {
this.logger.info(
"L2 sync is shutting down because we've reached your target block. Goodbye!"
)
return
}
let currentL2Block = await this.state.l2RpcProvider.getBlockNumber()
// Make sure we can't exceed the stop block.
if (
this.options.stopL2SyncAtBlock !== undefined &&
this.options.stopL2SyncAtBlock !== null
) {
currentL2Block = Math.min(
currentL2Block,
this.options.stopL2SyncAtBlock
)
}
// Make sure we don't exceed the tip. // Make sure we don't exceed the tip.
const targetL2Block = Math.min( const targetL2Block = Math.min(
......
...@@ -21,13 +21,12 @@ export interface L1DataTransportServiceOptions { ...@@ -21,13 +21,12 @@ export interface L1DataTransportServiceOptions {
logsPerPollingInterval: number logsPerPollingInterval: number
pollingInterval: number pollingInterval: number
port: number port: number
showUnconfirmedTransactions: boolean
syncFromL1?: boolean syncFromL1?: boolean
syncFromL2?: boolean syncFromL2?: boolean
transactionsPerPollingInterval: number transactionsPerPollingInterval: number
legacySequencerCompatibility: boolean legacySequencerCompatibility: boolean
stopL2SyncAtBlock?: number
sentryDsn?: string sentryDsn?: string
defaultBackend: string
} }
const optionSettings = { const optionSettings = {
......
...@@ -39,7 +39,6 @@ interface Bcfg { ...@@ -39,7 +39,6 @@ interface Bcfg {
l2ChainId: config.uint('l2ChainId'), l2ChainId: config.uint('l2ChainId'),
syncFromL1: config.bool('syncFromL1', true), syncFromL1: config.bool('syncFromL1', true),
syncFromL2: config.bool('syncFromL2', false), syncFromL2: config.bool('syncFromL2', false),
showUnconfirmedTransactions: config.bool('syncFromL2', false),
transactionsPerPollingInterval: config.uint( transactionsPerPollingInterval: config.uint(
'transactionsPerPollingInterval', 'transactionsPerPollingInterval',
1000 1000
...@@ -48,8 +47,8 @@ interface Bcfg { ...@@ -48,8 +47,8 @@ interface Bcfg {
'legacySequencerCompatibility', 'legacySequencerCompatibility',
false false
), ),
stopL2SyncAtBlock: config.uint('stopL2SyncAtBlock'),
sentryDsn: config.str('sentryDsn'), sentryDsn: config.str('sentryDsn'),
defaultBackend: config.str('defaultBackend', 'l1'),
}) })
await service.start() await service.start()
......
...@@ -48,9 +48,12 @@ const optionSettings = { ...@@ -48,9 +48,12 @@ const optionSettings = {
return validators.isUrl(val) || validators.isJsonRpcProvider(val) return validators.isUrl(val) || validators.isJsonRpcProvider(val)
}, },
}, },
showUnconfirmedTransactions: { defaultBackend: {
validate: validators.isBoolean, default: 'l1',
}, validate: (val: string) => {
return val === 'l1' || val === 'l2'
}
}
} }
export class L1TransportServer extends BaseService<L1TransportServerOptions> { export class L1TransportServer extends BaseService<L1TransportServerOptions> {
...@@ -66,7 +69,6 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -66,7 +69,6 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
} = {} as any } = {} as any
protected async _init(): Promise<void> { protected async _init(): Promise<void> {
// TODO: I don't know if this is strictly necessary, but it's probably a good thing to do.
if (!this.options.db.isOpen()) { if (!this.options.db.isOpen()) {
await this.options.db.open() await this.options.db.open()
} }
...@@ -171,14 +173,26 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -171,14 +173,26 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
* TODO: Link to our API spec. * TODO: Link to our API spec.
*/ */
private _registerAllRoutes(): void { private _registerAllRoutes(): void {
// TODO: Maybe add doc-like comments to each of these routes?
this._registerRoute( this._registerRoute(
'get', 'get',
'/eth/syncing', '/eth/syncing',
async (): Promise<SyncingResponse> => { async (req): Promise<SyncingResponse> => {
const highestL2BlockNumber = await this.state.db.getHighestL2BlockNumber() const backend = req.query.backend || this.options.defaultBackend
const currentL2Block = await this.state.db.getLatestTransaction()
let currentL2Block
let highestL2BlockNumber
switch (backend) {
case 'l1':
currentL2Block = await this.state.db.getLatestTransaction()
highestL2BlockNumber = await this.state.db.getHighestL2BlockNumber()
break
case 'l2':
currentL2Block = await this.state.db.getLatestUnconfirmedTransaction()
highestL2BlockNumber = await this.state.db.getHighestSyncedUnconfirmedBlock()
break
default:
throw new Error(`Unknown transaction backend ${backend}`)
}
if (currentL2Block === null) { if (currentL2Block === null) {
if (highestL2BlockNumber === null) { if (highestL2BlockNumber === null) {
...@@ -329,21 +343,19 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -329,21 +343,19 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
this._registerRoute( this._registerRoute(
'get', 'get',
'/transaction/latest', '/transaction/latest',
async (): Promise<TransactionResponse> => { async (req): Promise<TransactionResponse> => {
let transaction = await this.state.db.getLatestFullTransaction() const backend = req.query.backend || this.options.defaultBackend
if (this.options.showUnconfirmedTransactions) { let transaction = null
const latestUnconfirmedTx = await this.state.db.getLatestUnconfirmedTransaction()
if (
transaction === null ||
transaction === undefined ||
latestUnconfirmedTx.index >= transaction.index
) {
transaction = latestUnconfirmedTx
}
}
if (transaction === null) { switch (backend) {
transaction = await this.state.db.getLatestFullTransaction() case 'l1':
transaction = await this.state.db.getLatestFullTransaction()
break
case 'l2':
transaction = await this.state.db.getLatestUnconfirmedTransaction()
break
default:
throw new Error(`Unknown transaction backend ${backend}`)
} }
if (transaction === null) { if (transaction === null) {
...@@ -368,17 +380,22 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -368,17 +380,22 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
'get', 'get',
'/transaction/index/:index', '/transaction/index/:index',
async (req): Promise<TransactionResponse> => { async (req): Promise<TransactionResponse> => {
const backend = req.query.backend || this.options.defaultBackend
let transaction = null let transaction = null
if (this.options.showUnconfirmedTransactions) {
transaction = await this.state.db.getUnconfirmedTransactionByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (transaction === null) { switch (backend ) {
transaction = await this.state.db.getFullTransactionByIndex( case 'l1':
BigNumber.from(req.params.index).toNumber() transaction = await this.state.db.getFullTransactionByIndex(
) BigNumber.from(req.params.index).toNumber()
)
break
case 'l2':
transaction = await this.state.db.getUnconfirmedTransactionByIndex(
BigNumber.from(req.params.index).toNumber()
)
break
default:
throw new Error(`Unknown transaction backend ${backend}`)
} }
if (transaction === null) { if (transaction === null) {
...@@ -456,21 +473,19 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -456,21 +473,19 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
this._registerRoute( this._registerRoute(
'get', 'get',
'/stateroot/latest', '/stateroot/latest',
async (): Promise<StateRootResponse> => { async (req): Promise<StateRootResponse> => {
let stateRoot = await this.state.db.getLatestStateRoot() const backend = req.query.backend || this.options.defaultBackend
if (this.options.showUnconfirmedTransactions) { let stateRoot = null
const latestUnconfirmedStateRoot = await this.state.db.getLatestUnconfirmedStateRoot()
if (
stateRoot === null ||
stateRoot === undefined ||
latestUnconfirmedStateRoot.index >= stateRoot.index
) {
stateRoot = latestUnconfirmedStateRoot
}
}
if (stateRoot === null) { switch (backend) {
stateRoot = await this.state.db.getLatestStateRoot() case 'l1':
stateRoot = await this.state.db.getLatestStateRoot()
break
case 'l2':
stateRoot = await this.state.db.getLatestUnconfirmedStateRoot()
break
default:
throw new Error(`Unknown transaction backend ${backend}`)
} }
if (stateRoot === null) { if (stateRoot === null) {
...@@ -495,17 +510,22 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> { ...@@ -495,17 +510,22 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
'get', 'get',
'/stateroot/index/:index', '/stateroot/index/:index',
async (req): Promise<StateRootResponse> => { async (req): Promise<StateRootResponse> => {
const backend = req.query.backend || this.options.defaultBackend
let stateRoot = null let stateRoot = null
if (this.options.showUnconfirmedTransactions) {
stateRoot = await this.state.db.getUnconfirmedStateRootByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (stateRoot === null) { switch (backend) {
stateRoot = await this.state.db.getStateRootByIndex( case 'l1':
BigNumber.from(req.params.index).toNumber() stateRoot = await this.state.db.getStateRootByIndex(
) BigNumber.from(req.params.index).toNumber()
)
break
case 'l2':
stateRoot = await this.state.db.getUnconfirmedStateRootByIndex(
BigNumber.from(req.params.index).toNumber()
)
break
default:
throw new Error(`Unknown transaction backend ${backend}`)
} }
if (stateRoot === null) { if (stateRoot === null) {
......
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