Skip to content

Commit a7000eb

Browse files
authored
fix(node): handle slot zero in getL2ToL1Messages (#21386)
If there was a block in L2 slot zero, then `getL2ToL1Messages` returned an incorrect response, since the `slotNumber !== previousSlotNumber` would fail in the first iteration of the loop.
1 parent 7f523b3 commit a7000eb

2 files changed

Lines changed: 58 additions & 16 deletions

File tree

yarn-project/aztec-node/src/aztec-node/server.test.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TestCircuitVerifier } from '@aztec/bb-prover';
22
import { EpochCache } from '@aztec/epoch-cache';
33
import type { RollupContract } from '@aztec/ethereum/contracts';
4-
import { BlockNumber } from '@aztec/foundation/branded-types';
4+
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
55
import { Fr } from '@aztec/foundation/curves/bn254';
66
import { EthAddress } from '@aztec/foundation/eth-address';
77
import { BadRequestError } from '@aztec/foundation/json-rpc';
@@ -16,7 +16,8 @@ import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-ju
1616
import type { GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client';
1717
import type { SlasherClientInterface } from '@aztec/slasher';
1818
import { AztecAddress } from '@aztec/stdlib/aztec-address';
19-
import { BlockHash, L2Block, type L2BlockSource } from '@aztec/stdlib/block';
19+
import { BlockHash, CheckpointedL2Block, L2Block, type L2BlockSource } from '@aztec/stdlib/block';
20+
import { L1PublishedData } from '@aztec/stdlib/checkpoint';
2021
import type { ContractDataSource } from '@aztec/stdlib/contract';
2122
import { EmptyL1RollupConstants } from '@aztec/stdlib/epoch-helpers';
2223
import { GasFees } from '@aztec/stdlib/gas';
@@ -35,6 +36,7 @@ import {
3536
TX_ERROR_INVALID_EXPIRATION_TIMESTAMP,
3637
TX_ERROR_SIZE_ABOVE_LIMIT,
3738
Tx,
39+
TxEffect,
3840
} from '@aztec/stdlib/tx';
3941
import { getPackageVersion } from '@aztec/stdlib/update-checker';
4042
import type { ValidatorClient } from '@aztec/validator-client';
@@ -810,4 +812,54 @@ describe('aztec node', () => {
810812
});
811813
});
812814
});
815+
816+
describe('getL2ToL1Messages', () => {
817+
const makeCheckpointedBlock = (slotNumber: number, l2ToL1MsgsByTx: Fr[][]): CheckpointedL2Block => {
818+
const block = L2Block.empty(
819+
BlockHeader.empty({
820+
globalVariables: GlobalVariables.empty({ slotNumber: SlotNumber(slotNumber) }),
821+
}),
822+
);
823+
// Override the body's txEffects with our custom l2ToL1Msgs
824+
unfreeze(block.body).txEffects = l2ToL1MsgsByTx.map(msgs => ({ l2ToL1Msgs: msgs }) as TxEffect);
825+
return new CheckpointedL2Block(CheckpointNumber(0), block, new L1PublishedData(0n, 0n, '0x0'), []);
826+
};
827+
828+
it('groups blocks by slot number into checkpoints', async () => {
829+
const msg1 = Fr.random();
830+
const msg2 = Fr.random();
831+
const msg3 = Fr.random();
832+
833+
// Two blocks in slot 1, one block in slot 2
834+
const blocks = [
835+
makeCheckpointedBlock(1, [[msg1]]),
836+
makeCheckpointedBlock(1, [[msg2]]),
837+
makeCheckpointedBlock(2, [[msg3]]),
838+
];
839+
840+
l2BlockSource.getCheckpointedBlocksForEpoch.mockResolvedValue(blocks);
841+
842+
const result = await node.getL2ToL1Messages(EpochNumber(0));
843+
844+
// First checkpoint (slot 1): 2 blocks, each with 1 tx with 1 message
845+
// Second checkpoint (slot 2): 1 block with 1 tx with 1 message
846+
expect(result).toEqual([[[[msg1]], [[msg2]]], [[[msg3]]]]);
847+
});
848+
849+
it('correctly includes blocks in slot zero', async () => {
850+
const msg1 = Fr.random();
851+
const msg2 = Fr.random();
852+
853+
// Block in slot 0, block in slot 1
854+
const blocks = [makeCheckpointedBlock(0, [[msg1]]), makeCheckpointedBlock(1, [[msg2]])];
855+
856+
l2BlockSource.getCheckpointedBlocksForEpoch.mockResolvedValue(blocks);
857+
858+
const result = await node.getL2ToL1Messages(EpochNumber(0));
859+
860+
// First checkpoint (slot 0): 1 block with 1 tx with 1 message
861+
// Second checkpoint (slot 1): 1 block with 1 tx with 1 message
862+
expect(result).toEqual([[[[msg1]]], [[[msg2]]]]);
863+
});
864+
});
813865
});

yarn-project/aztec-node/src/aztec-node/server.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
99
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
1010
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
1111
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
12-
import { compactArray, pick, unique } from '@aztec/foundation/collection';
12+
import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
1313
import { Fr } from '@aztec/foundation/curves/bn254';
1414
import { EthAddress } from '@aztec/foundation/eth-address';
1515
import { BadRequestError } from '@aztec/foundation/json-rpc';
@@ -1100,19 +1100,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
11001100
public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
11011101
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
11021102
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
1103-
const blocksInCheckpoints: L2Block[][] = [];
1104-
let previousSlotNumber = SlotNumber.ZERO;
1105-
let checkpointIndex = -1;
1106-
for (const checkpointedBlock of checkpointedBlocks) {
1107-
const block = checkpointedBlock.block;
1108-
const slotNumber = block.header.globalVariables.slotNumber;
1109-
if (slotNumber !== previousSlotNumber) {
1110-
checkpointIndex++;
1111-
blocksInCheckpoints.push([]);
1112-
previousSlotNumber = slotNumber;
1113-
}
1114-
blocksInCheckpoints[checkpointIndex].push(block);
1115-
}
1103+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, cb => cb.block.header.globalVariables.slotNumber).map(
1104+
group => group.map(cb => cb.block),
1105+
);
11161106
return blocksInCheckpoints.map(blocks =>
11171107
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
11181108
);

0 commit comments

Comments
 (0)