Commit 363c5d7f authored by Raffaele's avatar Raffaele Committed by GitHub

adding timestamp for blocks checked and highest, so we can monitor ho… (#10691)

* adding timestamp for blocks checked and highest, so we can monitor how long ago the block we are using was produced

* adding logs for connecting disputeGame with withdrawalHash

* imporved logs

* adding details on logs for wd-mon and faultproof-wd-mon

* adding extra information for wd-mon also for valid withdrawal

* Update packages/chain-mon/src/wd-mon/service.ts
Co-authored-by: default avatarcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/chain-mon/src/faultproof-wd-mon/service.ts
Co-authored-by: default avatarcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fixing object

* improved logs and metrics for wd mon

* added extra logging information and fixing bugs on initial block number

---------
Co-authored-by: default avatarcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
parent 3dae2f58
...@@ -33,6 +33,8 @@ type Options = { ...@@ -33,6 +33,8 @@ type Options = {
type Metrics = { type Metrics = {
highestCheckedBlockNumber: Gauge highestCheckedBlockNumber: Gauge
highestKnownBlockNumber: Gauge highestKnownBlockNumber: Gauge
highestCheckedBlockTimestamp: Gauge
highestKnownBlockTimestamp: Gauge
withdrawalsValidated: Gauge withdrawalsValidated: Gauge
invalidProposalWithdrawals: Gauge invalidProposalWithdrawals: Gauge
invalidProofWithdrawals: Gauge invalidProofWithdrawals: Gauge
...@@ -50,11 +52,13 @@ type State = { ...@@ -50,11 +52,13 @@ type State = {
withdrawalHash: string withdrawalHash: string
senderAddress: string senderAddress: string
disputeGame: ethers.Contract disputeGame: ethers.Contract
event: ethers.Event
}> }>
invalidProofWithdrawals: Array<{ invalidProofWithdrawals: Array<{
withdrawalHash: string withdrawalHash: string
senderAddress: string senderAddress: string
disputeGame: ethers.Contract disputeGame: ethers.Contract
event: ethers.Event
}> }>
} }
...@@ -133,11 +137,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -133,11 +137,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
desc: 'Highest L1 block number that we have searched.', desc: 'Highest L1 block number that we have searched.',
labels: ['type'], labels: ['type'],
}, },
highestCheckedBlockTimestamp: {
type: Gauge,
desc: 'Timestamp of the highest L1 block number that we have searched.',
labels: ['type'],
},
highestKnownBlockNumber: { highestKnownBlockNumber: {
type: Gauge, type: Gauge,
desc: 'Highest L1 block number that we have seen.', desc: 'Highest L1 block number that we have seen.',
labels: ['type'], labels: ['type'],
}, },
highestKnownBlockTimestamp: {
type: Gauge,
desc: 'Timestamp of the highest L1 block number that we have seen.',
labels: ['type'],
},
invalidProposalWithdrawals: { invalidProposalWithdrawals: {
type: Gauge, type: Gauge,
desc: 'Number of withdrawals against invalid proposals.', desc: 'Number of withdrawals against invalid proposals.',
...@@ -212,6 +227,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -212,6 +227,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
this.state.highestUncheckedBlockNumber = this.state.highestUncheckedBlockNumber =
DEFAULT_STARTING_BLOCK_NUMBERS[l2ChainId] || 0 DEFAULT_STARTING_BLOCK_NUMBERS[l2ChainId] || 0
} }
this.logger.info(
`initialized starting at block number ${this.state.highestUncheckedBlockNumber}`
)
//make sure the highestUncheckedBlockNumber is not higher than the latest block number on chain
const latestL1BlockNumber =
await this.options.l1RpcProvider.getBlockNumber()
this.state.highestUncheckedBlockNumber = Math.min(
this.state.highestUncheckedBlockNumber,
latestL1BlockNumber
)
this.logger.info(
`starting at block number ${this.state.highestUncheckedBlockNumber}`
)
// Default state is that forgeries have not been detected. // Default state is that forgeries have not been detected.
this.state.forgeryDetected = false this.state.forgeryDetected = false
...@@ -247,6 +278,14 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -247,6 +278,14 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
const disputeGameAddress = disputeGame.address const disputeGameAddress = disputeGame.address
const isGameBlacklisted = const isGameBlacklisted =
this.state.portal.disputeGameBlacklist(disputeGameAddress) this.state.portal.disputeGameBlacklist(disputeGameAddress)
const event = disputeGameData.event
const block = await event.getBlock()
const ts =
dateformat(
new Date(block.timestamp * 1000),
'mmmm dS, yyyy, h:MM:ss TT',
true
) + ' UTC'
if (isGameBlacklisted) { if (isGameBlacklisted) {
this.state.invalidProposalWithdrawals.splice(i, 1) this.state.invalidProposalWithdrawals.splice(i, 1)
...@@ -254,18 +293,57 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -254,18 +293,57 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
const status = await disputeGame.status() const status = await disputeGame.status()
if (status === GameStatus.CHALLENGER_WINS) { if (status === GameStatus.CHALLENGER_WINS) {
this.state.invalidProposalWithdrawals.splice(i, 1) this.state.invalidProposalWithdrawals.splice(i, 1)
this.logger.info(
`withdrawalHash not seen on L2 - game correctly resolved`,
{
withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
disputeGameAddress: disputeGame.address,
blockNumber: block.number,
transaction: event.transactionHash,
}
)
} else if (status === GameStatus.DEFENDER_WINS) { } else if (status === GameStatus.DEFENDER_WINS) {
this.logger.error(
`withdrawalHash not seen on L2 - forgery detected`,
{
withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
disputeGameAddress: disputeGame.address,
blockNumber: block.number,
transaction: event.transactionHash,
}
)
this.state.forgeryDetected = true this.state.forgeryDetected = true
this.metrics.isDetectingForgeries.set( this.metrics.isDetectingForgeries.set(
Number(this.state.forgeryDetected) Number(this.state.forgeryDetected)
) )
} else {
this.logger.warn(
`withdrawalHash not seen on L2 - game still IN_PROGRESS`,
{
withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
disputeGameAddress: disputeGame.address,
blockNumber: block.number,
transaction: event.transactionHash,
}
)
} }
} }
} }
// Get the latest L1 block number.
let latestL1BlockNumber: number let latestL1BlockNumber: number
let latestL1Block: ethers.providers.Block
let highestUncheckedBlock: ethers.providers.Block
try { try {
// Get the latest L1 block number.
latestL1BlockNumber = await this.options.l1RpcProvider.getBlockNumber() latestL1BlockNumber = await this.options.l1RpcProvider.getBlockNumber()
latestL1Block = await this.options.l1RpcProvider.getBlock(
latestL1BlockNumber
)
highestUncheckedBlock = await this.options.l1RpcProvider.getBlock(
this.state.highestUncheckedBlockNumber
)
} catch (err) { } catch (err) {
// Log the issue so we can debug it. // Log the issue so we can debug it.
this.logger.error(`got error when connecting to node`, { this.logger.error(`got error when connecting to node`, {
...@@ -285,10 +363,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -285,10 +363,22 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
} }
// Update highest block number metrics so we can keep track of how the service is doing. // Update highest block number metrics so we can keep track of how the service is doing.
this.metrics.highestKnownBlockNumber.set(latestL1BlockNumber)
this.metrics.highestCheckedBlockNumber.set( this.metrics.highestCheckedBlockNumber.set(
{ type: 'L1' },
this.state.highestUncheckedBlockNumber this.state.highestUncheckedBlockNumber
) )
this.metrics.highestKnownBlockNumber.set(
{ type: 'L1' },
latestL1BlockNumber
)
this.metrics.highestCheckedBlockTimestamp.set(
{ type: 'L1' },
highestUncheckedBlock.timestamp
)
this.metrics.highestKnownBlockTimestamp.set(
{ type: 'L1' },
latestL1Block.timestamp
)
// Check if the RPC provider is behind us for some reason. Can happen occasionally, // Check if the RPC provider is behind us for some reason. Can happen occasionally,
// particularly if connected to an RPC provider that load balances over multiple nodes that // particularly if connected to an RPC provider that load balances over multiple nodes that
...@@ -310,6 +400,9 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -310,6 +400,9 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
this.logger.info(`checking recent blocks`, { this.logger.info(`checking recent blocks`, {
fromBlockNumber: this.state.highestUncheckedBlockNumber, fromBlockNumber: this.state.highestUncheckedBlockNumber,
toBlockNumber, toBlockNumber,
latestL1BlockNumber,
percentageDone:
Math.floor((toBlockNumber / latestL1BlockNumber) * 100) + '% done',
}) })
// Query for WithdrawalProven events within the specified block range. // Query for WithdrawalProven events within the specified block range.
...@@ -371,6 +464,10 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -371,6 +464,10 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
// Unlike below we don't grab the timestamp here because it adds an unnecessary request. // Unlike below we don't grab the timestamp here because it adds an unnecessary request.
this.logger.info(`valid withdrawal`, { this.logger.info(`valid withdrawal`, {
withdrawalHash: event.args.withdrawalHash, withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
disputeGameAddress: disputeGame.address,
blockNumber: block.number,
transaction: event.transactionHash,
}) })
// Bump the withdrawals metric so we can keep track. // Bump the withdrawals metric so we can keep track.
...@@ -379,10 +476,16 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -379,10 +476,16 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
this.state.invalidProofWithdrawals.push(disputeGameData) this.state.invalidProofWithdrawals.push(disputeGameData)
// Uh oh! // Uh oh!
this.logger.error(`withdrawalHash not seen on L2`, { this.logger.error(
withdrawalHash: event.args.withdrawalHash, `withdrawalHash not seen on L2 - forgery detected`,
provenAt: ts, {
}) withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
disputeGameAddress: disputeGame.address,
blockNumber: block.number,
transaction: event.transactionHash,
}
)
// Change to forgery state. // Change to forgery state.
this.state.forgeryDetected = true this.state.forgeryDetected = true
...@@ -392,9 +495,10 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -392,9 +495,10 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
} }
} else { } else {
this.state.invalidProposalWithdrawals.push(disputeGameData) this.state.invalidProposalWithdrawals.push(disputeGameData)
this.logger.info(`invalid proposal`, { this.logger.warn(`invalid proposal`, {
withdrawalHash: event.args.withdrawalHash, withdrawalHash: event.args.withdrawalHash,
provenAt: ts, provenAt: ts,
disputeGameAddress: disputeGame.address,
}) })
} }
} }
...@@ -410,18 +514,20 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -410,18 +514,20 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
* @param event The event containing the withdrawal hash. * @param event The event containing the withdrawal hash.
* @returns An array of objects containing the withdrawal hash, sender address, and dispute game address. * @returns An array of objects containing the withdrawal hash, sender address, and dispute game address.
*/ */
async getWithdrawalDisputeGames(event: ethers.Event): Promise< async getWithdrawalDisputeGames(event_in: ethers.Event): Promise<
Array<{ Array<{
withdrawalHash: string withdrawalHash: string
senderAddress: string senderAddress: string
disputeGame: ethers.Contract disputeGame: ethers.Contract
event: ethers.Event
}> }>
> { > {
const withdrawalHash = event.args.withdrawalHash const withdrawalHash = event_in.args.withdrawalHash
const disputeGameMap: Array<{ const disputeGameMap: Array<{
withdrawalHash: string withdrawalHash: string
senderAddress: string senderAddress: string
disputeGame: ethers.Contract disputeGame: ethers.Contract
event: ethers.Event
}> = [] }> = []
const numProofSubmitter = await this.state.portal.numProofSubmitters( const numProofSubmitter = await this.state.portal.numProofSubmitters(
...@@ -450,6 +556,7 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2< ...@@ -450,6 +556,7 @@ export class FaultProofWithdrawalMonitor extends BaseServiceV2<
withdrawalHash, withdrawalHash,
senderAddress: proofSubmitter, senderAddress: proofSubmitter,
disputeGame: disputeGame_, disputeGame: disputeGame_,
event: event_in,
}) })
}) })
) )
......
...@@ -30,6 +30,7 @@ type Metrics = { ...@@ -30,6 +30,7 @@ type Metrics = {
withdrawalsValidated: Gauge withdrawalsValidated: Gauge
isDetectingForgeries: Gauge isDetectingForgeries: Gauge
nodeConnectionFailures: Gauge nodeConnectionFailures: Gauge
detectedForgeries: Gauge
} }
type State = { type State = {
...@@ -110,6 +111,11 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> { ...@@ -110,6 +111,11 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> {
desc: 'Number of times node connection has failed', desc: 'Number of times node connection has failed',
labels: ['layer', 'section'], labels: ['layer', 'section'],
}, },
detectedForgeries: {
type: Gauge,
desc: 'detected forged withdrawals',
labels: ['withdrawalHash', 'provenAt', 'blockNumber', 'transaction'],
},
}, },
}) })
} }
...@@ -223,6 +229,9 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> { ...@@ -223,6 +229,9 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> {
this.logger.info(`checking recent blocks`, { this.logger.info(`checking recent blocks`, {
fromBlockNumber: this.state.highestUncheckedBlockNumber, fromBlockNumber: this.state.highestUncheckedBlockNumber,
toBlockNumber, toBlockNumber,
latestL1BlockNumber,
percentageDone:
Math.floor((toBlockNumber / latestL1BlockNumber) * 100) + '% done',
}) })
// Query for WithdrawalProven events within the specified block range. // Query for WithdrawalProven events within the specified block range.
...@@ -257,39 +266,50 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> { ...@@ -257,39 +266,50 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> {
const hash = event.args.withdrawalHash const hash = event.args.withdrawalHash
const exists = await this.state.messenger.sentMessages(hash) const exists = await this.state.messenger.sentMessages(hash)
const block = await event.getBlock()
const ts = `${dateformat(
new Date(block.timestamp * 1000),
'mmmm dS, yyyy, h:MM:ss TT',
true
)} UTC`
// Hopefully the withdrawal exists! // Hopefully the withdrawal exists!
if (exists) { if (exists) {
// Unlike below we don't grab the timestamp here because it adds an unnecessary request. // Unlike below we don't grab the timestamp here because it adds an unnecessary request.
this.logger.info(`valid withdrawal`, { this.logger.info(`valid withdrawal`, {
withdrawalHash: event.args.withdrawalHash, withdrawalHash: event.args.withdrawalHash,
provenAt: ts,
blockNumber: block.number,
transaction: event.transactionHash,
}) })
// Bump the withdrawals metric so we can keep track. // Bump the withdrawals metric so we can keep track.
this.metrics.withdrawalsValidated.inc() this.metrics.withdrawalsValidated.inc()
} else { } else {
// Grab and format the timestamp so it's clear how much time is left. // Grab and format the timestamp so it's clear how much time is left.
const block = await event.getBlock()
const ts = `${dateformat(
new Date(block.timestamp * 1000),
'mmmm dS, yyyy, h:MM:ss TT',
true
)} UTC`
// Uh oh! // Uh oh!
this.logger.error(`withdrawalHash not seen on L2`, { this.logger.error(`withdrawalHash not seen on L2`, {
withdrawalHash: event.args.withdrawalHash, withdrawalHash: event.args.withdrawalHash,
provenAt: ts, provenAt:
dateformat(
new Date(block.timestamp * 1000),
'mmmm dS, yyyy, h:MM:ss TT',
true
) + ' UTC',
blockNumber: block.number.toString(),
transaction: event.transactionHash,
}) })
// Change to forgery state. // Change to forgery state.
this.state.forgeryDetected = true this.state.forgeryDetected = true
this.metrics.isDetectingForgeries.set(1) this.metrics.isDetectingForgeries.set(1)
this.metrics.detectedForgeries.inc({
// Return early so that we never increment the highest unchecked block number and therefore withdrawalHash: hash,
// will continue to loop on this forgery indefinitely. We probably want to change this provenAt: ts,
// behavior at some point so that we keep scanning for additional forgeries since the blockNumber: block.number.toString(),
// existence of one forgery likely implies the existence of many others. transaction: event.transactionHash,
return sleep(this.options.sleepTimeMs) })
} }
} }
......
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