Skip to content

Commit c585ba8

Browse files
decofedanrobinson
andauthored
docs(specs): fix zone spec inconsistencies with implementation (#380)
- Zone Predeploys: 4→6 (add TempoStateReader at 0x...0004, ZoneTxContext at 0x...0005) - Zone Token Model: document TIP-20 factory precompile at 0x20Fc... - Withdrawal fee: gasLimit*tempoGasRate → (WITHDRAWAL_BASE_GAS + gasLimit)*tempoGasRate - advanceTempo signature: add enabledTokens (4th param) - finalizeWithdrawalBatch signature: add blockNumber, encryptedSenders (3 params) - Block timestamp: 'monotonically increasing' → 'non-decreasing' - readTempoStorageSlot: separate allowed callers from current callers, clarify TIP-403 proxy - encryptedSender wire format: add missing nonce (12 bytes), total 113 bytes - IZoneInbox/IZoneOutbox interfaces: match implementation signatures - Network Upgrades: note verifier rotation is aspirational (verifier is immutable) Amp-Thread-ID: https://ampcode.com/threads/T-019d87a9-817b-7403-899e-86fdeca109f1 Co-authored-by: Daniel Robinson <[email protected]>
1 parent 1df1b41 commit c585ba8

1 file changed

Lines changed: 34 additions & 19 deletions

File tree

docs/specs/zone_spec.md

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,22 @@ The portal gives the messenger max approval for each enabled token so that withd
193193

194194
### Zone Predeploys
195195

196-
Each zone has four system contracts deployed at genesis at fixed addresses:
196+
Each zone has six system contracts deployed at genesis at fixed addresses:
197197

198198
| Predeploy | Address | Purpose |
199199
|-----------|---------|---------|
200200
| [`TempoState`](#itempostate) | `0x1c00...0000` | Stores finalized Tempo block headers and provides storage read access to Tempo contracts. |
201201
| [`ZoneInbox`](#izoneinbox) | `0x1c00...0001` | Advances the zone's view of Tempo and processes incoming deposits. Sole mint authority. |
202202
| [`ZoneOutbox`](#izoneoutbox) | `0x1c00...0002` | Handles withdrawal requests and batch finalization. Sole burn authority. |
203203
| [`ZoneConfig`](#izoneconfig) | `0x1c00...0003` | Central configuration. Reads the sequencer address and token registry from Tempo via `TempoState`. |
204+
| `TempoStateReader` | `0x1c00...0004` | Precompile stub for reading Tempo L1 storage. Actual reads are performed by the zone node and validated against the `tempoStateRoot`. |
205+
| `ZoneTxContext` | `0x1c00...0005` | Provides the current transaction hash to system contracts (used by `ZoneOutbox` for `senderTag` computation). |
204206

205207
`ZoneConfig` reads the sequencer address and token registry from the portal on Tempo via `TempoState` storage reads, making Tempo the single source of truth for zone configuration. See [Tempo State Reads](#tempo-state-reads) for details.
206208

207209
### Zone Token Model
208210

209-
Zones have no TIP-20 factory and contract creation is disabled (`CREATE` and `CREATE2` revert). All TIP-20 tokens on a zone are representations of Tempo tokens, deployed at the same address as on Tempo. When the sequencer enables a token on the portal, the zone node provisions a TIP-20 precompile at that address.
211+
Contract creation is disabled on zones (`CREATE` and `CREATE2` revert). All TIP-20 tokens on a zone are representations of Tempo tokens, deployed at the same address as on Tempo. When the sequencer enables a token on the portal, the zone's TIP-20 factory precompile (at `0x20Fc000000000000000000000000000000000000`) provisions a TIP-20 token precompile at that address. The factory is called by `ZoneInbox` during `advanceTempo` and is not user-accessible.
210212

211213
Token supply on the zone is controlled exclusively by the system contracts:
212214

@@ -236,7 +238,7 @@ The sequencer configures two gas rates that determine fees for deposits and with
236238
| Rate | Set via | Used for |
237239
|------|---------|----------|
238240
| `zoneGasRate` | `ZonePortal.setZoneGasRate()` | Deposit fees: `FIXED_DEPOSIT_GAS (100,000) * zoneGasRate` |
239-
| `tempoGasRate` | `ZoneOutbox.setTempoGasRate()` | Withdrawal fees: `gasLimit * tempoGasRate` |
241+
| `tempoGasRate` | `ZoneOutbox.setTempoGasRate()` | Withdrawal fees: `(WITHDRAWAL_BASE_GAS (50,000) + gasLimit) * tempoGasRate` |
240242

241243
Both rates are denominated in token units per gas unit. A single uniform `zoneGasRate` applies to all tokens. Fees are paid in the same token being deposited or withdrawn.
242244

@@ -419,14 +421,15 @@ sequenceDiagram
419421
The withdrawal fee compensates the sequencer for Tempo-side gas costs:
420422

421423
```
422-
fee = gasLimit * tempoGasRate
424+
fee = (WITHDRAWAL_BASE_GAS + gasLimit) * tempoGasRate
425+
= (50,000 + gasLimit) * tempoGasRate
423426
```
424427

425-
The user specifies `gasLimit` covering all execution costs on Tempo (processing overhead plus any callback). The fee is paid in the same token being withdrawn. On success, `amount` goes to the recipient and `fee` goes to the sequencer. On failure (bounce-back), only `amount` is re-deposited to `fallbackRecipient`; the sequencer keeps the fee.
428+
`WITHDRAWAL_BASE_GAS` (50,000) covers the fixed overhead of processing a withdrawal on Tempo (queue dequeue, transfer, event emission). The user specifies `gasLimit` covering any additional execution costs (e.g., callback gas). For simple withdrawals with no callback, use `gasLimit = 0`. The fee is paid in the same token being withdrawn. On success, `amount` goes to the recipient and `fee` goes to the sequencer. On failure (bounce-back), only `amount` is re-deposited to `fallbackRecipient`; the sequencer keeps the fee.
426429

427430
### Withdrawal Batching
428431

429-
At the end of the final block in a batch, the sequencer calls `finalizeWithdrawalBatch(count)` on the `ZoneOutbox`. This constructs a hash chain from pending withdrawals in LIFO order (newest to oldest), so the oldest withdrawal ends up outermost, enabling FIFO processing on Tempo:
432+
At the end of the final block in a batch, the sequencer calls `finalizeWithdrawalBatch(count, blockNumber, encryptedSenders)` on the `ZoneOutbox`. The `blockNumber` must match the current zone block number. The `encryptedSenders` array carries one ciphertext per finalized withdrawal for [authenticated withdrawals](#authenticated-withdrawals) (empty bytes for withdrawals without `revealTo`). This constructs a hash chain from pending withdrawals in LIFO order (newest to oldest), so the oldest withdrawal ends up outermost, enabling FIFO processing on Tempo:
430433

431434
```
432435
withdrawalQueueHash = EMPTY_SENTINEL
@@ -487,7 +490,7 @@ senderTag = keccak256(abi.encodePacked(sender, txHash))
487490

488491
The `txHash` is the hash of the `requestWithdrawal` transaction on the zone. Since zone transaction data is not published, `txHash` acts as a blinding factor known only to the sender and the sequencer.
489492

490-
The sender can optionally specify a `revealTo` public key (compressed secp256k1, 33 bytes) when requesting the withdrawal. If provided, the sequencer encrypts `(sender, txHash)` to that key using ECDH and populates `encryptedSender` in the withdrawal struct. The wire format is `ephemeralPubKey (33 bytes) || ciphertext (52 bytes) || mac (16 bytes)`.
493+
The sender can optionally specify a `revealTo` public key (compressed secp256k1, 33 bytes) when requesting the withdrawal. If provided, the sequencer encrypts `(sender, txHash)` to that key using ECDH and populates `encryptedSender` in the withdrawal struct. The wire format is `ephemeralPubKey (33 bytes) || nonce (12 bytes) || ciphertext (52 bytes) || tag (16 bytes)` totaling 113 bytes.
491494

492495
Two disclosure modes are available:
493496

@@ -523,9 +526,9 @@ Zone transactions specify which enabled TIP-20 token to use for gas fees via a `
523526

524527
Each zone block contains system transactions and user transactions in a fixed order:
525528

526-
1. `ZoneInbox.advanceTempo(header, deposits, decryptions)` (optional, at the start of the block). Advances the zone's view of Tempo, processes any pending deposits, and verifies encrypted deposit decryptions. If omitted, the zone's Tempo binding carries forward from the previous block.
529+
1. `ZoneInbox.advanceTempo(header, deposits, decryptions, enabledTokens)` (optional, at the start of the block). Advances the zone's view of Tempo, enables newly-bridged tokens, processes any pending deposits, and verifies encrypted deposit decryptions. If omitted, the zone's Tempo binding carries forward from the previous block.
527530
2. User transactions, executed in order.
528-
3. `ZoneOutbox.finalizeWithdrawalBatch(count)` (required in the final block of a batch, absent in intermediate blocks). Constructs the withdrawal hash chain from pending withdrawals and writes the `withdrawalQueueHash` and `withdrawalBatchIndex` to state. Must be called even if there are zero withdrawals so the batch index advances.
531+
3. `ZoneOutbox.finalizeWithdrawalBatch(count, blockNumber, encryptedSenders)` (required in the final block of a batch, absent in intermediate blocks). Constructs the withdrawal hash chain from pending withdrawals, populates `encryptedSender` for authenticated withdrawals, and writes the `withdrawalQueueHash` and `withdrawalBatchIndex` to state. Must be called even if there are zero withdrawals so the batch index advances.
529532

530533
A batch covers one or more zone blocks, with each batch interval targeting 250 milliseconds. The sequencer controls batch frequency, and intermediate blocks within a batch contain only `advanceTempo` (optional) and user transactions.
531534

@@ -541,7 +544,7 @@ Zone blocks use a simplified header with fewer fields than a standard Ethereum h
541544
| `transactionsRoot` | `bytes32` | Root computed over the ordered list of block transactions |
542545
| `receiptsRoot` | `bytes32` | Root computed over the ordered list of transaction receipts |
543546
| `number` | `uint64` | Block number |
544-
| `timestamp` | `uint64` | Block timestamp (must be monotonically increasing) |
547+
| `timestamp` | `uint64` | Block timestamp (must be non-decreasing) |
545548
| `protocolVersion` | `uint64` | Zone protocol version |
546549

547550
The block hash is `keccak256` of the RLP-encoded header. Batch proofs commit to block hash transitions (`prevBlockHash` to `nextBlockHash`), not raw state roots, so the proof covers the full header structure.
@@ -576,15 +579,16 @@ If a block omits `advanceTempo`, the Tempo binding carries forward from the prev
576579

577580
### Storage Reads
578581

579-
`TempoState` provides `readTempoStorageSlot(account, slot)` for reading storage from any Tempo contract. This function is restricted to zone system contracts only: `ZoneInbox`, `ZoneOutbox`, and `ZoneConfig`. User transactions cannot call it.
582+
`TempoState` provides `readTempoStorageSlot(account, slot)` for reading storage from any Tempo contract. This function is restricted to zone system contracts (`ZoneInbox`, `ZoneOutbox`, `ZoneConfig`). User transactions cannot call it.
580583

581584
The function is a precompile stub. The actual storage reads are performed by the zone node and validated against the `tempoStateRoot` from the finalized header. The prover includes Merkle proofs for each unique account and storage slot accessed by system contracts during the batch.
582585

583-
System contracts use storage reads for:
586+
Current callers:
584587

588+
- `ZoneInbox`: `currentDepositQueueHash` and encryption keys from the portal
585589
- `ZoneConfig`: sequencer address, token registry from the portal
586-
- `ZoneInbox`: `currentDepositQueueHash` from the portal
587-
- `TIP403Registry`: policy state from Tempo
590+
591+
TIP-403 policy authorization on the zone is handled by a dedicated read-only proxy precompile (at the same address as the L1 `TIP403Registry`), which resolves policy queries via the zone node's policy provider rather than calling `readTempoStorageSlot` directly.
588592

589593
### Staleness and Finality
590594

@@ -1149,11 +1153,14 @@ Address: `0x1c00000000000000000000000000000000000001`
11491153
interface IZoneInbox {
11501154
function processedDepositQueueHash() external view returns (bytes32);
11511155
function advanceTempo(
1152-
bytes calldata header, QueuedDeposit[] calldata deposits, DecryptionData[] calldata decryptions
1156+
bytes calldata header, QueuedDeposit[] calldata deposits, DecryptionData[] calldata decryptions,
1157+
EnabledToken[] calldata enabledTokens
11531158
) external;
11541159
}
11551160
```
11561161

1162+
`EnabledToken` carries token metadata (`token`, `name`, `symbol`, `currency`) for provisioning zone-side TIP-20 precompiles via the TIP-20 factory.
1163+
11571164
### IZoneOutbox
11581165

11591166
Address: `0x1c00000000000000000000000000000000000002`
@@ -1165,12 +1172,18 @@ interface IZoneOutbox {
11651172
function calculateWithdrawalFee(uint64 gasLimit) external view returns (uint128);
11661173
function setTempoGasRate(uint128 _tempoGasRate) external;
11671174
1175+
function requestWithdrawal(
1176+
address token, address to, uint128 amount, bytes32 memo,
1177+
uint64 gasLimit, address fallbackRecipient, bytes calldata data
1178+
) external;
1179+
11681180
function requestWithdrawal(
11691181
address token, address to, uint128 amount, bytes32 memo,
11701182
uint64 gasLimit, address fallbackRecipient, bytes calldata data, bytes calldata revealTo
11711183
) external;
11721184
1173-
function finalizeWithdrawalBatch(uint256 count) external returns (bytes32 withdrawalQueueHash);
1185+
function finalizeWithdrawalBatch(uint256 count, uint64 blockNumber, bytes[] calldata encryptedSenders)
1186+
external returns (bytes32 withdrawalQueueHash);
11741187
}
11751188
```
11761189

@@ -1197,15 +1210,17 @@ Deployed at the same address as on Tempo. Read-only on the zone. Its `isAuthoriz
11971210

11981211
## Network Upgrades and Hard Fork Activation
11991212

1213+
> **Note:** The verifier rotation and protocol version mechanisms described below are the target design. The current `ZonePortal` implementation declares `verifier` as `immutable`, so the rotation mechanism is not yet implemented. This section will be updated when the upgrade contracts are deployed.
1214+
12001215
Zones activate hard fork upgrades in lockstep with Tempo using same-block activation. The trigger is the Tempo block number: the zone block whose `advanceTempo` imports the fork Tempo block uses the new execution rules for its entire scope.
12011216

1202-
The portal maintains two verifier slots (`verifier` and `forkVerifier`). At each fork, verifiers rotate: the previous fork verifier is promoted to `verifier`, and the new fork verifier takes the `forkVerifier` slot. At most two verifiers are active at any time. The `IVerifier` interface is unchanged across forks; new proof parameters are passed via the opaque `verifierConfig` bytes.
1217+
The portal will maintain two verifier slots (`verifier` and `forkVerifier`). At each fork, verifiers rotate: the previous fork verifier is promoted to `verifier`, and the new fork verifier takes the `forkVerifier` slot. At most two verifiers are active at any time. The `IVerifier` interface is unchanged across forks; new proof parameters are passed via the opaque `verifierConfig` bytes.
12031218

1204-
`ZoneFactory` maintains a `protocolVersion` counter incremented at each fork. Zone nodes embed the highest protocol version they support and halt cleanly if the imported Tempo block bumps `protocolVersion` beyond their supported version, preventing an outdated node from producing blocks under incorrect rules.
1219+
`ZoneFactory` will maintain a `protocolVersion` counter incremented at each fork. Zone nodes embed the highest protocol version they support and halt cleanly if the imported Tempo block bumps `protocolVersion` beyond their supported version, preventing an outdated node from producing blocks under incorrect rules.
12051220

12061221
No onchain action is required from zone operators. The new verifier is deployed and rotated as part of the Tempo hard fork. Operators upgrade their zone node binary and prover program before the fork; when the fork Tempo block arrives, the node activates new rules automatically.
12071222

1208-
The portal enforces a `forkActivationBlock` cutoff where batches targeting the old `verifier` must have `tempoBlockNumber < forkActivationBlock`. This prevents post-fork batches from being submitted against old verification rules.
1223+
The portal will enforce a `forkActivationBlock` cutoff where batches targeting the old `verifier` must have `tempoBlockNumber < forkActivationBlock`. This prevents post-fork batches from being submitted against old verification rules.
12091224

12101225
The Tempo hard fork block executes the following as system transactions:
12111226

0 commit comments

Comments
 (0)