Skip to content
Open
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
11 changes: 7 additions & 4 deletions crates/primitives/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ macro_rules! define_abi {

function zoneId() external view returns (uint32);
function sequencer() external view returns (address);
function verifier() external view returns (address);
function zoneFactory() external view returns (address);
function sequencerPubkey() external view returns (bytes32);
function withdrawalBatchIndex() external view returns (uint64);
function blockHash() external view returns (bytes32);
Expand All @@ -211,6 +211,7 @@ macro_rules! define_abi {
function processWithdrawal(Withdrawal calldata withdrawal, bytes32 remainingQueue) external;

function submitBatch(
address targetVerifier,
uint64 tempoBlockNumber,
uint64 recentTempoBlockNumber,
BlockTransition calldata blockTransition,
Expand Down Expand Up @@ -398,7 +399,6 @@ macro_rules! define_abi {
address messenger;
address initialToken;
address sequencer;
address verifier;
bytes32 genesisBlockHash;
bytes32 genesisTempoBlockHash;
uint64 genesisTempoBlockNumber;
Expand All @@ -414,7 +414,6 @@ macro_rules! define_abi {
struct CreateZoneParams {
address token;
address sequencer;
address verifier;
ZoneParams zoneParams;
}
#[derive(Debug)]
Expand All @@ -424,13 +423,17 @@ macro_rules! define_abi {
address indexed messenger,
address token,
address sequencer,
address verifier,
bytes32 genesisBlockHash,
bytes32 genesisTempoBlockHash,
uint64 genesisTempoBlockNumber
);
function createZone(CreateZoneParams calldata params) external returns (uint32 zoneId, address portal);
function verifier() external view returns (address);
function forkVerifier() external view returns (address);
function forkActivationBlock() external view returns (uint64);
function protocolVersion() external view returns (uint64);
function setForkVerifier(address newForkVerifier) external;
function validateVerifier(address targetVerifier, uint64 tempoBlockNumber) external view;
function zones(uint32 zoneId) external view returns (ZoneInfo memory);
function zoneCount() external view returns (uint32);
function isZonePortal(address portal) external view returns (bool);
Expand Down
6 changes: 6 additions & 0 deletions crates/tempo-zone/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ pub struct BatchSubmitter {
genesis_tempo_block_number: u64,
/// Concurrency for pipelined L1 header fetching in ancestry mode.
l1_fetch_concurrency: usize,
/// Address of the verifier to pass as `targetVerifier` in `submitBatch`.
/// Read from `ZoneFactory.verifier()` at startup.
target_verifier: Address,
}

impl BatchSubmitter {
Expand All @@ -111,6 +114,7 @@ impl BatchSubmitter {
portal_address: Address,
l1_provider: DynProvider<TempoNetwork>,
genesis_tempo_block_number: u64,
target_verifier: Address,
) -> Self {
let portal = ZonePortal::new(portal_address, l1_provider.clone());
Self {
Expand All @@ -119,6 +123,7 @@ impl BatchSubmitter {
portal,
genesis_tempo_block_number,
l1_fetch_concurrency: 16,
target_verifier,
}
}

Expand Down Expand Up @@ -201,6 +206,7 @@ impl BatchSubmitter {
let pending = self
.portal
.submitBatch(
self.target_verifier,
batch.tempo_block_number,
recent_tempo_block_number,
block_transition,
Expand Down
28 changes: 20 additions & 8 deletions crates/tempo-zone/src/zonemonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use tracing::{error, info, instrument, warn};
use alloy_sol_types::{ContractError, SolInterface as _};

use crate::{
abi::{self, TempoState, ZoneInbox, ZoneOutbox, ZonePortal},
abi::{self, TempoState, ZoneFactory, ZoneInbox, ZoneOutbox, ZonePortal},
batch::{
AnchorGapKind, BatchData, BatchSubmitter, ZoneBlockSnapshot, fetch_slot_withdrawals,
log_query_ranges,
Expand Down Expand Up @@ -181,17 +181,29 @@ impl ZoneMonitor {
let inbox = ZoneInbox::new(config.inbox_address, provider.clone());
let tempo_state = TempoState::new(config.tempo_state_address, provider.clone());

let genesis_tempo_block_number: u64 =
ZonePortal::new(config.portal_address, l1_provider.clone())
.genesisTempoBlockNumber()
.call()
.await
.wrap_err("failed to read genesisTempoBlockNumber during zone monitor startup")?;
let portal_instance = ZonePortal::new(config.portal_address, l1_provider.clone());
let genesis_tempo_block_number: u64 = portal_instance
.genesisTempoBlockNumber()
.call()
.await
.wrap_err("failed to read genesisTempoBlockNumber during zone monitor startup")?;

let factory_address: Address = portal_instance
.zoneFactory()
.call()
.await
.wrap_err("failed to read zoneFactory during zone monitor startup")?;
let target_verifier: Address = ZoneFactory::new(factory_address, l1_provider.clone())
.verifier()
.call()
.await
.wrap_err("failed to read verifier from ZoneFactory during zone monitor startup")?;

let batch_submitter = BatchSubmitter::new(
config.portal_address,
l1_provider,
genesis_tempo_block_number,
target_verifier,
);

let (prev_zone_block_hash, portal_withdrawal_queue_tail) = tokio::try_join!(
Expand Down Expand Up @@ -1011,7 +1023,7 @@ mod tests {
inbox: ZoneInbox::new(Address::repeat_byte(0x33), zone_provider.clone()),
tempo_state: TempoState::new(Address::repeat_byte(0x44), zone_provider),
withdrawal_store: SharedWithdrawalStore::new(),
batch_submitter: BatchSubmitter::new(portal_address, l1_provider, 0),
batch_submitter: BatchSubmitter::new(portal_address, l1_provider, 0, Address::ZERO),
withdrawal_notify: Arc::new(Notify::new()),
repair_notify: Arc::new(Notify::new()),
last_submitted_zone_block: 10,
Expand Down
2 changes: 0 additions & 2 deletions crates/tempo-zone/tests/it/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,12 +978,10 @@ impl L1TestNode {
l1_header.encode(&mut rlp_buf);
let genesis_tempo_block_hash = keccak256(&rlp_buf);

let verifier_address = factory.verifier().call().await?;
let receipt = factory
.createZone(ZoneFactory::CreateZoneParams {
token: PATH_USD_ADDRESS,
sequencer,
verifier: verifier_address,
zoneParams: ZoneFactory::ZoneParams {
genesisBlockHash: B256::ZERO,
genesisTempoBlockHash: genesis_tempo_block_hash,
Expand Down
30 changes: 25 additions & 5 deletions docs/specs/src/zone/IZone.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ struct ZoneInfo {
address messenger;
address initialToken; // first TIP-20 enabled at zone creation (additional tokens enabled via enableToken)
address sequencer;
address verifier;
bytes32 genesisBlockHash;
bytes32 genesisTempoBlockHash;
uint64 genesisTempoBlockNumber;
Expand Down Expand Up @@ -420,7 +419,6 @@ interface IZoneFactory {
struct CreateZoneParams {
address initialToken; // first TIP-20 to enable (sequencer can enable more later)
address sequencer;
address verifier;
ZoneParams zoneParams;
}

Expand All @@ -430,7 +428,6 @@ interface IZoneFactory {
address indexed messenger,
address initialToken,
address sequencer,
address verifier,
bytes32 genesisBlockHash,
bytes32 genesisTempoBlockHash,
uint64 genesisTempoBlockNumber
Expand All @@ -441,14 +438,16 @@ interface IZoneFactory {
error InvalidVerifier();
error InsufficientGas();
error ZoneIdOverflow();
error UnknownVerifier();
error UseForkVerifier();

/// @notice Returns whether a verifier contract is approved for zone creation.
/// @param verifier The verifier contract address to check.
/// @return valid True if `verifier` can be passed to `createZone`.
function isValidVerifier(address verifier) external view returns (bool);

/// @notice Creates a new zone and deploys its portal and messenger contracts.
/// @param params The initial token, sequencer, verifier, and genesis parameters for the zone.
/// @param params The initial token, sequencer, and genesis parameters for the zone.
/// @return zoneId The newly assigned zone ID.
/// @return portal The deployed portal address for the new zone.
function createZone(CreateZoneParams calldata params)
Expand All @@ -474,6 +473,26 @@ interface IZoneFactory {
/// @return isMessenger True if `messenger` was created by this factory.
function isZoneMessenger(address messenger) external view returns (bool);

/// @notice Current active verifier (pre-fork or promoted from previous forkVerifier)
function verifier() external view returns (address);

/// @notice Post-fork verifier (address(0) if no fork yet)
function forkVerifier() external view returns (address);

/// @notice L1 block number at which forkVerifier was set (0 = no fork)
function forkActivationBlock() external view returns (uint64);

/// @notice Protocol version counter, incremented at each hard fork
function protocolVersion() external view returns (uint64);

/// @notice Rotate verifiers and increment protocolVersion. Called as L1 system transaction.
function setForkVerifier(address newForkVerifier) external;

/// @notice Validate that targetVerifier is recognized and allowed for the given tempoBlockNumber.
/// @dev Called by portals during submitBatch. Reverts if the verifier is unknown or if
/// the old verifier is used for a batch at or past the fork activation block.
function validateVerifier(address targetVerifier, uint64 tempoBlockNumber) external view;

}

/// @notice Per-token configuration in the portal's token registry
Expand Down Expand Up @@ -585,7 +604,7 @@ interface IZonePortal {
function sequencer() external view returns (address);
function pendingSequencer() external view returns (address);
function zoneGasRate() external view returns (uint128);
function verifier() external view returns (address);
function zoneFactory() external view returns (IZoneFactory);
function withdrawalBatchIndex() external view returns (uint64);
function blockHash() external view returns (bytes32);
function currentDepositQueueHash() external view returns (bytes32);
Expand Down Expand Up @@ -723,6 +742,7 @@ interface IZonePortal {

function processWithdrawal(Withdrawal calldata withdrawal, bytes32 remainingQueue) external;
function submitBatch(
address targetVerifier,
uint64 tempoBlockNumber,
uint64 recentTempoBlockNumber,
BlockTransition calldata blockTransition,
Expand Down
55 changes: 45 additions & 10 deletions docs/specs/src/zone/ZoneFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ contract ZoneFactory is IZoneFactory {
mapping(address => bool) internal _isZonePortal;
mapping(address => bool) internal _isZoneMessenger;
mapping(address => bool) internal _validVerifiers;
address internal _verifier;

/// @notice Current active verifier (pre-fork or promoted from previous forkVerifier)
address public verifier;

/// @notice Post-fork verifier (address(0) if no fork yet)
address public forkVerifier;

/// @notice L1 block number at which forkVerifier was set (0 = no fork)
uint64 public forkActivationBlock;

/// @notice Protocol version counter, incremented at each hard fork
uint64 public protocolVersion;

/// @notice Tracks deployment count for CREATE address prediction
/// @dev Contracts start with nonce 1, not 0. Nonce 1 is used by the Verifier deployment
Expand All @@ -41,7 +52,7 @@ contract ZoneFactory is IZoneFactory {
constructor() {
address v = address(new Verifier());
_validVerifiers[v] = true;
_verifier = v;
verifier = v;
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -55,7 +66,6 @@ contract ZoneFactory is IZoneFactory {
// Validate initial token is a TIP-20
if (!TempoUtilities.isTIP20(params.initialToken)) revert InvalidToken();
if (params.sequencer == address(0)) revert InvalidSequencer();
if (!_validVerifiers[params.verifier]) revert InvalidVerifier();
if (gasleft() < ZONE_CREATION_GAS) revert InsufficientGas();

zoneId = _nextZoneId;
Expand Down Expand Up @@ -89,7 +99,7 @@ contract ZoneFactory is IZoneFactory {
params.initialToken,
messengerAddress,
params.sequencer,
params.verifier,
address(this),
params.zoneParams.genesisBlockHash,
params.zoneParams.genesisTempoBlockNumber
);
Expand All @@ -105,7 +115,6 @@ contract ZoneFactory is IZoneFactory {
messenger: messengerAddress,
initialToken: params.initialToken,
sequencer: params.sequencer,
verifier: params.verifier,
genesisBlockHash: params.zoneParams.genesisBlockHash,
genesisTempoBlockHash: params.zoneParams.genesisTempoBlockHash,
genesisTempoBlockNumber: params.zoneParams.genesisTempoBlockNumber
Expand All @@ -120,7 +129,6 @@ contract ZoneFactory is IZoneFactory {
messengerAddress,
params.initialToken,
params.sequencer,
params.verifier,
params.zoneParams.genesisBlockHash,
params.zoneParams.genesisTempoBlockHash,
params.zoneParams.genesisTempoBlockNumber
Expand Down Expand Up @@ -155,6 +163,37 @@ contract ZoneFactory is IZoneFactory {
return address(uint160(uint256(keccak256(data))));
}

/*//////////////////////////////////////////////////////////////
VERIFIER MANAGEMENT
//////////////////////////////////////////////////////////////*/

/// @notice Rotate verifiers and increment protocolVersion.
/// @dev Called as an L1 system transaction at hard fork time. O(1) — no per-zone iteration.
/// On first fork: verifier keeps its value, forkVerifier is set.
/// On subsequent forks: previous forkVerifier promoted to verifier, new one installed.
function setForkVerifier(address newForkVerifier) external {
if (forkVerifier != address(0)) {
verifier = forkVerifier;
}
forkVerifier = newForkVerifier;
forkActivationBlock = uint64(block.number);
_validVerifiers[newForkVerifier] = true;
protocolVersion++;
}

/// @notice Validate that targetVerifier is recognized and allowed for tempoBlockNumber.
/// @dev Called by portals during submitBatch.
function validateVerifier(address targetVerifier, uint64 tempoBlockNumber) external view {
if (targetVerifier != verifier && targetVerifier != forkVerifier) {
revert UnknownVerifier();
}
if (targetVerifier == verifier && forkActivationBlock != 0) {
if (tempoBlockNumber >= forkActivationBlock) {
revert UseForkVerifier();
}
}
}

/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
Expand All @@ -180,8 +219,4 @@ contract ZoneFactory is IZoneFactory {
return _validVerifiers[v];
}

function verifier() external view returns (address) {
return _verifier;
}

}
Loading
Loading