diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 87de3e243101..7c6611645063 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -585,19 +585,21 @@ contract Rollup is Leonidas, IRollup, ITestRollup { revert Errors.Rollup__InvalidArchive(tipArchive, _header.lastArchive.root); } - uint256 slot = _header.globalVariables.slotNumber; - if (slot > type(uint128).max) { - revert Errors.Rollup__SlotValueTooLarge(slot); - } + if (!isDevNet) { + uint256 slot = _header.globalVariables.slotNumber; + if (slot > type(uint128).max) { + revert Errors.Rollup__SlotValueTooLarge(slot); + } - uint256 lastSlot = uint256(blocks[pendingBlockCount - 1].slotNumber); - if (slot <= lastSlot) { - revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot); - } + uint256 lastSlot = uint256(blocks[pendingBlockCount - 1].slotNumber); + if (slot <= lastSlot) { + revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot); + } - uint256 timestamp = getTimestampForSlot(slot); - if (_header.globalVariables.timestamp != timestamp) { - revert Errors.Rollup__InvalidTimestamp(timestamp, _header.globalVariables.timestamp); + uint256 timestamp = getTimestampForSlot(slot); + if (_header.globalVariables.timestamp != timestamp) { + revert Errors.Rollup__InvalidTimestamp(timestamp, _header.globalVariables.timestamp); + } } // Check if the data is available using availability oracle (change availability oracle if you want a different DA layer) diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index a9205542fbc2..9dbb457114bf 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -296,7 +296,11 @@ contract RollupTest is DecoderBase { availabilityOracle.publish(body); - vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidTimestamp.selector, realTs, badTs)); + if (Constants.IS_DEV_NET != 1) { + vm.expectRevert( + abi.encodeWithSelector(Errors.Rollup__InvalidTimestamp.selector, realTs, badTs) + ); + } rollup.process(header, archive, bytes32(0)); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index fd53a10894e0..dfe50773692f 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -57,7 +57,13 @@ import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice'; import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer'; import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry'; import { getCanonicalMultiCallEntrypointAddress } from '@aztec/protocol-contracts/multi-call-entrypoint'; -import { AggregateTxValidator, DataTxValidator, GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client'; +import { + AggregateTxValidator, + DataTxValidator, + type GlobalVariableBuilder, + SequencerClient, + createGlobalVariableBuilder, +} from '@aztec/sequencer-client'; import { PublicProcessorFactory, WASMSimulator, createSimulationProvider } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; @@ -195,7 +201,7 @@ export class AztecNodeService implements AztecNode { sequencer, ethereumChain.chainInfo.id, config.version, - new GlobalVariableBuilder(config), + createGlobalVariableBuilder(config), store, txValidator, telemetry, diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index c50bae061427..4f00c6bfe793 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -3,6 +3,7 @@ import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec import { SignerlessWallet } from '@aztec/aztec.js'; import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { type AztecNode } from '@aztec/circuit-types'; +import { IS_DEV_NET } from '@aztec/circuits.js'; import { deployCanonicalAuthRegistry, deployCanonicalKeyRegistry, deployCanonicalL2FeeJuice } from '@aztec/cli/misc'; import { type DeployL1Contracts, @@ -133,6 +134,7 @@ export async function deployContractsToL1( vkTreeRoot: getVKTreeRoot(), assumeProvenUntil: opts.assumeProvenUntilBlockNumber, salt: undefined, + blockInterval: IS_DEV_NET ? undefined : 12, }), ); diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index ec413f2689d3..a572cf6a3b65 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -1,6 +1,7 @@ import { type ContractArtifact, type FunctionArtifact, loadContractArtifact } from '@aztec/aztec.js/abi'; import { type L1ContractArtifactsForDeployment } from '@aztec/aztec.js/ethereum'; import { type PXE } from '@aztec/circuit-types'; +import { IS_DEV_NET } from '@aztec/circuits.js'; import { type DeployL1Contracts } from '@aztec/ethereum'; import { type EthAddress } from '@aztec/foundation/eth-address'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -118,6 +119,7 @@ export async function deployAztecContracts( l2FeeJuiceAddress: FeeJuiceAddress, vkTreeRoot: getVKTreeRoot(), salt, + blockInterval: IS_DEV_NET ? undefined : 12, }); } diff --git a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts index 4cc48f528fa4..2a0bcea67876 100644 --- a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts +++ b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts @@ -1,4 +1,5 @@ import { type DebugLogger, type L1ContractArtifactsForDeployment, deployL1Contracts } from '@aztec/aztec.js'; +import { IS_DEV_NET } from '@aztec/circuits.js'; import { AvailabilityOracleAbi, AvailabilityOracleBytecode, @@ -63,6 +64,7 @@ export const setupL1Contracts = async ( l2FeeJuiceAddress: FeeJuiceAddress, vkTreeRoot: getVKTreeRoot(), salt: undefined, + blockInterval: IS_DEV_NET ? undefined : 12, }); return l1Data; diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index a065b2037b1c..9ba27259634e 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -33,6 +33,7 @@ import { CANONICAL_AUTH_REGISTRY_ADDRESS, CANONICAL_KEY_REGISTRY_ADDRESS, GasSettings, + IS_DEV_NET, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, computeContractAddressFromInstance, getContractClassFromArtifact, @@ -151,6 +152,7 @@ export const setupL1Contracts = async ( l2FeeJuiceAddress: FeeJuiceAddress, vkTreeRoot: getVKTreeRoot(), salt: args.salt, + blockInterval: IS_DEV_NET ? undefined : 12, }); return l1Data; diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index bf4d2b004479..701783583df5 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -147,7 +147,13 @@ export const deployL1Contracts = async ( chain: Chain, logger: DebugLogger, contractsToDeploy: L1ContractArtifactsForDeployment, - args: { l2FeeJuiceAddress: AztecAddress; vkTreeRoot: Fr; assumeProvenUntil?: number; salt: number | undefined }, + args: { + blockInterval: number | undefined; + l2FeeJuiceAddress: AztecAddress; + vkTreeRoot: Fr; + assumeProvenUntil?: number; + salt: number | undefined; + }, ): Promise => { // We are assuming that you are running this on a local anvil node which have 1s block times // To align better with actual deployment, we update the block interval to 12s @@ -161,13 +167,13 @@ export const deployL1Contracts = async ( }; return await (await fetch(rpcUrl, content)).json(); }; - if (chain.id == foundry.id) { - const interval = 12; - const res = await rpcCall(rpcUrl, 'anvil_setBlockTimestampInterval', [interval]); + + if (chain.id == foundry.id && args.blockInterval !== undefined) { + const res = await rpcCall(rpcUrl, 'anvil_setBlockTimestampInterval', [args.blockInterval]); if (res.error) { throw new Error(`Error setting block interval: ${res.error.message}`); } - logger.info(`Set block interval to ${interval}`); + logger.info(`Set block interval to ${args.blockInterval}`); } logger.info(`Deploying contracts from ${account.address.toString()}...`); diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 02e173299815..65172f8e3c4c 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -8,7 +8,7 @@ import { type WorldStateSynchronizer } from '@aztec/world-state'; import { BlockBuilderFactory } from '../block_builder/index.js'; import { type SequencerClientConfig } from '../config.js'; -import { GlobalVariableBuilder } from '../global_variable_builder/index.js'; +import { createGlobalVariableBuilder } from '../global_variable_builder/index.js'; import { L1Publisher } from '../publisher/index.js'; import { Sequencer, type SequencerConfig } from '../sequencer/index.js'; import { TxValidatorFactory } from '../tx_validator/tx_validator_factory.js'; @@ -44,7 +44,7 @@ export class SequencerClient { telemetryClient: TelemetryClient, ) { const publisher = new L1Publisher(config, telemetryClient); - const globalsBuilder = new GlobalVariableBuilder(config); + const globalsBuilder = createGlobalVariableBuilder(config); const merkleTreeDb = worldStateSynchronizer.getLatest(); const publicProcessorFactory = new PublicProcessorFactory( diff --git a/yarn-project/sequencer-client/src/global_variable_builder/devnet_global_builder.ts b/yarn-project/sequencer-client/src/global_variable_builder/devnet_global_builder.ts new file mode 100644 index 000000000000..1e0294848364 --- /dev/null +++ b/yarn-project/sequencer-client/src/global_variable_builder/devnet_global_builder.ts @@ -0,0 +1,75 @@ +import { type AztecAddress, type EthAddress, GasFees, GlobalVariables } from '@aztec/circuits.js'; +import { type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum'; +import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { RollupAbi } from '@aztec/l1-artifacts'; + +import { + type GetContractReturnType, + type HttpTransport, + type PublicClient, + createPublicClient, + getAddress, + getContract, + http, +} from 'viem'; +import type * as chains from 'viem/chains'; + +/** + * Simple global variables builder for devnet. Uses current timestamp and slots equal to block numbers. + */ +export class DevnetGlobalVariableBuilder { + private log = createDebugLogger('aztec:sequencer:devnet_global_variable_builder'); + + private rollupContract: GetContractReturnType>; + private publicClient: PublicClient; + + constructor(config: L1ReaderConfig) { + const { l1RpcUrl, l1ChainId: chainId, l1Contracts } = config; + + const chain = createEthereumChain(l1RpcUrl, chainId); + + this.publicClient = createPublicClient({ + chain: chain.chainInfo, + transport: http(chain.rpcUrl), + }); + + this.rollupContract = getContract({ + address: getAddress(l1Contracts.rollupAddress.toString()), + abi: RollupAbi, + client: this.publicClient, + }); + } + + /** + * Simple builder of global variables that use the minimum time possible. + * @param blockNumber - The block number to build global variables for. + * @param coinbase - The address to receive block reward. + * @param feeRecipient - The address to receive fees. + * @returns The global variables for the given block number. + */ + public async buildGlobalVariables( + blockNumber: Fr, + coinbase: EthAddress, + feeRecipient: AztecAddress, + ): Promise { + const version = new Fr(await this.rollupContract.read.VERSION()); + const chainId = new Fr(this.publicClient.chain.id); + const timestamp = new Fr(Math.floor(Date.now() / 1000)); + const slot = blockNumber; + + const gasFees = GasFees.default(); + const globalVariables = new GlobalVariables( + chainId, + version, + blockNumber, + slot, + timestamp, + coinbase, + feeRecipient, + gasFees, + ); + this.log.debug(`Built global variables for block ${blockNumber}`, globalVariables.toJSON()); + return globalVariables; + } +} diff --git a/yarn-project/sequencer-client/src/global_variable_builder/index.ts b/yarn-project/sequencer-client/src/global_variable_builder/index.ts index 5669a0412ae4..ae6d8ea1439f 100644 --- a/yarn-project/sequencer-client/src/global_variable_builder/index.ts +++ b/yarn-project/sequencer-client/src/global_variable_builder/index.ts @@ -1 +1,19 @@ -export { GlobalVariableBuilder } from './global_builder.js'; +import { type AztecAddress, type EthAddress, type GlobalVariables } from '@aztec/circuits.js'; +import { IS_DEV_NET } from '@aztec/circuits.js'; +import { type L1ReaderConfig } from '@aztec/ethereum'; +import { type Fr } from '@aztec/foundation/fields'; + +import { DevnetGlobalVariableBuilder } from './devnet_global_builder.js'; +import { SimpleGlobalVariableBuilder } from './simple_global_builder.js'; + +export { DevnetGlobalVariableBuilder } from './devnet_global_builder.js'; +export { SimpleGlobalVariableBuilder } from './simple_global_builder.js'; + +export function createGlobalVariableBuilder(config: L1ReaderConfig): GlobalVariableBuilder { + return IS_DEV_NET ? new DevnetGlobalVariableBuilder(config) : new SimpleGlobalVariableBuilder(config); +} + +/** Builds global variables for a block. */ +export interface GlobalVariableBuilder { + buildGlobalVariables(blockNumber: Fr, coinbase: EthAddress, feeRecipient: AztecAddress): Promise; +} diff --git a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts b/yarn-project/sequencer-client/src/global_variable_builder/simple_global_builder.ts similarity index 98% rename from yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts rename to yarn-project/sequencer-client/src/global_variable_builder/simple_global_builder.ts index f37db0af1cd8..c189a93932ac 100644 --- a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts +++ b/yarn-project/sequencer-client/src/global_variable_builder/simple_global_builder.ts @@ -24,7 +24,7 @@ import type * as chains from 'viem/chains'; /** * Simple global variables builder. */ -export class GlobalVariableBuilder { +export class SimpleGlobalVariableBuilder { private log = createDebugLogger('aztec:sequencer:global_variable_builder'); private rollupContract: GetContractReturnType>; diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 9f4f79046fa2..3f6ff1a99be4 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -35,7 +35,7 @@ import { type MerkleTreeOperations, WorldStateRunningState, type WorldStateSynch import { type MockProxy, mock, mockFn } from 'jest-mock-extended'; import { type BlockBuilderFactory } from '../block_builder/index.js'; -import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js'; +import { type GlobalVariableBuilder } from '../global_variable_builder/index.js'; import { type L1Publisher } from '../publisher/l1-publisher.js'; import { TxValidatorFactory } from '../tx_validator/tx_validator_factory.js'; import { Sequencer } from './sequencer.js'; diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 1fe8fa5f52c1..557370614910 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -22,7 +22,7 @@ import { type ValidatorClient } from '@aztec/validator-client'; import { type WorldStateStatus, type WorldStateSynchronizer } from '@aztec/world-state'; import { type BlockBuilderFactory } from '../block_builder/index.js'; -import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js'; +import { type GlobalVariableBuilder } from '../global_variable_builder/index.js'; import { type L1Publisher } from '../publisher/l1-publisher.js'; import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js'; import { type SequencerConfig } from './config.js';