Commit 2c0ad576 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #4764 from ethereum-optimism/sc/fd-bedrock-support

feat(fd): support Bedrock networks
parents 24ca4622 b004d1ad
---
'@eth-optimism/fault-detector': minor
---
Updates the fault detector to support Bedrock networks.
ignores: [ ignores: [
"@babel/eslint-parser", "@babel/eslint-parser",
"@types/level",
"@typescript-eslint/parser", "@typescript-eslint/parser",
"eslint-plugin-import", "eslint-plugin-import",
"eslint-plugin-unicorn", "eslint-plugin-unicorn",
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
"ethers": "^5.7.0", "ethers": "^5.7.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-prom-bundle": "^6.3.6", "express-prom-bundle": "^6.3.6",
"level": "^6.0.1", "level6": "npm:level@^6.0.1",
"levelup": "^4.4.0" "levelup": "^4.4.0"
}, },
"devDependencies": { "devDependencies": {
......
/* Imports: External */ /* Imports: External */
import { BaseService, LegacyMetrics } from '@eth-optimism/common-ts' import { BaseService, LegacyMetrics } from '@eth-optimism/common-ts'
import { LevelUp } from 'levelup' import { LevelUp } from 'levelup'
import level from 'level' import level from 'level6'
import { Counter } from 'prom-client' import { Counter } from 'prom-client'
/* Imports: Internal */ /* Imports: Internal */
......
import { Contract } from 'ethers' import { Contract, BigNumber } from 'ethers'
export interface OutputOracle<TSubmissionEventArgs> {
contract: Contract
filter: any
getTotalElements: () => Promise<BigNumber>
getEventIndex: (args: TSubmissionEventArgs) => BigNumber
}
/** /**
* Partial event interface, meant to reduce the size of the event cache to avoid * Partial event interface, meant to reduce the size of the event cache to avoid
...@@ -41,27 +48,32 @@ const getCache = ( ...@@ -41,27 +48,32 @@ const getCache = (
} }
/** /**
* Updates the event cache for the SCC. * Updates the event cache for a contract and event.
* *
* @param scc The State Commitment Chain contract. * @param contract Contract to update cache for.
* @param filter Event filter to use.
*/ */
export const updateStateBatchEventCache = async ( export const updateOracleCache = async <TSubmissionEventArgs>(
scc: Contract oracle: OutputOracle<TSubmissionEventArgs>
): Promise<void> => { ): Promise<void> => {
const cache = getCache(scc.address) const cache = getCache(oracle.contract.address)
let currentBlock = cache.highestBlock let currentBlock = cache.highestBlock
const endingBlock = await scc.provider.getBlockNumber() const endingBlock = await oracle.contract.provider.getBlockNumber()
let step = endingBlock - currentBlock let step = endingBlock - currentBlock
let failures = 0 let failures = 0
while (currentBlock < endingBlock) { while (currentBlock < endingBlock) {
try { try {
const events = await scc.queryFilter( const events = await oracle.contract.queryFilter(
scc.filters.StateBatchAppended(), oracle.filter,
currentBlock, currentBlock,
currentBlock + step currentBlock + step
) )
// Throw the events into the cache.
for (const event of events) { for (const event of events) {
cache.eventCache[event.args._batchIndex.toNumber()] = { cache.eventCache[
oracle.getEventIndex(event.args as TSubmissionEventArgs).toNumber()
] = {
blockNumber: event.blockNumber, blockNumber: event.blockNumber,
transactionHash: event.transactionHash, transactionHash: event.transactionHash,
args: event.args, args: event.args,
...@@ -97,15 +109,15 @@ export const updateStateBatchEventCache = async ( ...@@ -97,15 +109,15 @@ export const updateStateBatchEventCache = async (
/** /**
* Finds the Event that corresponds to a given state batch by index. * Finds the Event that corresponds to a given state batch by index.
* *
* @param scc StateCommitmentChain contract. * @param oracle Output oracle contract
* @param index State batch index to search for. * @param index State batch index to search for.
* @returns Event corresponding to the batch. * @returns Event corresponding to the batch.
*/ */
export const findEventForStateBatch = async ( export const findEventForStateBatch = async <TSubmissionEventArgs>(
scc: Contract, oracle: OutputOracle<TSubmissionEventArgs>,
index: number index: number
): Promise<PartialEvent> => { ): Promise<PartialEvent> => {
const cache = getCache(scc.address) const cache = getCache(oracle.contract.address)
// Try to find the event in cache first. // Try to find the event in cache first.
if (cache.eventCache[index]) { if (cache.eventCache[index]) {
...@@ -113,7 +125,7 @@ export const findEventForStateBatch = async ( ...@@ -113,7 +125,7 @@ export const findEventForStateBatch = async (
} }
// Update the event cache if we don't have the event. // Update the event cache if we don't have the event.
await updateStateBatchEventCache(scc) await updateOracleCache(oracle)
// Event better be in cache now! // Event better be in cache now!
if (cache.eventCache[index] === undefined) { if (cache.eventCache[index] === undefined) {
...@@ -126,23 +138,23 @@ export const findEventForStateBatch = async ( ...@@ -126,23 +138,23 @@ export const findEventForStateBatch = async (
/** /**
* Finds the first state batch index that has not yet passed the fault proof window. * Finds the first state batch index that has not yet passed the fault proof window.
* *
* @param scc StateCommitmentChain contract. * @param oracle Output oracle contract.
* @returns Starting state root batch index. * @returns Starting state root batch index.
*/ */
export const findFirstUnfinalizedStateBatchIndex = async ( export const findFirstUnfinalizedStateBatchIndex = async <TSubmissionEventArgs>(
scc: Contract oracle: OutputOracle<TSubmissionEventArgs>,
fpw: number
): Promise<number> => { ): Promise<number> => {
const fpw = (await scc.FRAUD_PROOF_WINDOW()).toNumber() const latestBlock = await oracle.contract.provider.getBlock('latest')
const latestBlock = await scc.provider.getBlock('latest') const totalBatches = (await oracle.getTotalElements()).toNumber()
const totalBatches = (await scc.getTotalBatches()).toNumber()
// Perform a binary search to find the next batch that will pass the challenge period. // Perform a binary search to find the next batch that will pass the challenge period.
let lo = 0 let lo = 0
let hi = totalBatches let hi = totalBatches
while (lo !== hi) { while (lo !== hi) {
const mid = Math.floor((lo + hi) / 2) const mid = Math.floor((lo + hi) / 2)
const event = await findEventForStateBatch(scc, mid) const event = await findEventForStateBatch(oracle, mid)
const block = await scc.provider.getBlock(event.blockNumber) const block = await oracle.contract.provider.getBlock(event.blockNumber)
if (block.timestamp + fpw < latestBlock.timestamp) { if (block.timestamp + fpw < latestBlock.timestamp) {
lo = mid + 1 lo = mid + 1
......
...@@ -9,21 +9,23 @@ import { ...@@ -9,21 +9,23 @@ import {
import { getChainId, sleep, toRpcHexString } from '@eth-optimism/core-utils' import { getChainId, sleep, toRpcHexString } from '@eth-optimism/core-utils'
import { CrossChainMessenger } from '@eth-optimism/sdk' import { CrossChainMessenger } from '@eth-optimism/sdk'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Contract, ethers, Transaction } from 'ethers' import { ethers, Transaction } from 'ethers'
import dateformat from 'dateformat' import dateformat from 'dateformat'
import { version } from '../package.json' import { version } from '../package.json'
import { import {
findFirstUnfinalizedStateBatchIndex, findFirstUnfinalizedStateBatchIndex,
findEventForStateBatch, findEventForStateBatch,
updateStateBatchEventCache,
PartialEvent, PartialEvent,
OutputOracle,
updateOracleCache,
} from './helpers' } from './helpers'
type Options = { type Options = {
l1RpcProvider: Provider l1RpcProvider: Provider
l2RpcProvider: Provider l2RpcProvider: Provider
startBatchIndex: number startBatchIndex: number
bedrock: boolean
} }
type Metrics = { type Metrics = {
...@@ -34,7 +36,7 @@ type Metrics = { ...@@ -34,7 +36,7 @@ type Metrics = {
type State = { type State = {
fpw: number fpw: number
scc: Contract oo: OutputOracle<any>
messenger: CrossChainMessenger messenger: CrossChainMessenger
highestCheckedBatchIndex: number highestCheckedBatchIndex: number
diverged: boolean diverged: boolean
...@@ -65,6 +67,12 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -65,6 +67,12 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
desc: 'Batch index to start checking from', desc: 'Batch index to start checking from',
public: true, public: true,
}, },
bedrock: {
validator: validators.bool,
default: false,
desc: 'Whether or not the service is running against a Bedrock chain',
public: true,
},
}, },
metricsSpec: { metricsSpec: {
highestBatchIndex: { highestBatchIndex: {
...@@ -103,24 +111,42 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -103,24 +111,42 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
l2SignerOrProvider: this.options.l2RpcProvider, l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId: await getChainId(this.options.l1RpcProvider), l1ChainId: await getChainId(this.options.l1RpcProvider),
l2ChainId: await getChainId(this.options.l2RpcProvider), l2ChainId: await getChainId(this.options.l2RpcProvider),
bedrock: this.options.bedrock,
}) })
// Not diverged by default. // Not diverged by default.
this.state.diverged = false this.state.diverged = false
// We use this a lot, a bit cleaner to pull out to the top level of the state object. // We use this a lot, a bit cleaner to pull out to the top level of the state object.
this.state.scc = this.state.messenger.contracts.l1.StateCommitmentChain this.state.fpw = await this.state.messenger.getChallengePeriodSeconds()
this.state.fpw = (await this.state.scc.FRAUD_PROOF_WINDOW()).toNumber() if (this.options.bedrock) {
const oo = this.state.messenger.contracts.l1.L2OutputOracle
this.state.oo = {
contract: oo,
filter: oo.filters.OutputProposed(),
getTotalElements: async () => oo.latestOutputIndex(),
getEventIndex: (args) => args.l2OutputIndex,
}
} else {
const oo = this.state.messenger.contracts.l1.StateCommitmentChain
this.state.oo = {
contract: oo,
filter: oo.filters.StateBatchAppended(),
getTotalElements: async () => oo.getTotalBatches(),
getEventIndex: (args) => args._batchIndex,
}
}
// Populate the event cache. // Populate the event cache.
this.logger.info(`warming event cache, this might take a while...`) this.logger.info(`warming event cache, this might take a while...`)
await updateStateBatchEventCache(this.state.scc) await updateOracleCache(this.state.oo)
// Figure out where to start syncing from. // Figure out where to start syncing from.
if (this.options.startBatchIndex === -1) { if (this.options.startBatchIndex === -1) {
this.logger.info(`finding appropriate starting height`) this.logger.info(`finding appropriate starting height`)
const firstUnfinalized = await findFirstUnfinalizedStateBatchIndex( const firstUnfinalized = await findFirstUnfinalizedStateBatchIndex(
this.state.scc this.state.oo,
this.state.fpw
) )
// We may not have an unfinalized batches in the case where no batches have been submitted // We may not have an unfinalized batches in the case where no batches have been submitted
...@@ -129,7 +155,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -129,7 +155,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
if (firstUnfinalized === undefined) { if (firstUnfinalized === undefined) {
this.logger.info(`no unfinalized batches found, starting from latest`) this.logger.info(`no unfinalized batches found, starting from latest`)
this.state.highestCheckedBatchIndex = ( this.state.highestCheckedBatchIndex = (
await this.state.scc.getTotalBatches() await this.state.oo.getTotalElements()
).toNumber() ).toNumber()
} else { } else {
this.state.highestCheckedBatchIndex = firstUnfinalized this.state.highestCheckedBatchIndex = firstUnfinalized
...@@ -141,6 +167,14 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -141,6 +167,14 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
this.logger.info(`starting height`, { this.logger.info(`starting height`, {
startBatchIndex: this.state.highestCheckedBatchIndex, startBatchIndex: this.state.highestCheckedBatchIndex,
}) })
// Set the initial metrics.
this.metrics.highestBatchIndex.set(
{
type: 'checked',
},
this.state.highestCheckedBatchIndex
)
} }
async routes(router: ExpressRouter): Promise<void> { async routes(router: ExpressRouter): Promise<void> {
...@@ -154,7 +188,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -154,7 +188,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
async main(): Promise<void> { async main(): Promise<void> {
let latestBatchIndex: number let latestBatchIndex: number
try { try {
latestBatchIndex = (await this.state.scc.getTotalBatches()).toNumber() latestBatchIndex = (await this.state.oo.getTotalElements()).toNumber()
} catch (err) { } catch (err) {
this.logger.error(`got error when connecting to node`, { this.logger.error(`got error when connecting to node`, {
error: err, error: err,
...@@ -189,7 +223,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -189,7 +223,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
let event: PartialEvent let event: PartialEvent
try { try {
event = await findEventForStateBatch( event = await findEventForStateBatch(
this.state.scc, this.state.oo,
this.state.highestCheckedBatchIndex this.state.highestCheckedBatchIndex
) )
} catch (err) { } catch (err) {
...@@ -206,34 +240,6 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -206,34 +240,6 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
return return
} }
let batchTransaction: Transaction
try {
batchTransaction = await this.options.l1RpcProvider.getTransaction(
event.transactionHash
)
} catch (err) {
this.logger.error(`got error when connecting to node`, {
error: err,
node: 'l1',
section: 'getTransaction',
})
this.metrics.nodeConnectionFailures.inc({
layer: 'l1',
section: 'getTransaction',
})
await sleep(15000)
return
}
const [stateRoots] = this.state.scc.interface.decodeFunctionData(
'appendStateBatch',
batchTransaction.data
)
const batchStart = event.args._prevTotalElements.toNumber() + 1
const batchSize = event.args._batchSize.toNumber()
const batchEnd = batchStart + batchSize
let latestBlock: number let latestBlock: number
try { try {
latestBlock = await this.options.l2RpcProvider.getBlockNumber() latestBlock = await this.options.l2RpcProvider.getBlockNumber()
...@@ -251,55 +257,80 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -251,55 +257,80 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
return return
} }
if (latestBlock < batchEnd) { if (this.options.bedrock) {
this.logger.info(`node is behind, waiting for sync`, { if (latestBlock < event.args.l2BlockNumber.toNumber()) {
batchEnd, this.logger.info(`node is behind, waiting for sync`, {
latestBlock, batchEnd: event.args.l2BlockNumber.toNumber(),
}) latestBlock,
return })
} return
}
// `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into let targetBlock: any
// multiple requests of maximum 1000 blocks in the case that batchSize > 1000.
let blocks: any[] = []
for (let i = 0; i < batchSize; i += 1000) {
let newBlocks: any[]
try { try {
newBlocks = await ( targetBlock = await (
this.options.l2RpcProvider as ethers.providers.JsonRpcProvider this.options.l2RpcProvider as ethers.providers.JsonRpcProvider
).send('eth_getBlockRange', [ ).send('eth_getBlockByNumber', [
toRpcHexString(batchStart + i), toRpcHexString(event.args.l2BlockNumber.toNumber()),
toRpcHexString(batchStart + i + Math.min(batchSize - i, 1000) - 1),
false, false,
]) ])
} catch (err) { } catch (err) {
this.logger.error(`got error when connecting to node`, { this.logger.error(`got error when connecting to node`, {
error: err, error: err,
node: 'l2', node: 'l2',
section: 'getBlockRange', section: 'getBlock',
}) })
this.metrics.nodeConnectionFailures.inc({ this.metrics.nodeConnectionFailures.inc({
layer: 'l2', layer: 'l2',
section: 'getBlockRange', section: 'getBlock',
}) })
await sleep(15000) await sleep(15000)
return return
} }
blocks = blocks.concat(newBlocks) let messagePasserProofResponse: any
} try {
messagePasserProofResponse = await (
this.options.l2RpcProvider as ethers.providers.JsonRpcProvider
).send('eth_getProof', [
this.state.messenger.contracts.l2.BedrockMessagePasser.address,
[],
toRpcHexString(event.args.l2BlockNumber.toNumber()),
])
} catch (err) {
this.logger.error(`got error when connecting to node`, {
error: err,
node: 'l2',
section: 'getProof',
})
this.metrics.nodeConnectionFailures.inc({
layer: 'l2',
section: 'getProof',
})
await sleep(15000)
return
}
const outputRoot = ethers.utils.solidityKeccak256(
['uint256', 'bytes32', 'bytes32', 'bytes32'],
[
0,
targetBlock.stateRoot,
messagePasserProofResponse.storageHash,
targetBlock.hash,
]
)
for (const [i, stateRoot] of stateRoots.entries()) { if (outputRoot !== event.args.outputRoot) {
if (blocks[i].stateRoot !== stateRoot) {
this.state.diverged = true this.state.diverged = true
this.metrics.isCurrentlyMismatched.set(1) this.metrics.isCurrentlyMismatched.set(1)
this.logger.error(`state root mismatch`, { this.logger.error(`state root mismatch`, {
blockNumber: blocks[i].number, blockNumber: targetBlock.number,
expectedStateRoot: blocks[i].stateRoot, expectedStateRoot: event.args.outputRoot,
actualStateRoot: stateRoot, actualStateRoot: outputRoot,
finalizationTime: dateformat( finalizationTime: dateformat(
new Date( new Date(
(ethers.BigNumber.from(blocks[i].timestamp).toNumber() + (ethers.BigNumber.from(targetBlock.timestamp).toNumber() +
this.state.fpw) * this.state.fpw) *
1000 1000
), ),
...@@ -308,8 +339,99 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -308,8 +339,99 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
}) })
return return
} }
} else {
let batchTransaction: Transaction
try {
batchTransaction = await this.options.l1RpcProvider.getTransaction(
event.transactionHash
)
} catch (err) {
this.logger.error(`got error when connecting to node`, {
error: err,
node: 'l1',
section: 'getTransaction',
})
this.metrics.nodeConnectionFailures.inc({
layer: 'l1',
section: 'getTransaction',
})
await sleep(15000)
return
}
const [stateRoots] = this.state.oo.contract.interface.decodeFunctionData(
'appendStateBatch',
batchTransaction.data
)
const batchStart = event.args._prevTotalElements.toNumber() + 1
const batchSize = event.args._batchSize.toNumber()
const batchEnd = batchStart + batchSize
if (latestBlock < batchEnd) {
this.logger.info(`node is behind, waiting for sync`, {
batchEnd,
latestBlock,
})
return
}
// `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into
// multiple requests of maximum 1000 blocks in the case that batchSize > 1000.
let blocks: any[] = []
for (let i = 0; i < batchSize; i += 1000) {
let newBlocks: any[]
try {
newBlocks = await (
this.options.l2RpcProvider as ethers.providers.JsonRpcProvider
).send('eth_getBlockRange', [
toRpcHexString(batchStart + i),
toRpcHexString(batchStart + i + Math.min(batchSize - i, 1000) - 1),
false,
])
} catch (err) {
this.logger.error(`got error when connecting to node`, {
error: err,
node: 'l2',
section: 'getBlockRange',
})
this.metrics.nodeConnectionFailures.inc({
layer: 'l2',
section: 'getBlockRange',
})
await sleep(15000)
return
}
blocks = blocks.concat(newBlocks)
}
for (const [i, stateRoot] of stateRoots.entries()) {
if (blocks[i].stateRoot !== stateRoot) {
this.state.diverged = true
this.metrics.isCurrentlyMismatched.set(1)
this.logger.error(`state root mismatch`, {
blockNumber: blocks[i].number,
expectedStateRoot: blocks[i].stateRoot,
actualStateRoot: stateRoot,
finalizationTime: dateformat(
new Date(
(ethers.BigNumber.from(blocks[i].timestamp).toNumber() +
this.state.fpw) *
1000
),
'mmmm dS, yyyy, h:MM:ss TT'
),
})
return
}
}
} }
this.logger.info(`checked batch ok`, {
batchIndex: this.state.highestCheckedBatchIndex,
})
this.state.highestCheckedBatchIndex++ this.state.highestCheckedBatchIndex++
this.metrics.highestBatchIndex.set( this.metrics.highestBatchIndex.set(
{ {
......
...@@ -12,6 +12,7 @@ import { expect } from './setup' ...@@ -12,6 +12,7 @@ import { expect } from './setup'
import { import {
findEventForStateBatch, findEventForStateBatch,
findFirstUnfinalizedStateBatchIndex, findFirstUnfinalizedStateBatchIndex,
OutputOracle,
} from '../src' } from '../src'
describe('helpers', () => { describe('helpers', () => {
...@@ -28,6 +29,7 @@ describe('helpers', () => { ...@@ -28,6 +29,7 @@ describe('helpers', () => {
let AddressManager: Contract let AddressManager: Contract
let ChainStorageContainer: Contract let ChainStorageContainer: Contract
let StateCommitmentChain: Contract let StateCommitmentChain: Contract
let oracle: OutputOracle<any>
beforeEach(async () => { beforeEach(async () => {
// Set up fakes // Set up fakes
FakeBondManager = await smock.fake(getContractInterface('BondManager')) FakeBondManager = await smock.fake(getContractInterface('BondManager'))
...@@ -67,6 +69,13 @@ describe('helpers', () => { ...@@ -67,6 +69,13 @@ describe('helpers', () => {
// Set up mock returns // Set up mock returns
FakeCanonicalTransactionChain.getTotalElements.returns(1000000000) // just needs to be large FakeCanonicalTransactionChain.getTotalElements.returns(1000000000) // just needs to be large
FakeBondManager.isCollateralized.returns(true) FakeBondManager.isCollateralized.returns(true)
oracle = {
contract: StateCommitmentChain,
filter: StateCommitmentChain.filters.StateBatchAppended(),
getTotalElements: async () => StateCommitmentChain.getTotalBatches(),
getEventIndex: (args: any) => args._batchIndex,
}
}) })
describe('findEventForStateBatch', () => { describe('findEventForStateBatch', () => {
...@@ -79,7 +88,7 @@ describe('helpers', () => { ...@@ -79,7 +88,7 @@ describe('helpers', () => {
}) })
it('should return the event', async () => { it('should return the event', async () => {
const event = await findEventForStateBatch(StateCommitmentChain, 0) const event = await findEventForStateBatch(oracle, 0)
expect(event.args._batchIndex).to.equal(0) expect(event.args._batchIndex).to.equal(0)
}) })
...@@ -88,7 +97,7 @@ describe('helpers', () => { ...@@ -88,7 +97,7 @@ describe('helpers', () => {
describe('when the event does not exist', () => { describe('when the event does not exist', () => {
it('should throw an error', async () => { it('should throw an error', async () => {
await expect( await expect(
findEventForStateBatch(StateCommitmentChain, 0) findEventForStateBatch(oracle, 0)
).to.eventually.be.rejectedWith('unable to find event for batch') ).to.eventually.be.rejectedWith('unable to find event for batch')
}) })
}) })
...@@ -119,7 +128,8 @@ describe('helpers', () => { ...@@ -119,7 +128,8 @@ describe('helpers', () => {
it('should find the first batch older than the FPW', async () => { it('should find the first batch older than the FPW', async () => {
const first = await findFirstUnfinalizedStateBatchIndex( const first = await findFirstUnfinalizedStateBatchIndex(
StateCommitmentChain oracle,
challengeWindowSeconds
) )
expect(first).to.equal(1) expect(first).to.equal(1)
...@@ -144,7 +154,8 @@ describe('helpers', () => { ...@@ -144,7 +154,8 @@ describe('helpers', () => {
it('should return zero', async () => { it('should return zero', async () => {
const first = await findFirstUnfinalizedStateBatchIndex( const first = await findFirstUnfinalizedStateBatchIndex(
StateCommitmentChain oracle,
challengeWindowSeconds
) )
expect(first).to.equal(0) expect(first).to.equal(0)
...@@ -177,7 +188,8 @@ describe('helpers', () => { ...@@ -177,7 +188,8 @@ describe('helpers', () => {
it('should return undefined', async () => { it('should return undefined', async () => {
const first = await findFirstUnfinalizedStateBatchIndex( const first = await findFirstUnfinalizedStateBatchIndex(
StateCommitmentChain oracle,
challengeWindowSeconds
) )
expect(first).to.equal(undefined) expect(first).to.equal(undefined)
......
...@@ -12090,7 +12090,7 @@ level-ws@^2.0.0: ...@@ -12090,7 +12090,7 @@ level-ws@^2.0.0:
readable-stream "^3.1.0" readable-stream "^3.1.0"
xtend "^4.0.1" xtend "^4.0.1"
level@^6.0.1: "level6@npm:level@^6.0.1":
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6" resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6"
integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw== integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==
......
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