From 9934adb2b882d67e71fbc46b4cb8d5ebbc6c6584 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 3 Sep 2024 13:48:23 -0300 Subject: [PATCH] fix: Handle proofs for blocks submitted out of range (#8357) `prover-stats` was throwing an error when querying with a start-block that was not the start of the chain, since it'd look up for the submission time of the L2 block for a proof, but would not find it because it was out of range. --- yarn-project/cli/src/cmds/l1/prover_stats.ts | 44 ++++++++++++------- .../foundation/src/collection/object.ts | 8 ++-- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/yarn-project/cli/src/cmds/l1/prover_stats.ts b/yarn-project/cli/src/cmds/l1/prover_stats.ts index c0da44ead4c..e2728be4da5 100644 --- a/yarn-project/cli/src/cmds/l1/prover_stats.ts +++ b/yarn-project/cli/src/cmds/l1/prover_stats.ts @@ -2,7 +2,7 @@ import { getL2BlockProcessedLogs, retrieveL2ProofVerifiedEvents } from '@aztec/a import { createAztecNodeClient } from '@aztec/circuit-types'; import { EthAddress } from '@aztec/circuits.js'; import { createEthereumChain } from '@aztec/ethereum'; -import { unique } from '@aztec/foundation/collection'; +import { compactArray, mapValues, unique } from '@aztec/foundation/collection'; import { type LogFn, type Logger, createDebugLogger } from '@aztec/foundation/log'; import chunk from 'lodash.chunk'; @@ -65,6 +65,9 @@ export async function proverStats(opts: { // But if we do, fetch the events for each block submitted, so we can look up their timestamp const blockEvents = await getL2BlockEvents(startBlock, lastBlockNum, batchSize, debugLog, publicClient, rollup); + debugLog.verbose( + `First L2 block within range is ${blockEvents[0]?.args.blockNumber} at L1 block ${blockEvents[0]?.blockNumber}`, + ); // Get the timestamps for every block on every log, both for proof and block submissions const l1BlockNumbers = unique([...events.map(e => e.l1BlockNumber), ...blockEvents.map(e => e.blockNumber)]); @@ -86,24 +89,35 @@ export async function proverStats(opts: { } // Now calculate stats - const stats = groupBy(events, 'proverId'); + const stats = mapValues(groupBy(events, 'proverId'), (blocks, proverId) => + compactArray( + blocks.map(e => { + const provenTimestamp = l1BlockTimestamps[e.l1BlockNumber.toString()]; + const uploadedBlockNumber = l2BlockSubmissions[e.l2BlockNumber.toString()]; + if (!uploadedBlockNumber) { + debugLog.verbose( + `Skipping ${proverId}'s proof for L2 block ${e.l2BlockNumber} as it was before the start block`, + ); + return undefined; + } + const uploadedTimestamp = l1BlockTimestamps[uploadedBlockNumber.toString()]; + const provingTime = provenTimestamp - uploadedTimestamp; + debugLog.debug( + `prover=${e.proverId} blockNumber=${e.l2BlockNumber} uploaded=${uploadedTimestamp} proven=${provenTimestamp} time=${provingTime}`, + ); + return { provenTimestamp, uploadedTimestamp, provingTime, ...e }; + }), + ), + ); + log(`prover_id, blocks_proven_within_timeout, total_blocks_proven, avg_proving_time`); for (const proverId in stats) { - const blocks = stats[proverId].map(e => { - const provenTimestamp = l1BlockTimestamps[e.l1BlockNumber.toString()]; - const uploadedBlockNumber = l2BlockSubmissions[e.l2BlockNumber.toString()]; - const uploadedTimestamp = l1BlockTimestamps[uploadedBlockNumber.toString()]; - const provingTime = provenTimestamp - uploadedTimestamp; - debugLog.debug( - `prover=${e.proverId} blockNumber=${e.l2BlockNumber} uploaded=${uploadedTimestamp} proven=${provenTimestamp} time=${provingTime}`, - ); - return { provenTimestamp, uploadedTimestamp, provingTime, ...e }; - }); - + const blocks = stats[proverId]; const withinTimeout = blocks.filter(b => b.provingTime <= provingTimeout); const uniqueBlocksWithinTimeout = new Set(withinTimeout.map(e => e.l2BlockNumber)); - const uniqueBlocks = new Set(stats[proverId].map(e => e.l2BlockNumber)); - const avgProvingTime = Math.ceil(Number(blocks.reduce((acc, b) => acc + b.provingTime, 0n)) / blocks.length); + const uniqueBlocks = new Set(blocks.map(e => e.l2BlockNumber)); + const avgProvingTime = + blocks.length === 0 ? 0 : Math.ceil(Number(blocks.reduce((acc, b) => acc + b.provingTime, 0n)) / blocks.length); log(`${proverId}, ${uniqueBlocksWithinTimeout.size}, ${uniqueBlocks.size}, ${avgProvingTime}`); } diff --git a/yarn-project/foundation/src/collection/object.ts b/yarn-project/foundation/src/collection/object.ts index b55c3b8688f..9603daf4543 100644 --- a/yarn-project/foundation/src/collection/object.ts +++ b/yarn-project/foundation/src/collection/object.ts @@ -1,19 +1,19 @@ /** Returns a new object with the same keys and where each value has been passed through the mapping function. */ export function mapValues( obj: Record, - fn: (value: T) => U, + fn: (value: T, key: K) => U, ): Record; export function mapValues( obj: Partial>, - fn: (value: T) => U, + fn: (value: T, key: K) => U, ): Partial>; export function mapValues( obj: Record, - fn: (value: T) => U, + fn: (value: T, key: K) => U, ): Record { const result: Record = {} as Record; for (const key in obj) { - result[key] = fn(obj[key]); + result[key] = fn(obj[key], key); } return result; }