Skip to content

Commit

Permalink
fix: Allow unwinding multiple empty blocks
Browse files Browse the repository at this point in the history
The archiver kv store used the block **body** hash as identifier to
store the block bodies. This means that two empty blocks would have the
same identifier. So, when unwinding (ie deleting) two or more empty
blocks, the first one would remove the body that corresponds to an empty
block, and the second one would fail with "Body could not be retrieved".

This fixes the issue by using the block hash, guaranteed to be unique,
as a key to store the block bodies.
  • Loading branch information
spalladino committed Nov 20, 2024
1 parent 4129e27 commit 3a4edd7
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
29 changes: 25 additions & 4 deletions yarn-project/archiver/src/archiver/archiver_store_test_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
[5, 2, () => blocks.slice(4, 6)],
];

const makeL1Published = (block: L2Block, l1BlockNumber: number): L1Published<L2Block> => ({
data: block,
l1: {
blockNumber: BigInt(l1BlockNumber),
blockHash: `0x${l1BlockNumber}`,
timestamp: BigInt(l1BlockNumber * 1000),
},
});

beforeEach(() => {
store = getStore();
blocks = times(10, i => ({
data: L2Block.random(i + 1),
l1: { blockNumber: BigInt(i + 10), blockHash: `0x${i}`, timestamp: BigInt(i * 1000) },
}));
blocks = times(10, i => makeL1Published(L2Block.random(i + 1), i + 10));
});

describe('addBlocks', () => {
Expand All @@ -69,6 +75,21 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
expect(await store.getBlocks(blockNumber, 1)).toEqual([]);
});

it('can unwind multiple empty blocks', async () => {
const emptyBlocks = times(10, i => makeL1Published(L2Block.random(i + 1, 0), i + 10));
await store.addBlocks(emptyBlocks);
expect(await store.getSynchedL2BlockNumber()).toBe(10);

await store.unwindBlocks(10, 3);
expect(await store.getSynchedL2BlockNumber()).toBe(7);
expect((await store.getBlocks(1, 10)).map(b => b.data.number)).toEqual([1, 2, 3, 4, 5, 6, 7]);
});

it('refuses to unwind blocks if the tip is not the last block', async () => {
await store.addBlocks(blocks);
await expect(store.unwindBlocks(5, 1)).rejects.toThrow(/can only unwind blocks from the tip/i);
});
});

describe('getBlocks', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class BlockStore {
/** Map block number to block data */
#blocks: AztecMap<number, BlockStorage>;

/** Map block body hash to block body */
/** Map block hash to block body */
#blockBodies: AztecMap<string, Buffer>;

/** Stores L1 block number in which the last processed L2 block was included */
Expand Down Expand Up @@ -72,7 +72,7 @@ export class BlockStore {
void this.#txIndex.set(tx.txHash.toString(), [block.data.number, i]);
});

void this.#blockBodies.set(block.data.body.getTxsEffectsHash().toString('hex'), block.data.body.toBuffer());
void this.#blockBodies.set(block.data.hash().toString(), block.data.body.toBuffer());
}

void this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
Expand All @@ -92,7 +92,7 @@ export class BlockStore {
return this.db.transaction(() => {
const last = this.getSynchedL2BlockNumber();
if (from != last) {
throw new Error(`Can only remove from the tip`);
throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
}

for (let i = 0; i < blocksToUnwind; i++) {
Expand All @@ -106,7 +106,9 @@ export class BlockStore {
block.data.body.txEffects.forEach(tx => {
void this.#txIndex.delete(tx.txHash.toString());
});
void this.#blockBodies.delete(block.data.body.getTxsEffectsHash().toString('hex'));
const blockHash = block.data.hash().toString();
void this.#blockBodies.delete(blockHash);
this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`);
}

return true;
Expand Down Expand Up @@ -154,10 +156,12 @@ export class BlockStore {
private getBlockFromBlockStorage(blockStorage: BlockStorage) {
const header = Header.fromBuffer(blockStorage.header);
const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);

const blockBodyBuffer = this.#blockBodies.get(header.contentCommitment.txsEffectsHash.toString('hex'));
const blockHash = header.hash().toString();
const blockBodyBuffer = this.#blockBodies.get(blockHash);
if (blockBodyBuffer === undefined) {
throw new Error('Body could not be retrieved');
throw new Error(
`Could not retrieve body for block ${header.globalVariables.blockNumber.toNumber()} ${blockHash}`,
);
}
const body = Body.fromBuffer(blockBodyBuffer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class MemoryArchiverStore implements ArchiverDataStore {
public async unwindBlocks(from: number, blocksToUnwind: number): Promise<boolean> {
const last = await this.getSynchedL2BlockNumber();
if (from != last) {
throw new Error(`Can only remove the tip`);
throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
}

const stopAt = from - blocksToUnwind;
Expand Down

0 comments on commit 3a4edd7

Please sign in to comment.