Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 25 additions & 22 deletions barretenberg/cpp/src/barretenberg/world_state/world_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,16 +587,6 @@ WorldStateStatusFull WorldState::sync_block(const StateReference& block_state_re
const std::vector<crypto::merkle_tree::PublicDataLeafValue>& public_writes)
{
validate_trees_are_equally_synched();
WorldStateStatusFull status;
if (is_same_state_reference(WorldStateRevision::uncommitted(), block_state_ref) &&
is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
std::pair<bool, std::string> result = commit(status);
if (!result.first) {
throw std::runtime_error(result.second);
}
populate_status_summary(status);
return status;
}
rollback();

Fork::SharedPtr fork = retrieve_fork(CANONICAL_FORK_ID);
Expand Down Expand Up @@ -658,22 +648,32 @@ WorldStateStatusFull WorldState::sync_block(const StateReference& block_state_re

signal.wait_for_level();

if (!success) {
throw std::runtime_error("Failed to sync block: " + err_message);
}
// Check resulting state and commit if successful
WorldStateStatusFull status;
try {
if (!success) {
throw std::runtime_error("Failed to sync block: " + err_message);
}

if (!is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
throw std::runtime_error("Can't synch block: block header hash is not the tip of the archive tree");
}
if (!is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
throw std::runtime_error("Can't synch block: block header hash is not the tip of the archive tree");
}

if (!is_same_state_reference(WorldStateRevision::uncommitted(), block_state_ref)) {
throw std::runtime_error("Can't synch block: block state does not match world state");
}
if (!is_same_state_reference(WorldStateRevision::uncommitted(), block_state_ref)) {
throw std::runtime_error("Can't synch block: block state does not match world state");
}

std::pair<bool, std::string> result = commit(status);
if (!result.first) {
throw std::runtime_error(result.second);
std::pair<bool, std::string> result = commit(status);
if (!result.first) {
throw std::runtime_error(result.second);
}
} catch (const std::exception& e) {
// We failed, rollback any uncommitted state before leaving
rollback();
throw;
}

// Success return the status
populate_status_summary(status);
return status;
}
Expand Down Expand Up @@ -726,6 +726,9 @@ WorldStateStatusSummary WorldState::set_finalized_blocks(const block_number_t& t
}
WorldStateStatusFull WorldState::unwind_blocks(const block_number_t& toBlockNumber)
{
// Ensure no uncommitted state
rollback();

WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
std::array<TreeMeta, NUM_TREES> responses;
get_all_tree_info(revision, responses);
Expand Down
76 changes: 75 additions & 1 deletion yarn-project/world-state/src/native/native_world_state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import type { SiblingPath } from '@aztec/foundation/trees';
import { PublicDataWrite } from '@aztec/stdlib/avm';
import type { L2Block } from '@aztec/stdlib/block';
import { L2Block } from '@aztec/stdlib/block';
import { DatabaseVersion, DatabaseVersionManager } from '@aztec/stdlib/database-version';
import type { MerkleTreeLeafType, MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
import { makeGlobalVariables } from '@aztec/stdlib/testing';
Expand Down Expand Up @@ -832,6 +832,80 @@ describe('NativeWorldState', () => {
});
});

describe('Invalid Blocks', () => {
let ws: NativeWorldStateService;
let rollupAddress!: EthAddress;

beforeEach(async () => {
rollupAddress = EthAddress.random();
ws = await NativeWorldStateService.new(rollupAddress, dataDir, wsTreeMapSizes);
});

afterEach(async () => {
await ws.close();
});

it('handles invalid blocks', async () => {
const fork = await ws.fork();

// Insert a few blocks
for (let i = 0; i < 4; i++) {
const blockNumber = i + 1;
const provenBlock = blockNumber - 2;
const { block, messages } = await mockBlock(blockNumber, 1, fork);
const status = await ws.handleL2BlockAndMessages(block, messages);

expect(status.summary.unfinalizedBlockNumber).toBe(BigInt(blockNumber));
expect(status.summary.oldestHistoricalBlock).toBe(1n);

if (provenBlock > 0) {
const provenStatus = await ws.setFinalized(BigInt(provenBlock));
expect(provenStatus.unfinalizedBlockNumber).toBe(BigInt(blockNumber));
expect(provenStatus.finalizedBlockNumber).toBe(BigInt(provenBlock));
expect(provenStatus.oldestHistoricalBlock).toBe(1n);
} else {
expect(status.summary.finalizedBlockNumber).toBe(0n);
}
}

// Now build an invalid block, see that it is rejected and that we can then insert the correct block
{
const { block: block, messages } = await mockBlock(5, 1, fork);
const invalidBlock = L2Block.fromBuffer(block.toBuffer());
invalidBlock.header.state.partial.nullifierTree.root = Fr.random();

await expect(ws.handleL2BlockAndMessages(invalidBlock, messages)).rejects.toThrow(
"Can't synch block: block state does not match world state",
);

// Accepts the correct block
await expect(ws.handleL2BlockAndMessages(block, messages)).resolves.toBeDefined();

const summary = await ws.getStatusSummary();
expect(summary.unfinalizedBlockNumber).toBe(5n);
expect(summary.finalizedBlockNumber).toBe(2n);
expect(summary.oldestHistoricalBlock).toBe(1n);
}

// Now we push another invalid block, see that it is rejected and check we can unwind to the last proven block
{
const { block: block, messages } = await mockBlock(6, 1, fork);
const invalidBlock = L2Block.fromBuffer(block.toBuffer());
invalidBlock.header.state.partial.nullifierTree.root = Fr.random();

await expect(ws.handleL2BlockAndMessages(invalidBlock, messages)).rejects.toThrow(
"Can't synch block: block state does not match world state",
);

// Now we want to unwind to the last proven block
const unwindStatus = await ws.unwindBlocks(2n);
expect(unwindStatus.summary.unfinalizedBlockNumber).toBe(2n);
expect(unwindStatus.summary.finalizedBlockNumber).toBe(2n);
expect(unwindStatus.summary.oldestHistoricalBlock).toBe(1n);
}
});
});

describe('Finding leaves', () => {
let block: L2Block;
let messages: Fr[];
Expand Down
Loading