-
Notifications
You must be signed in to change notification settings - Fork 727
Document Denim 200ms native blocks #1776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
0x00101010
wants to merge
6
commits into
master
Choose a base branch
from
200ms/docs
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+186
−0
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c2c6d25
Add Denim: 200ms section
0x00101010 bd03ed3
docs: refine Denim native blocks spec
0x00101010 71dc82a
docs: clarify BaseTime activation state
0x00101010 c506957
docs: specify BaseTime engine attributes
0x00101010 aaec89b
docs: clarify preliminary timestamp validation
0x00101010 5865269
docs: simplify BaseTime progression validation
0x00101010 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
docs/base-chain/specs/upgrades/denim/200ms-native-blocks.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| --- | ||
| title: "200ms native blocks" | ||
| description: "Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior." | ||
| --- | ||
|
|
||
| <Warning> | ||
| Denim is a tentative draft published to elicit feedback. The design is mostly finalized, but this is not a production specification and may change. Denim is not active on Base Sepolia or Base Mainnet; activation times and required client versions remain undecided. | ||
| </Warning> | ||
|
|
||
| ## Summary | ||
|
|
||
| Denim changes Base block production from one canonical block every two seconds to five complete canonical blocks per second. Each 200ms block has its own block number, hash, state root, receipts, forkchoice updates, and unsafe, safe, and finalized lifecycle. | ||
|
|
||
| Denim replaces [Flashblocks](/base-chain/flashblocks/faq), which publish incremental pending-state updates for a single block. As part of the Denim rollout, Base will stop producing Flashblocks and instead produce a canonical block every 200ms. Applications using Flashblocks must migrate to canonical block and RPC streams. | ||
|
|
||
| Denim keeps the Ethereum block header and its seconds-based `timestamp` unchanged. A BaseTime metadata deposit supplies the sub-second component. Together, these values identify a canonical block's full millisecond timestamp. | ||
|
|
||
| ## Tentative activation | ||
|
|
||
| | Network | Activation timestamp | Required client versions | | ||
| | --- | --- | --- | | ||
| | `sepolia` | `TBD` | `TBD` | | ||
| | `mainnet` | `TBD` | `TBD` | | ||
|
|
||
| No Denim activation has been scheduled. A later node upgrade guide will provide release and rollout instructions. | ||
|
|
||
| ## Execution | ||
|
|
||
| ### Full block timestamp | ||
|
|
||
| Denim leaves `block.header.timestamp` as Unix time in whole seconds. For an activated block `b`, its full timestamp is: | ||
|
|
||
| $$ | ||
| T_{ms}(b) = 1000 \times b.header.timestamp + b.tx[1].timestamp\_millis\_part | ||
| $$ | ||
|
|
||
| The BaseTime deposit at `tx[1]` carries `timestamp_millis_part`. The only valid values are `0`, `200`, `400`, `600`, and `800`. Consecutive activated blocks satisfy: | ||
|
|
||
| $$ | ||
| T_{ms}(child) = T_{ms}(parent) + 200 | ||
| $$ | ||
|
|
||
| Blocks cannot skip slots. The seconds header and millisecond part must come from the same scheduled timestamp; wall-clock time controls when the sequencer starts a build, not the timestamp assigned to that block. EVM `block.timestamp` remains the whole-second header value. | ||
|
|
||
| ### BaseTime metadata deposit | ||
|
|
||
| After activation, every block contains the canonical BaseTime update at `tx[1]`, immediately after the L1 information deposit at `tx[0]` and before user transactions. The deposit is bound to the current block number by source-hash domain `3`. | ||
|
|
||
| | Field | Value | | ||
| | --- | --- | | ||
| | Transaction type | Deposit (`0x7e`) | | ||
| | Source hash | Domain `3`, bound to the current block number | | ||
| | From | `0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001` | | ||
| | To | `0x4200000000000000000000000000000000000030` | | ||
| | Mint | `0` | | ||
| | Value | `0` | | ||
| | Gas limit | `1,000,000` | | ||
| | System transaction | `false` | | ||
| | Calldata | `setTimestampMillisPart(uint16)` with selector `0x86bdf394` and a 32-byte ABI-encoded millisecond part | | ||
|
|
||
| Before activation, blocks must not contain this metadata transaction or the Engine millisecond field. After activation, implementations validate the transaction's position, source hash, sender, recipient, mint, value, gas limit, system flag, calldata shape, and lattice value. | ||
|
|
||
| ### BaseTime predeploy | ||
|
|
||
| The initial design exposes the current block's millisecond part to contracts through a predeploy. | ||
|
|
||
| | Property | Value | | ||
| | --- | --- | | ||
| | Proxy | `0x4200000000000000000000000000000000000030` | | ||
| | Implementation | `0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30030` | | ||
| | Storage | `uint16` millisecond part in slot `0` | | ||
| | Setter | `setTimestampMillisPart(uint16)` | | ||
| | Millisecond-part getter | `timestampMillisPart()` | | ||
| | Full-timestamp getter | `timestampMs()` | | ||
|
|
||
| The BaseTime deposit executes before L1 user deposits, and user transactions, so all later transactions can read the updated value. | ||
|
|
||
| Fresh chains install the linked BaseTime predeploy in genesis. On existing chains, the reserved address already contains the canonical proxy runtime and uses the Base ProxyAdmin, but its implementation slot is unset. Calls therefore revert until activation. | ||
|
|
||
| At activation, before transaction execution, the protocol installs the canonical BaseTime implementation and links the existing proxy. It preserves the proxy admin and any implementation already set through governance. | ||
|
|
||
| ### Engine payload attributes | ||
|
|
||
| The Engine API uses `BasePayloadAttributes`, which flattens the standard `PayloadAttributes` fields alongside Base-specific fields. | ||
|
|
||
| For payloads at or after Denim activation, `BasePayloadAttributes.timestampMillisPart` **MUST** be present and equal `0`, `200`, `400`, `600`, or `800`. Before activation, it **MUST NOT** be present. | ||
|
|
||
| `BasePayloadAttributes.transactions[1]` **MUST** contain the BaseTime metadata deposit. The deposit **MUST** be sent by the protocol depositor to the BaseTime predeploy, and its calldata **MUST** contain the canonical encoding of `setTimestampMillisPart(uint16)`. The encoded value **MUST** equal `timestampMillisPart`. | ||
|
|
||
| An execution client **MUST** reject malformed `forkchoiceUpdated` payload attributes with JSON-RPC `Invalid params` (`-32602`). It **MUST** report an execution payload with a missing or invalid BaseTime deposit as invalid during `newPayload` validation. | ||
|
|
||
| The payload ID includes `timestampMillisPart`, so builds that differ only in the millisecond part receive different IDs. When the field is absent, legacy payload-ID calculation remains unchanged. | ||
|
|
||
| All existing Engine timestamp fields remain seconds-based. | ||
|
|
||
| ### Validation | ||
|
|
||
| When processing `forkchoiceUpdated`, the execution client performs checks that do not require reading contract state. Before Denim, `timestampMillisPart` must be absent. After Denim, it must be present and equal `0`, `200`, `400`, `600`, or `800`. The block's whole-second timestamp must not be earlier than its parent's. Because block headers do not store milliseconds, the client checks exact 200ms progression after execution. | ||
|
|
||
| After execution, the client **MUST** verify that the block's full timestamp is exactly 200ms after its parent's. Blocks that do not satisfy this requirement are invalid. | ||
|
|
||
| ## Derivation | ||
|
|
||
| ### Scheduled timestamps | ||
|
|
||
| After activation, the derivation pipeline computes each block's timestamp from the Denim activation schedule and absolute L2 block number. It does not read the millisecond part from batch data or derive it from the local wall clock. The sequence is: | ||
|
0x00101010 marked this conversation as resolved.
|
||
|
|
||
| `p (.000)` → `child (.200)` → `child (.400)` → `child (.600)` → `child (.800)` → `child (.000 in the next second)` | ||
|
|
||
| The pipeline splits the scheduled timestamp into the seconds-based header timestamp and the BaseTime millisecond part, then reconstructs the BaseTime deposit at `tx[1]`. | ||
|
|
||
| ### Block lifecycle | ||
|
|
||
| The sequencer builds and executes a complete block for every selected 200ms slot. Each block receives its own hash, state root, receipts, Engine payload, forkchoice update, and unsafe-to-safe-to-finalized lifecycle. | ||
|
|
||
| The block begins with the L1 information deposit at `tx[0]` and the BaseTime metadata deposit at `tx[1]`. User transactions and other applicable transactions follow. The design intends the payload attribute, `tx[1]`, and the value written to the BaseTime predeploy to represent the same planned millisecond part. | ||
|
|
||
| ## RPC | ||
|
|
||
| <Note> | ||
| The RPC behavior below is planned for Denim and is not available on production endpoints. | ||
| </Note> | ||
|
|
||
| ### Seconds compatibility | ||
|
|
||
| Denim keeps the existing `timestamp` JSON-RPC field in Unix seconds. Engine timestamps, transaction-validity timestamps, `eth_call` timestamps, and EVM `block.timestamp` also remain seconds-based. RPC responses do not expose the internal `timestampMillisPart` field. | ||
|
|
||
| After the Denim rollout, use canonical block responses and subscriptions such as `eth_subscribe("newHeads")` for sub-second updates. Flashblocks streams will stop. | ||
|
|
||
| ### Block and header timestamps | ||
|
|
||
| The following responses will add optional `timestampMs`, encoded as a JSON-RPC quantity containing the full Unix timestamp in milliseconds: | ||
|
|
||
| - `eth_getBlockByHash` | ||
| - `eth_getBlockByNumber` | ||
| - `eth_getHeaderByHash` | ||
| - `eth_getHeaderByNumber` | ||
| - `eth_subscribe("newHeads")` | ||
|
|
||
| For example, a block at 42.200 seconds has: | ||
|
|
||
| | Field | Value | | ||
| | --- | --- | | ||
| | `timestamp` | `0x2a` | | ||
| | `timestampMs` | `0xa4d8` | | ||
|
|
||
| Clients will derive `timestampMs` from authenticated BaseTime metadata rather than estimate it from the seconds field. If a historical block body or its BaseTime metadata has been pruned or is otherwise unavailable, the response will omit `timestampMs`. | ||
|
|
||
| ### Transaction timestamps | ||
|
|
||
| Mined transaction objects will add optional `blockTimestampMs` for these methods: | ||
|
|
||
| - `eth_getTransactionByHash` | ||
| - `eth_getTransactionByBlockHashAndIndex` | ||
| - `eth_getTransactionByBlockNumberAndIndex` | ||
|
|
||
| Full transaction objects nested in block responses will follow the same mined-transaction behavior. Pending transactions will omit `blockTimestampMs` because they do not yet belong to a canonical block. | ||
|
|
||
| ### Log and receipt timestamps | ||
|
|
||
| Mined log objects will add optional `blockTimestampMs` when returned by: | ||
|
|
||
| - `eth_getLogs` | ||
| - `eth_getFilterChanges` | ||
| - `eth_getFilterLogs` | ||
| - `eth_getTransactionReceipt` | ||
| - `eth_getBlockReceipts` | ||
| - `eth_subscribe("logs")` | ||
| - `eth_subscribe("transactionReceipts")` | ||
|
|
||
| Receipts will expose the field on their nested logs, not at the receipt's top level. A removed log will retain its original block timestamp along with its original block provenance. If the originating block's authenticated BaseTime metadata is unavailable, the log will omit the field rather than estimate it. | ||
|
|
||
| As with block responses, pruned or unprovenanced transaction and log data will omit `blockTimestampMs`. The fields are optional so pre-Denim history and clients without the required body data remain representable. | ||
|
|
||
| ### Tooling | ||
|
|
||
| Foundry's `AnyRpcBlock` and `OtherFields` paths can preserve an unknown block-level `timestampMs`, while EVM execution continues to use seconds. Plain Alloy `AnyRpcHeader` drops unknown fields; consumers that need Denim timestamps can use `WithOtherFields<AnyRpcHeader>` or a typed Base response. Locally mined Anvil blocks are not expected to produce BaseTime metadata in the initial rollout. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.