diff --git a/src/bridge/SequencerInbox.sol b/src/bridge/SequencerInbox.sol index 3cb3d14..8f10ff6 100644 --- a/src/bridge/SequencerInbox.sol +++ b/src/bridge/SequencerInbox.sol @@ -536,6 +536,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox revert BadSequencerNumber(seqMessageIndex, sequenceNumber); } + if (!isUsingFeeToken) { + // only report batch poster spendings if chain is using ETH as native currency + submitBatchSpendingReport(dataHash, seqMessageIndex, block.basefee, 0); + } + emit SequencerBatchDelivered( seqMessageIndex, beforeAcc, diff --git a/test/contract/sequencerInboxDelayBufferable.spec.ts b/test/contract/sequencerInboxDelayBufferable.spec.ts index fe5bacf..d2165c2 100644 --- a/test/contract/sequencerInboxDelayBufferable.spec.ts +++ b/test/contract/sequencerInboxDelayBufferable.spec.ts @@ -9,6 +9,7 @@ import { setupSequencerInbox, mineBlocks, forceIncludeMessages, + generateEigenDACertificate, } from './testHelpers' // TODO: Add delay proof tests for EigenDA entrypoint @@ -571,6 +572,540 @@ describe('SequencerInboxDelayBufferable', async () => { }) }) +// NOTE: This test is just an extension of the 'SequencerInboxDelayBufferable' and +// the 'SequencerInboxDelayBufferableBlobMock' tests but instead uses the EigenDA +// inbox entrypoint with certificates generated arbitrarily for submission. +// no certs are actually being verified since we use the L2 dummy blob verifier +describe('SequencerInboxDelayBufferableEigenDAMock', async () => { + it('can deplete buffer', async () => { + const { bridge, sequencerInbox, batchPoster, delayConfig, maxDelay } = + await setupSequencerInbox(true, true) + const delayedInboxPending: DelayedMsgDelivered[] = [] + let delayedMessageCount = await bridge.delayedMessageCount() + let seqReportedMessageSubCount = + await bridge.sequencerReportedSubMessageCount() + + expect(delayedMessageCount).to.equal(0) + expect(seqReportedMessageSubCount).to.equal(0) + expect(await sequencerInbox.isDelayBufferable()).to.be.true + + let delayBufferData = await sequencerInbox.buffer() + + // full buffer + expect(delayBufferData.bufferBlocks).to.equal(delayConfig.max) + + const cert = await generateEigenDACertificate() + + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 0, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + delayedMessageCount = await bridge.delayedMessageCount() + seqReportedMessageSubCount = await bridge.sequencerReportedSubMessageCount() + + expect(delayedMessageCount).to.equal(1) + expect(seqReportedMessageSubCount).to.equal(10) + expect(await sequencerInbox.totalDelayedMessagesRead()).to.equal(0) + + await forceIncludeMessages( + sequencerInbox, + delayedInboxPending[0].delayedCount + 1, + delayedInboxPending[0].delayedMessage, + 'ForceIncludeBlockTooSoon' + ) + + await mineBlocks(7200, 12) + + const txnReciept = await forceIncludeMessages( + sequencerInbox, + delayedInboxPending[0].delayedCount + 1, + delayedInboxPending[0].delayedMessage + ) + + let forceIncludedMsg = delayedInboxPending.pop() + const delayBlocks = + txnReciept!.blockNumber - + forceIncludedMsg!.delayedMessage.header.blockNumber + const unexpectedDelayBlocks = delayBlocks - delayConfig.threshold.toNumber() + const replenishAmount = Math.floor( + (delayBlocks * delayConfig.replenishRateInBasis) / 10000 + ) + + expect(await sequencerInbox.totalDelayedMessagesRead()).to.equal(1) + + delayBufferData = await sequencerInbox.buffer() + + // full + expect(delayBufferData.bufferBlocks).to.equal(delayConfig.max) + + expect(delayBufferData.prevBlockNumber).to.equal( + forceIncludedMsg?.delayedMessage.header.blockNumber + ) + + expect(delayBufferData.prevSequencedBlockNumber).to.equal( + txnReciept!.blockNumber + ) + + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 2, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + await mineBlocks(7200, 12) + + const txnReciept2 = await forceIncludeMessages( + sequencerInbox, + delayedInboxPending[0].delayedCount + 1, + delayedInboxPending[0].delayedMessage + ) + forceIncludedMsg = delayedInboxPending.pop() + delayBufferData = await sequencerInbox.buffer() + + const depletedBufferBlocks = + delayConfig.max - unexpectedDelayBlocks + replenishAmount + expect(delayBufferData.bufferBlocks).to.equal(depletedBufferBlocks) + + expect(await sequencerInbox.totalDelayedMessagesRead()).to.equal(2) + + expect(delayBufferData.prevBlockNumber).to.equal( + forceIncludedMsg?.delayedMessage.header.blockNumber + ) + expect(delayBufferData.prevSequencedBlockNumber).to.equal( + txnReciept2!.blockNumber + ) + + const deadline = await sequencerInbox.forceInclusionDeadline( + delayBufferData.prevBlockNumber + ) + const delayBlocksDeadline = + depletedBufferBlocks > maxDelay.delayBlocks + ? maxDelay.delayBlocks + : depletedBufferBlocks + expect(deadline).to.equal( + delayBufferData.prevBlockNumber.add(delayBlocksDeadline) + ) + const delay = delayBufferData.prevSequencedBlockNumber.sub( + delayBufferData.prevBlockNumber + ) + const unexpectedDelayBlocks2 = delay.sub(delayConfig.threshold).toNumber() + const futureBlock = + forceIncludedMsg!.delayedMessage.header.blockNumber + delay.toNumber() + const deadline2 = await sequencerInbox.forceInclusionDeadline(futureBlock) + const replenishAmount2 = Math.floor( + (delay.toNumber() * delayConfig.replenishRateInBasis) / 10000 + ) + const calcBufferBlocks = + depletedBufferBlocks - unexpectedDelayBlocks2 > + delayConfig.threshold.toNumber() + ? depletedBufferBlocks - unexpectedDelayBlocks2 + replenishAmount2 + : delayConfig.threshold.toNumber() + const delayBlocksDeadline2 = + calcBufferBlocks > maxDelay.delayBlocks + ? maxDelay.delayBlocks + : calcBufferBlocks + expect(deadline2).to.equal(futureBlock + delayBlocksDeadline2) + }) + + it('can replenish buffer', async () => { + const cert = await generateEigenDACertificate() + + const { bridge, sequencerInbox, batchPoster, delayConfig } = + await setupSequencerInbox(true, true) + const delayedInboxPending: DelayedMsgDelivered[] = [] + let delayedMessageCount = await bridge.delayedMessageCount() + let seqReportedMessageSubCount = + await bridge.sequencerReportedSubMessageCount() + let delayBufferData = await sequencerInbox.buffer() + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 0, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + delayedMessageCount = await bridge.delayedMessageCount() + seqReportedMessageSubCount = await bridge.sequencerReportedSubMessageCount() + + await forceIncludeMessages( + sequencerInbox, + delayedInboxPending[0].delayedCount + 1, + delayedInboxPending[0].delayedMessage, + 'ForceIncludeBlockTooSoon' + ) + + await mineBlocks(7200, 12) + + await forceIncludeMessages( + sequencerInbox, + delayedInboxPending[0].delayedCount + 1, + delayedInboxPending[0].delayedMessage + ) + + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 2, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + const tx = sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromBlobs( + 3, + delayedMessageCount.add(1), + ethers.constants.AddressZero, + seqReportedMessageSubCount.add(10), + seqReportedMessageSubCount.add(20), + { gasLimit: 10000000 } + ) + await expect(tx).to.be.revertedWith('DelayProofRequired') + + let nextDelayedMsg = delayedInboxPending.pop() + await mineBlocks(delayConfig.threshold.toNumber() - 100, 12) + + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDADelayProof( + 3, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(1), + seqReportedMessageSubCount.add(10), + seqReportedMessageSubCount.add(20), + { + beforeDelayedAcc: nextDelayedMsg!.delayedAcc, + delayedMessage: { + kind: nextDelayedMsg!.delayedMessage.header.kind, + sender: nextDelayedMsg!.delayedMessage.header.sender, + blockNumber: nextDelayedMsg!.delayedMessage.header.blockNumber, + timestamp: nextDelayedMsg!.delayedMessage.header.timestamp, + inboxSeqNum: nextDelayedMsg!.delayedCount, + baseFeeL1: nextDelayedMsg!.delayedMessage.header.baseFee, + messageDataHash: + nextDelayedMsg!.delayedMessage.header.messageDataHash, + }, + }, + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + delayBufferData = await sequencerInbox.buffer() + nextDelayedMsg = delayedInboxPending.pop() + + await mineBlocks(delayConfig.threshold.toNumber() - 100, 12) + + await ( + await sequencerInbox + .connect(batchPoster) + .connect(batchPoster) + .addSequencerL2BatchFromEigenDADelayProof( + 4, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(2), + seqReportedMessageSubCount.add(20), + seqReportedMessageSubCount.add(30), + { + beforeDelayedAcc: nextDelayedMsg!.delayedAcc, + delayedMessage: { + kind: nextDelayedMsg!.delayedMessage.header.kind, + sender: nextDelayedMsg!.delayedMessage.header.sender, + blockNumber: nextDelayedMsg!.delayedMessage.header.blockNumber, + timestamp: nextDelayedMsg!.delayedMessage.header.timestamp, + inboxSeqNum: nextDelayedMsg!.delayedCount, + baseFeeL1: nextDelayedMsg!.delayedMessage.header.baseFee, + messageDataHash: + nextDelayedMsg!.delayedMessage.header.messageDataHash, + }, + }, + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + return res + }) + + const delayBufferData2 = await sequencerInbox.buffer() + const replenishBlocks = Math.floor( + ((nextDelayedMsg!.delayedMessage.header.blockNumber - + delayBufferData.prevBlockNumber.toNumber()) * + delayConfig.replenishRateInBasis) / + 10000 + ) + expect(delayBufferData2.bufferBlocks.toNumber()).to.equal( + delayBufferData.bufferBlocks.toNumber() + replenishBlocks + ) + }) + + it('happy path', async () => { + const cert = await generateEigenDACertificate() + const { bridge, sequencerInbox, batchPoster, delayConfig } = + await setupSequencerInbox(true, true) + const delayedInboxPending: DelayedMsgDelivered[] = [] + const delayedMessageCount = await bridge.delayedMessageCount() + const seqReportedMessageSubCount = + await bridge.sequencerReportedSubMessageCount() + + const block = (await network.provider.send('eth_getBlockByNumber', [ + 'latest', + false, + ])) as Block + const blockNumber = Number.parseInt(block.number.toString(10)) + expect( + blockNumber - (await sequencerInbox.buffer()).prevBlockNumber.toNumber() + ).lessThanOrEqual((await sequencerInbox.buffer()).threshold.toNumber()) + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 0, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + await mineBlocks(delayConfig.threshold.toNumber() - 100, 12) + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 1, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(1), + seqReportedMessageSubCount.add(10), + seqReportedMessageSubCount.add(20), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + return res + }) + }) + + it('unhappy path', async () => { + const cert = await generateEigenDACertificate() + const { bridge, sequencerInbox, batchPoster, delayConfig } = + await setupSequencerInbox(true, true) + let delayedInboxPending: DelayedMsgDelivered[] = [] + const delayedMessageCount = await bridge.delayedMessageCount() + const seqReportedMessageSubCount = + await bridge.sequencerReportedSubMessageCount() + + const block = (await network.provider.send('eth_getBlockByNumber', [ + 'latest', + false, + ])) as Block + const blockNumber = Number.parseInt(block.number.toString(10)) + expect( + blockNumber - (await sequencerInbox.buffer()).prevBlockNumber.toNumber() + ).lessThanOrEqual((await sequencerInbox.buffer()).threshold.toNumber()) + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 0, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount, + seqReportedMessageSubCount.add(10), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + await mineBlocks(delayConfig.threshold.toNumber() - 100, 12) + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 1, + cert, + ethers.constants.AddressZero, + delayedMessageCount, + seqReportedMessageSubCount.add(10), + seqReportedMessageSubCount.add(20), + { gasLimit: 10000000 } + ) + ) + .wait() + .then(res => { + delayedInboxPending.push(getBatchSpendingReport(res)) + }) + + let firstReadMsg = delayedInboxPending[0] + await mineBlocks(101, 12) + + const txn = sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDA( + 2, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(2), + seqReportedMessageSubCount.add(20), + seqReportedMessageSubCount.add(30), + { gasLimit: 10000000 } + ) + await expect(txn).to.be.revertedWith('DelayProofRequired') + + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDADelayProof( + 2, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(2), + seqReportedMessageSubCount.add(20), + seqReportedMessageSubCount.add(30), + { + beforeDelayedAcc: firstReadMsg!.delayedAcc, + delayedMessage: { + kind: firstReadMsg!.delayedMessage.header.kind, + sender: firstReadMsg!.delayedMessage.header.sender, + blockNumber: firstReadMsg!.delayedMessage.header.blockNumber, + timestamp: firstReadMsg!.delayedMessage.header.timestamp, + inboxSeqNum: firstReadMsg!.delayedCount, + baseFeeL1: firstReadMsg!.delayedMessage.header.baseFee, + messageDataHash: + firstReadMsg!.delayedMessage.header.messageDataHash, + }, + }, + { gasLimit: 10000000 } + ) + ) + .wait() + .then(async res => { + delayedInboxPending = [] + delayedInboxPending.push(getBatchSpendingReport(res)) + return res + }) + + const delayBufferDataBefore = await sequencerInbox.buffer() + firstReadMsg = delayedInboxPending[0] + await ( + await sequencerInbox + .connect(batchPoster) + .addSequencerL2BatchFromEigenDADelayProof( + 3, + cert, + ethers.constants.AddressZero, + delayedMessageCount.add(3), + seqReportedMessageSubCount.add(30), + seqReportedMessageSubCount.add(40), + { + beforeDelayedAcc: firstReadMsg!.delayedAcc, + delayedMessage: { + kind: firstReadMsg!.delayedMessage.header.kind, + sender: firstReadMsg!.delayedMessage.header.sender, + blockNumber: firstReadMsg!.delayedMessage.header.blockNumber, + timestamp: firstReadMsg!.delayedMessage.header.timestamp, + inboxSeqNum: firstReadMsg!.delayedCount, + baseFeeL1: firstReadMsg!.delayedMessage.header.baseFee, + messageDataHash: + firstReadMsg!.delayedMessage.header.messageDataHash, + }, + }, + { gasLimit: 10000000 } + ) + ) + .wait() + .then(async res => { + delayedInboxPending = [] + delayedInboxPending.push(getBatchSpendingReport(res)) + return res + }) + + const unexpectedDelayBlocks = + delayBufferDataBefore.prevSequencedBlockNumber.toNumber() - + delayBufferDataBefore.prevBlockNumber.toNumber() - + delayConfig.threshold.toNumber() + const elapsed = + firstReadMsg!.delayedMessage.header.blockNumber - + delayBufferDataBefore.prevBlockNumber.toNumber() + const replenishAmount = Math.floor( + (elapsed * delayConfig.replenishRateInBasis) / 10000 + ) + const bufferBlocksUpdate = + delayBufferDataBefore.bufferBlocks.toNumber() - + Math.min(unexpectedDelayBlocks, elapsed) + + replenishAmount + expect((await sequencerInbox.buffer()).bufferBlocks).to.equal( + Math.min(bufferBlocksUpdate, delayConfig.max) + ) + }) +}) + describe('SequencerInboxDelayBufferableBlobMock', async () => { it('can deplete buffer', async () => { const { bridge, sequencerInbox, batchPoster, delayConfig, maxDelay } = diff --git a/test/contract/testHelpers.ts b/test/contract/testHelpers.ts index 7e6ae00..2073e55 100644 --- a/test/contract/testHelpers.ts +++ b/test/contract/testHelpers.ts @@ -12,6 +12,7 @@ import { SequencerInbox, SequencerInbox__factory, TransparentUpgradeableProxy__factory, + EigenDABlobVerifierL2__factory, } from '../../build/types' import { applyAlias, initializeAccounts } from './utils' import { Event } from '@ethersproject/contracts' @@ -25,11 +26,19 @@ import { SequencerInboxInterface, } from '../../build/types/src/bridge/SequencerInbox' import { ContractReceipt, Signer } from 'ethers' + import { DelayedMsg, DelayedMsgDelivered, MaxTimeVariation, DelayConfig, + EigenDACertStruct, + BlobHeaderStruct, + BatchHeaderStruct, + BatchMetadataStruct, + BlobVerificationProofStruct, + G1PointStruct, + QuorumBlobParamStruct, } from './types' import { solidityPack } from 'ethers/lib/utils' import { @@ -292,6 +301,7 @@ export const setupSequencerInbox = async ( isBlobMock = false, maxDelay: MaxTimeVariation = maxDelayDefault, delayConfig: DelayConfig = delayConfigDefault + // useEigenDA = false ) => { const accounts = await initializeAccounts() const admin = accounts[0] @@ -372,6 +382,16 @@ export const setupSequencerInbox = async ( await bridgeAdmin.setDelayedInbox(inbox.address, true) await bridgeAdmin.setSequencerInbox(sequencerInbox.address) + const l2blobVerifierFac = (await ethers.getContractFactory( + 'EigenDABlobVerifierL2' + )) as EigenDABlobVerifierL2__factory + const l2ImmutableBlobVerifier = await l2blobVerifierFac.deploy() + + await ( + await sequencerInbox + .connect(rollupOwner) + .setEigenDARollupManager(l2ImmutableBlobVerifier.address) + ).wait() await ( await sequencerInbox .connect(rollupOwner) @@ -398,3 +418,67 @@ export const setupSequencerInbox = async ( delayConfig, } } + +// Helper function to create a mock G1PointStruct +function createG1Point(x: number, y: number): G1PointStruct { + return { + X: BigNumber.from(x), + Y: BigNumber.from(y), + } +} + +// Helper function to create a mock QuorumBlobParamStruct +function createQuorumBlobParam( + quorumNumber: number, + adversaryThreshold: number, + confirmationThreshold: number, + chunkLength: number +): QuorumBlobParamStruct { + return { + quorumNumber: BigNumber.from(quorumNumber), + adversaryThresholdPercentage: BigNumber.from(adversaryThreshold), + confirmationThresholdPercentage: BigNumber.from(confirmationThreshold), + chunkLength: BigNumber.from(chunkLength), + } +} + +// Helper function to generate a V1 EigenDA certificate with +// pseudo-random data fields +export const generateEigenDACertificate = async () => { + const blobHeader: BlobHeaderStruct = { + commitment: createG1Point(12345, 67890), + dataLength: BigNumber.from(1024), + quorumBlobParams: [ + createQuorumBlobParam(1, 25, 75, 256), + createQuorumBlobParam(2, 30, 70, 512), + ], + } + + const batchHeader: BatchHeaderStruct = { + blobHeadersRoot: ethers.utils.randomBytes(32), + quorumNumbers: ethers.utils.randomBytes(32), + signedStakeForQuorums: ethers.utils.randomBytes(32), + referenceBlockNumber: BigNumber.from(98765), + } + + const batchMetadata: BatchMetadataStruct = { + batchHeader, + signatoryRecordHash: ethers.utils.randomBytes(32), + confirmationBlockNumber: BigNumber.from(54321), + } + + const blobVerificationProof: BlobVerificationProofStruct = { + batchId: BigNumber.from(1), + blobIndex: BigNumber.from(0), + batchMetadata, + inclusionProof: ethers.utils.randomBytes(32), + quorumIndices: ethers.utils.randomBytes(32), + } + + const cert: EigenDACertStruct = { + blobHeader: blobHeader, + blobVerificationProof: blobVerificationProof, + } + + return cert +} diff --git a/test/contract/types.ts b/test/contract/types.ts index 051826f..cc5dd9f 100644 --- a/test/contract/types.ts +++ b/test/contract/types.ts @@ -1,4 +1,5 @@ import { BigNumber } from '@ethersproject/bignumber' +import { Bytes } from 'ethers' export type DelayedMsgHeader = { kind: number @@ -33,3 +34,47 @@ export type DelayConfig = { max: number replenishRateInBasis: number } + +export type QuorumBlobParamStruct = { + quorumNumber: BigNumber + adversaryThresholdPercentage: BigNumber + confirmationThresholdPercentage: BigNumber + chunkLength: BigNumber +} + +export type G1PointStruct = { + X: BigNumber + Y: BigNumber +} + +export type EigenDACertStruct = { + blobVerificationProof: BlobVerificationProofStruct + blobHeader: BlobHeaderStruct +} + +export type BlobHeaderStruct = { + commitment: G1PointStruct + dataLength: BigNumber + quorumBlobParams: QuorumBlobParamStruct[] +} + +export type BatchMetadataStruct = { + batchHeader: BatchHeaderStruct + signatoryRecordHash: Bytes + confirmationBlockNumber: BigNumber +} + +export type BatchHeaderStruct = { + blobHeadersRoot: Bytes + quorumNumbers: Bytes + signedStakeForQuorums: Bytes + referenceBlockNumber: BigNumber +} + +export type BlobVerificationProofStruct = { + batchId: BigNumber + blobIndex: BigNumber + batchMetadata: BatchMetadataStruct + inclusionProof: Bytes + quorumIndices: Bytes +}