Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "4.1.0"
".": "4.2.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -867,17 +867,17 @@ void ContentAddressedCachedTreeStore<LeafValueType>::advance_finalized_block(con
ReadTransactionPtr readTx = create_read_transaction();
get_meta(uncommittedMeta);
get_meta(committedMeta, *readTx, false);
// do nothing if the block is already finalized
if (committedMeta.finalizedBlockHeight >= blockNumber) {
return;
}
if (!dataStore_->read_block_data(blockNumber, blockPayload, *readTx)) {
throw std::runtime_error(format("Unable to advance finalized block: ",
blockNumber,
". Failed to read block data. Tree name: ",
forkConstantData_.name_));
}
}
// do nothing if the block is already finalized
if (committedMeta.finalizedBlockHeight >= blockNumber) {
return;
}

// can currently only finalize up to the unfinalized block height
if (committedMeta.finalizedBlockHeight > committedMeta.unfinalizedBlockHeight) {
Expand Down
102 changes: 99 additions & 3 deletions noir-projects/aztec-nr/aztec/src/oracle/block_header.nr
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,107 @@ fn constrain_get_block_header_at_internal(
}

mod test {
use crate::protocol::traits::Hash;
use crate::test::helpers::test_environment::TestEnvironment;
use super::{constrain_get_block_header_at_internal, get_block_header_at_internal};
use super::{constrain_get_block_header_at_internal, get_block_header_at, get_block_header_at_internal};

#[test(should_fail_with = "Proving membership of a block in archive failed")]
unconstrained fn fetching_header_with_mismatched_block_number_should_fail() {
#[test]
unconstrained fn fetching_earliest_block_header_succeeds() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor_block_header = context.anchor_block_header;

let header = get_block_header_at_internal(1);
constrain_get_block_header_at_internal(header, 1, anchor_block_header);

assert_eq(header.block_number(), 1);
});
}

#[test]
unconstrained fn fetching_past_block_header_succeeds() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor_block_header = context.anchor_block_header;
let target_block_number = anchor_block_header.block_number() - 2;

let header = get_block_header_at_internal(target_block_number);
constrain_get_block_header_at_internal(header, target_block_number, anchor_block_header);

assert_eq(header.block_number(), target_block_number);
});
}

#[test]
unconstrained fn fetching_header_immediately_before_anchor_succeeds() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

// Block N-1 is the boundary case: last_archive covers exactly up to block N-1.
env.private_context(|context| {
let anchor_block_header = context.anchor_block_header;
let target_block_number = anchor_block_header.block_number() - 1;

let header = get_block_header_at_internal(target_block_number);
constrain_get_block_header_at_internal(header, target_block_number, anchor_block_header);

assert_eq(header.block_number(), target_block_number);
});
}

#[test]
unconstrained fn fetching_anchor_block_header_works() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor_block_number = context.anchor_block_header.block_number();

let header = get_block_header_at(anchor_block_number, *context);

assert_eq(header.block_number(), anchor_block_number);
assert_eq(header.hash(), context.anchor_block_header.hash());
});
}

#[test(should_fail_with = "Last archive block number is smaller than the block number")]
unconstrained fn fetching_future_block_header_fails() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor_block_number = context.anchor_block_header.block_number();

let _header = get_block_header_at(anchor_block_number + 1, *context);
});
}

#[test(should_fail_with = "Block number provided is not the same as the block number from the header hint")]
unconstrained fn fetching_header_with_mismatched_block_number_fails() {
let env = TestEnvironment::new();

env.mine_block();
Expand Down
1 change: 1 addition & 0 deletions spartan/environments/staging-public.env
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SEQ_MAX_TX_PER_CHECKPOINT=7 # 0.1 TPS
# Build checkpoint even if block is empty.
SEQ_BUILD_CHECKPOINT_IF_EMPTY=true
SEQ_BLOCK_DURATION_MS=6000
SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT=36

CREATE_ROLLUP_CONTRACTS=false
P2P_TX_POOL_DELETE_TXS_AFTER_REORG=true
Expand Down
4 changes: 0 additions & 4 deletions spartan/scripts/deploy_network.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,7 @@ PROVER_FAILED_PROOF_STORE=${PROVER_FAILED_PROOF_STORE:-}
SEQ_MIN_TX_PER_BLOCK=${SEQ_MIN_TX_PER_BLOCK:-1}
SEQ_MAX_TX_PER_BLOCK=${SEQ_MAX_TX_PER_BLOCK:-null}
SEQ_MAX_TX_PER_CHECKPOINT=${SEQ_MAX_TX_PER_CHECKPOINT:-8}
<<<<<<< HEAD
SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER=${SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER:-2}
=======
SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER=${SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER:-}
>>>>>>> origin/v4
SEQ_BLOCK_DURATION_MS=${SEQ_BLOCK_DURATION_MS:-}
SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT=${SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT:-}
SEQ_BUILD_CHECKPOINT_IF_EMPTY=${SEQ_BUILD_CHECKPOINT_IF_EMPTY:-}
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/modules/data_store_updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ export class ArchiverDataStoreUpdater {
if (validFnCount > 0) {
this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
}
return await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns);
await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns);
}
return true;
}
Expand Down
25 changes: 24 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
referenceBlock: BlockParameter,
blockHash: BlockHash,
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
const committedDb = await this.getWorldState(referenceBlock);
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
return pathAndIndex === undefined
? undefined
Expand Down Expand Up @@ -1650,6 +1654,25 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
return snapshot;
}

/** Resolves a block parameter to a block number. */
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
if (block === 'latest') {
return BlockNumber(await this.blockSource.getBlockNumber());
}
if (BlockHash.isBlockHash(block)) {
const initialBlockHash = await this.#getInitialHeaderHash();
if (block.equals(initialBlockHash)) {
return BlockNumber.ZERO;
}
const header = await this.blockSource.getBlockHeaderByHash(block);
if (!header) {
throw new Error(`Block hash ${block.toString()} not found.`);
}
return header.getBlockNumber();
}
return block as BlockNumber;
}

/**
* Ensure we fully sync the world state
* @returns A promise that fulfils once the world state is synced
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec/src/testing/anvil_test_watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class AnvilTestWatcher {
return;
}

const l1Time = (await this.cheatcodes.timestamp()) * 1000;
const l1Time = (await this.cheatcodes.lastBlockTimestamp()) * 1000;
const wallTime = this.dateProvider.now();
if (l1Time > wallTime) {
this.logger.warn(`L1 is ahead of wall time. Syncing wall time to L1 time`);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec/src/testing/cheat_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class CheatCodes {
* @param duration - The duration to advance time by (in seconds)
*/
async warpL2TimeAtLeastBy(sequencerClient: SequencerClient, node: AztecNode, duration: bigint | number) {
const currentTimestamp = await this.eth.timestamp();
const currentTimestamp = await this.eth.lastBlockTimestamp();
const targetTimestamp = BigInt(currentTimestamp) + BigInt(duration);
await this.warpL2TimeAtLeastTo(sequencerClient, node, targetTimestamp);
}
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/end-to-end/src/e2e_cheat_codes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ describe('e2e_cheat_codes', () => {

it.each([100, 42, 99])(`setNextBlockTimestamp by %i`, async increment => {
const blockNumber = await ethCheatCodes.blockNumber();
const timestamp = await ethCheatCodes.timestamp();
const timestamp = await ethCheatCodes.lastBlockTimestamp();
await ethCheatCodes.setNextBlockTimestamp(timestamp + increment);

expect(await ethCheatCodes.timestamp()).toBe(timestamp);
expect(await ethCheatCodes.lastBlockTimestamp()).toBe(timestamp);

await ethCheatCodes.mine();

expect(await ethCheatCodes.blockNumber()).toBe(blockNumber + 1);
expect(await ethCheatCodes.timestamp()).toBe(timestamp + increment);
expect(await ethCheatCodes.lastBlockTimestamp()).toBe(timestamp + increment);
});

it('setNextBlockTimestamp to a past timestamp throws', async () => {
const timestamp = await ethCheatCodes.timestamp();
const timestamp = await ethCheatCodes.lastBlockTimestamp();
const pastTimestamp = timestamp - 1000;
await expect(async () => await ethCheatCodes.setNextBlockTimestamp(pastTimestamp)).rejects.toThrow(
'Timestamp error',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('e2e_crowdfunding_and_claim', () => {
} = await setup(3));

// We set the deadline to a week from now
deadline = (await cheatCodes.eth.timestamp()) + 7 * 24 * 60 * 60;
deadline = (await cheatCodes.eth.lastBlockTimestamp()) + 7 * 24 * 60 * 60;

({ contract: donationToken } = await TokenContract.deploy(
wallet,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ describe('e2e_p2p_add_rollup', () => {
const futureEpoch = EpochNumber.fromBigInt(500n + BigInt(await newRollup.getCurrentEpochNumber()));
const futureSlot = SlotNumber.fromBigInt(BigInt(futureEpoch) * BigInt(t.ctx.aztecNodeConfig.aztecEpochDuration));
const time = await newRollup.getTimestampForSlot(futureSlot);
if (time > BigInt(await t.ctx.cheatCodes.eth.timestamp())) {
if (time > BigInt(await t.ctx.cheatCodes.eth.lastBlockTimestamp())) {
await t.ctx.cheatCodes.eth.warp(Number(time));
await waitL1Block();
}
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_synching.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ describe('e2e_synching', () => {
for (const checkpoint of checkpoints) {
const lastBlock = checkpoint.blocks.at(-1)!;
const targetTime = Number(lastBlock.header.globalVariables.timestamp) - ETHEREUM_SLOT_DURATION;
while ((await cheatCodes.eth.timestamp()) < targetTime) {
while ((await cheatCodes.eth.lastBlockTimestamp()) < targetTime) {
await cheatCodes.eth.mine();
}
// If it breaks here, first place you should look is the pruning.
Expand Down
16 changes: 9 additions & 7 deletions yarn-project/end-to-end/src/fixtures/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ export async function setup(
config.dataDirectory = directoryToCleanup;
}

const dateProvider = new TestDateProvider();

if (!config.l1RpcUrls?.length) {
if (!isAnvilTestChain(chain.id)) {
throw new Error(`No ETHEREUM_HOSTS set but non anvil chain requested`);
Expand All @@ -306,6 +308,7 @@ export async function setup(
l1BlockTime: opts.ethereumSlotDuration,
accounts: opts.anvilAccounts,
port: opts.anvilPort ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : undefined),
dateProvider,
});
anvil = res.anvil;
config.l1RpcUrls = [res.rpcUrl];
Expand All @@ -317,8 +320,6 @@ export async function setup(
logger.info(`Logging metrics to ${filename}`);
setupMetricsLogger(filename);
}

const dateProvider = new TestDateProvider();
const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls, dateProvider);

if (opts.stateLoad) {
Expand Down Expand Up @@ -414,11 +415,12 @@ export async function setup(
await ethCheatCodes.setIntervalMining(config.ethereumSlotDuration);
}

// Always sync dateProvider to L1 time after deploying L1 contracts, regardless of mining mode.
// In compose mode, L1 time may have drifted ahead of system time due to the local-network watcher
// warping time forward on each filled slot. Without this sync, the sequencer computes the wrong
// slot from its dateProvider and cannot propose blocks.
dateProvider.setTime((await ethCheatCodes.timestamp()) * 1000);
// In compose mode (no local anvil), sync dateProvider to L1 time since it may have drifted
// ahead of system time due to the local-network watcher warping time forward on each filled slot.
// When running with a local anvil, the dateProvider is kept in sync via the stdout listener.
if (!anvil) {
dateProvider.setTime((await ethCheatCodes.lastBlockTimestamp()) * 1000);
}

if (opts.l2StartTime) {
await ethCheatCodes.warp(opts.l2StartTime, { resetBlockInterval: true });
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/end-to-end/src/simulators/lending_simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ export class LendingSimulator {

async prepare() {
this.accumulator = BASE;
const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.timestamp()) + BigInt(this.ethereumSlotDuration));
const slot = await this.rollup.getSlotAt(
BigInt(await this.cc.eth.lastBlockTimestamp()) + BigInt(this.ethereumSlotDuration),
);
this.time = Number(await this.rollup.getTimestampForSlot(slot));
}

Expand All @@ -103,7 +105,7 @@ export class LendingSimulator {
return;
}

const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.timestamp()));
const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.lastBlockTimestamp()));
const targetSlot = SlotNumber(slot + diff);
const ts = Number(await this.rollup.getTimestampForSlot(targetSlot));
const timeDiff = ts - this.time;
Expand Down
10 changes: 6 additions & 4 deletions yarn-project/ethereum/src/test/eth_cheat_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ export class EthCheatCodes {
}

/**
* Get the current timestamp
* @returns The current timestamp
* Get the timestamp of the latest mined L1 block.
* Note: this is NOT the current time — it's the discrete timestamp of the last block.
* Between blocks, the actual chain time advances but no new block reflects it.
* @returns The latest block timestamp in seconds
*/
public async timestamp(): Promise<number> {
public async lastBlockTimestamp(): Promise<number> {
const res = await this.doRpcCall('eth_getBlockByNumber', ['latest', true]);
return parseInt(res.timestamp, 16);
}
Expand Down Expand Up @@ -552,7 +554,7 @@ export class EthCheatCodes {
}

public async syncDateProvider() {
const timestamp = await this.timestamp();
const timestamp = await this.lastBlockTimestamp();
if ('setTime' in this.dateProvider) {
this.dateProvider.setTime(timestamp * 1000);
}
Expand Down
Loading
Loading