From 4cabf1bc148ab86c8831f25ade29dddf600268b8 Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Fri, 17 Mar 2023 22:00:03 -0400 Subject: [PATCH] docs(website): add more docs (#13316) Co-authored-by: David --- .../website/excalidraw/taiko-nodes.excalidraw | 409 ++++++++++++++++++ packages/website/next.config.js | 1 + .../website/pages/docs/concepts/_meta.json | 10 +- .../concepts/creating-l2-blocks/_meta.json | 11 + .../creating-l2-blocks/anchor-transaction.mdx | 14 + .../creating-l2-blocks/creating-l2-blocks.mdx | 7 + .../intrinsic-validity-functions.mdx | 63 +++ ...oposal-and-verification.md => overview.md} | 6 +- .../pages/docs/concepts/taiko-nodes.mdx | 99 +++++ .../pages/docs/guides/configure-wallet.mdx | 2 - .../pages/docs/guides/deploy-a-contract.md | 2 - .../pages/docs/guides/request-from-faucet.md | 2 - .../website/pages/docs/guides/run-a-node.mdx | 305 +++---------- .../pages/docs/guides/use-the-bridge.mdx | 2 - .../website/pages/docs/reference/_meta.json | 3 + .../docs/reference/protocol-constants.mdx | 29 ++ .../public/images/diagrams/taiko-nodes.png | Bin 0 -> 49569 bytes 17 files changed, 714 insertions(+), 251 deletions(-) create mode 100644 packages/website/excalidraw/taiko-nodes.excalidraw create mode 100644 packages/website/pages/docs/concepts/creating-l2-blocks/_meta.json create mode 100644 packages/website/pages/docs/concepts/creating-l2-blocks/anchor-transaction.mdx create mode 100644 packages/website/pages/docs/concepts/creating-l2-blocks/creating-l2-blocks.mdx create mode 100644 packages/website/pages/docs/concepts/creating-l2-blocks/intrinsic-validity-functions.mdx rename packages/website/pages/docs/concepts/{block-proposal-and-verification.md => overview.md} (96%) create mode 100644 packages/website/pages/docs/concepts/taiko-nodes.mdx create mode 100644 packages/website/pages/docs/reference/protocol-constants.mdx create mode 100644 packages/website/public/images/diagrams/taiko-nodes.png diff --git a/packages/website/excalidraw/taiko-nodes.excalidraw b/packages/website/excalidraw/taiko-nodes.excalidraw new file mode 100644 index 00000000000..c68e5fda96a --- /dev/null +++ b/packages/website/excalidraw/taiko-nodes.excalidraw @@ -0,0 +1,409 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "ellipse", + "version": 202, + "versionNonce": 1070461970, + "isDeleted": false, + "id": "qM2jKY_qgMEhrCVzNtJzq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 382.4819172642266, + "y": 434.76527255819155, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 202, + "height": 190, + "seed": 731216210, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "iG-zlHPjTZpt45kW52_h-" + }, + { + "id": "hYcjDeXeWzxIv5Ee5TsUi", + "type": "arrow" + } + ], + "updated": 1678929254408, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 131, + "versionNonce": 1665699342, + "isDeleted": false, + "id": "iG-zlHPjTZpt45kW52_h-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 396.9819172642266, + "y": 506.76527255819155, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 173, + "height": 46, + "seed": 627030734, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1678929254408, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "taiko-geth\n(execution engine)", + "baseline": 40, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qM2jKY_qgMEhrCVzNtJzq", + "originalText": "taiko-geth\n(execution engine)" + }, + { + "type": "ellipse", + "version": 355, + "versionNonce": 1001854133, + "isDeleted": false, + "id": "Q4J8rIwRQLGXmkHRL2Gqa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 823.898193359375, + "y": 434.72344970703125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 202, + "height": 190, + "seed": 1721779662, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "VDA50DlWkpaJpmCZSru15" + }, + { + "id": "hYcjDeXeWzxIv5Ee5TsUi", + "type": "arrow" + }, + { + "id": "GfTImpBro9K1_k7IXwl-B", + "type": "arrow" + } + ], + "updated": 1679059337893, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 312, + "versionNonce": 1093952085, + "isDeleted": false, + "id": "VDA50DlWkpaJpmCZSru15", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.398193359375, + "y": 506.22344970703125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 175, + "height": 46, + "seed": 113919506, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679059340890, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "taiko-client driver\n(beacon client)", + "baseline": 40, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Q4J8rIwRQLGXmkHRL2Gqa", + "originalText": "taiko-client driver\n(beacon client)" + }, + { + "type": "arrow", + "version": 359, + "versionNonce": 1109467579, + "isDeleted": false, + "id": "hYcjDeXeWzxIv5Ee5TsUi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 587.4767873402757, + "y": 536.0285973936182, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 230.83353710686583, + "height": 0.6413769384503212, + "seed": 502745294, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "WDy2XemUlkuWQG4McR8nm" + } + ], + "updated": 1679059337894, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qM2jKY_qgMEhrCVzNtJzq", + "focus": 0.06954578751473034, + "gap": 3.207015712266241 + }, + "endBinding": { + "elementId": "Q4J8rIwRQLGXmkHRL2Gqa", + "focus": -0.056500949316976856, + "gap": 5.7566688299851165 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 230.83353710686583, + -0.6413769384503212 + ] + ] + }, + { + "type": "text", + "version": 21, + "versionNonce": 401086267, + "isDeleted": false, + "id": "WDy2XemUlkuWQG4McR8nm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 673.9855907930778, + "y": 512.6371983227873, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 58, + "height": 46, + "seed": 1332479694, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679059331335, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Engine\nAPI", + "baseline": 40, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hYcjDeXeWzxIv5Ee5TsUi", + "originalText": "Engine\nAPI" + }, + { + "type": "arrow", + "version": 2178, + "versionNonce": 1990931035, + "isDeleted": false, + "id": "GfTImpBro9K1_k7IXwl-B", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 919.1610632620805, + "y": 425.3909715262163, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 0.9125332559432309, + "height": 177.37292701890095, + "seed": 2071254354, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "U_7yMPrw0wYtOOdI7RZt4" + } + ], + "updated": 1679059337895, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Q4J8rIwRQLGXmkHRL2Gqa", + "gap": 9.473453343931588, + "focus": -0.051683775614769785 + }, + "endBinding": { + "elementId": "IF4_94pWHK8h5tfhnazUb", + "gap": 6.467661444351063, + "focus": -0.0241095311122182 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.9125332559432309, + -177.37292701890095 + ] + ] + }, + { + "type": "text", + "version": 108, + "versionNonce": 367962883, + "isDeleted": false, + "id": "U_7yMPrw0wYtOOdI7RZt4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.7804652670332, + "y": 313.62166389093494, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 110, + "height": 46, + "seed": 598158802, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679058664752, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Listens for\nnew blocks", + "baseline": 40, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "GfTImpBro9K1_k7IXwl-B", + "originalText": "Listens for\nnew blocks" + }, + { + "type": "rectangle", + "version": 1017, + "versionNonce": 817186766, + "isDeleted": false, + "id": "IF4_94pWHK8h5tfhnazUb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 830.8392829448944, + "y": 117.55038306296427, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 170, + "height": 124, + "seed": 1566785426, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Ief7cdtHy82hrc-zWfmDT" + }, + { + "id": "GfTImpBro9K1_k7IXwl-B", + "type": "arrow" + } + ], + "updated": 1678929398032, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 885, + "versionNonce": 1442735122, + "isDeleted": false, + "id": "Ief7cdtHy82hrc-zWfmDT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 872.3392829448944, + "y": 156.55038306296427, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 87, + "height": 46, + "seed": 572091598, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1678929398032, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TaikoL1\ncontract", + "baseline": 40, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IF4_94pWHK8h5tfhnazUb", + "originalText": "TaikoL1\ncontract" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/packages/website/next.config.js b/packages/website/next.config.js index 102f41624ab..58fa153d886 100644 --- a/packages/website/next.config.js +++ b/packages/website/next.config.js @@ -1,5 +1,6 @@ const withNextra = require("nextra")({ defaultShowCopyCode: true, + latex: true, theme: "nextra-theme-docs", themeConfig: "./theme.config.tsx", }); diff --git a/packages/website/pages/docs/concepts/_meta.json b/packages/website/pages/docs/concepts/_meta.json index abe948b0317..fa3e4f9a03b 100644 --- a/packages/website/pages/docs/concepts/_meta.json +++ b/packages/website/pages/docs/concepts/_meta.json @@ -1,8 +1,14 @@ { - "block-proposal-and-verification": { - "title": "Block proposal & verification" + "overview": { + "title": "Overview" + }, + "creating-l2-blocks": { + "title": "Creating L2 blocks" }, "bridging": { "title": "Bridging" + }, + "taiko-nodes": { + "title": "Taiko nodes" } } diff --git a/packages/website/pages/docs/concepts/creating-l2-blocks/_meta.json b/packages/website/pages/docs/concepts/creating-l2-blocks/_meta.json new file mode 100644 index 00000000000..ee0deae7cf5 --- /dev/null +++ b/packages/website/pages/docs/concepts/creating-l2-blocks/_meta.json @@ -0,0 +1,11 @@ +{ + "creating-l2-blocks": { + "title": "Creating L2 blocks" + }, + "intrinsic-validity-functions": { + "title": "Intrinsic validity functions" + }, + "anchor-transaction": { + "title": "Anchor transaction" + } +} \ No newline at end of file diff --git a/packages/website/pages/docs/concepts/creating-l2-blocks/anchor-transaction.mdx b/packages/website/pages/docs/concepts/creating-l2-blocks/anchor-transaction.mdx new file mode 100644 index 00000000000..fb7400bcaec --- /dev/null +++ b/packages/website/pages/docs/concepts/creating-l2-blocks/anchor-transaction.mdx @@ -0,0 +1,14 @@ +The anchor transaction is a way for the protocol to make use of the programmability of the EVM (which we already need to be able to proof) to enforce certain protocol behavior. We can add additional tasks to anchor transactions to enrich Taiko’s functionalities by writing standard smart contract code (instead of requiring more complicated changes to Taiko’s ZK-EVM and node subsystems). + +The anchor transaction is required to be the first transaction in a Taiko block (which is important to make the block deterministic). The anchor transaction is currently used as follows: +1. Persisting `l1Height` $C_a$ and `l1Hash` $C_h$, data inherited from L1, to the storage trie. These values can be used by bridges to validate cross-chain messages. +2. Comparing $ρ_{i−1}$, the public input hash stored by the previous block, with `KEC(i − 1, d, h[2..256])`. The anchor transaction will throw an exception if such comparison fails. The protocol requires the anchor transaction to execute successfully and will not accept a proof for a block that fails to do so. Note that the genesis block has $ρ_0$ ≡ `KEC(0, d, [0, ..., 0])`. +3. Persisting a new public input hash $ρ_i$ ≡ `KEC(i, d, h[1..255])` to the storage trie for the next block to use. This allows transactions, in the current and all following blocks, to access these public input data with confidence as their values are now covered by ZK-EVM’s storage proof. +4. With anchoring, the block mapping function `M` (defined in the [whitepaper](/docs/resources/whitepaper)) can be simplified to: +$$$ +\begin{aligned} +B &≡ (H, T, U) \\ +&≡ M(δ, θ, B, \dot{B}) \\ +&≡ M(δ, θ, C, L) +\end{aligned} +$$$ diff --git a/packages/website/pages/docs/concepts/creating-l2-blocks/creating-l2-blocks.mdx b/packages/website/pages/docs/concepts/creating-l2-blocks/creating-l2-blocks.mdx new file mode 100644 index 00000000000..187297ffd77 --- /dev/null +++ b/packages/website/pages/docs/concepts/creating-l2-blocks/creating-l2-blocks.mdx @@ -0,0 +1,7 @@ +On Taiko, the next L2 state is known immediately and deterministically at the time a block is proposed to the TaikoL1 contract. After a block is proposed, a series of checks are done to compute this post-L2 state: + +1. Block is proposed by any proposer (permissionlessly). +2. Block level properties are checked validity by TaikoL1 contract ([proposed block intrinsic validity function](./intrinsic-validity-functions#proposed-block-intrinsic-validity-function)). +3. Proposed block is downloaded by a Taiko node, and the transaction list is parsed over and checked for validity ([transaction list intrinsic validity function](./intrinsic-validity-functions#transaction-list-intrinsic-validity-function)). + - IF every transaction in the list is valid, an ordered subset of the list is created by skipping over transactions which have an invalid nonce or the sender has too little Ether balance to pay for the transaction. This ordered subset is used along with the [anchor transaction](./anchor-transaction) to create a Taiko L2 block. + - IF any transaction in the list is invalid, the block is proven to be invalid. diff --git a/packages/website/pages/docs/concepts/creating-l2-blocks/intrinsic-validity-functions.mdx b/packages/website/pages/docs/concepts/creating-l2-blocks/intrinsic-validity-functions.mdx new file mode 100644 index 00000000000..1a229bdaeed --- /dev/null +++ b/packages/website/pages/docs/concepts/creating-l2-blocks/intrinsic-validity-functions.mdx @@ -0,0 +1,63 @@ +The Ethereum yellow paper has a well defined set of rules to compute the state transition. We use these same rules to take a proposed block, and compute the post-block state on Taiko. A proposed block on Taiko has two parts: + +- The block metadata +- A list of transactions (stored in a blob, and the block metadata stores the hash to this blob) + +We divide the yellow paper validity checks into two parts: + +- **Proposed block** intrinsic validity function +- **Transaction list** intrinsic validity function + +A proposed block must pass these two checks in order to construct an L2 block on Taiko. If a block passes the proposed block validity function but later fails the transaction list validity function, it will be proven as invalid. + +## Proposed block intrinsic validity function + +The formal specification of the proposed block intrinsic can be found in the [whitepaper](/docs/resources/whitepaper), but on a high level, the proposed block intrinsic validity function checks that the block is valid except for transaction list validity checks. This is separated because on TaikoL1, the transaction list is not known and stored as a data blob. It would also be too expensive to do these checks on L1. + +| Name | Symbol | Meaning | +| ----------------------- | ----------------- | -------------------------------------------------------------------------------------------------------- | +| **Block Metadata** | $C$ | The block metadata. | +| **id** | $C_i$ | A value equal to the number of proposed blocks. The genesis block has an id of zero. | +| **beneficiary** | $C_c$ | The 20-byte address to which all transaction fees in the block will be transferred. | +| **timestamp** | $C_s$ | The timestamp used in the block, set to the enclosing L1 timestamp. | +| **mixHash** | $C_m$ | The mixHash value used in the block, set to the enclosing L1 mixHash. | +| **txListHash** | $C_t$ | The Keccak-256 hash of this block’s txList (KZG commitment after EIP-4844). | +| **l1Height** | $C_a$ | The enclosing L1 block’s parent block number. | +| **l1Hash** | $C_h$ | The enclosing L1 block’s parent block hash. | +| **TaikoL1** | $R$ | The TaikoL1 contract. | +| **numProposedBlocks** | $R_i$ | The current number of proposed blocks (the `id` for the next proposed block is `numProposedBlocks + 1`). | +| **lastVerifiedBlockId** | $R_f$ | The last verified block id. | +| **txList** | $L$ | The list of transactions in a proposed block. | +| **txListSizeInBytes** | $\lVert L \rVert$ | The transaction list size in bytes. | + +The proposed block intrinsic validity function checks that all these conditions are met: + +1. $R_i \le R_f + K_{MaxNumBlocks}$ (the block id is not too far ahead) +2. $\lVert L \rVert \gt 0$ (transaction list is not empty) +3. $\lVert L \rVert \le K_{MaxTxListSizeInBytes}$ (transaction list does not exceed the maximum size) +4. $C_c \ne 0$ (beneficiary is not zero) +5. $C_i = R_i$ (block id is correct) +6. $C_s = \text{TIMESTAMP}$ (timestamp is correct) +7. $C_m = \text{DIFFICULTY}$ (mixHash is correct) +8. $C_t \ne 0$ (txListHash is not zero) +9. $C_a = \text{NUMBER} - 1$ (l1Height is correct) +10. $C_h = \text{BLOCKHASH}(C_a)$ (l1Hash is correct) + +## Transaction list intrinsic validity function + +The transaction list intrinsic validity function checks that the transaction list is valid and each transaction in the list is valid. + +A transaction list is valid if and only if: + +1. The transaction list is well-formed RLP, with no additional trailing bytes (rule #1 in Ethereum yellow paper). +2. The transaction list is no larger than than $K_{TxListMaxBytes}$. +3. The sum of all transactions' gas limit is no larger than the protocol constant $K_{BlockMaxGasLimit}$. +4. The total number of transactions is no larger than the protocol constant $K_{BlockMaxTxs}$. + +A transaction is valid if and only if: + +1. The transaction is well-formed RLP, with no additional trailing bytes (rule #1 in the Ethereum yellow paper). +2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). +3. The transaction's gas limit is no smaller than the intrinsic gas $K_{TxMinGasLimit}$ (rule #5 in the Ethereum yellow paper). + +If any of these fails, a throwaway block will instead be created on L2 whose first transaction is `invalidateBlock`, and this will prove the proposed block as invalid on TaikoL1. diff --git a/packages/website/pages/docs/concepts/block-proposal-and-verification.md b/packages/website/pages/docs/concepts/overview.md similarity index 96% rename from packages/website/pages/docs/concepts/block-proposal-and-verification.md rename to packages/website/pages/docs/concepts/overview.md index 7820002f1f6..eccf4882efb 100644 --- a/packages/website/pages/docs/concepts/block-proposal-and-verification.md +++ b/packages/website/pages/docs/concepts/overview.md @@ -1,5 +1,3 @@ -# Block proposal & verification - Taiko aims to build a secure, decentralized, and permissionless rollup on Ethereum. These requirements dictate the following properties: 1. All block data required to reconstruct the post-block state needs to be put on Ethereum so it is publicly available. If this would not be the case, Taiko would not only fail to be a rollup but would also fail to be fully decentralized. This data is required so that anyone can know the latest chain state and so that useful new blocks can be appended to the chain. For the decentralization of the proof generation Taiko requires an even stronger requirement: all block data needed to be able to re-execute all work in a block in a step-by-step fashion needs to be made public. This makes it possible for provers to generate a proof for a block using only publicly known data. @@ -7,10 +5,10 @@ Taiko aims to build a secure, decentralized, and permissionless rollup on Ethere We achieve this by splitting the block submission process in two parts: -### Block proposal +## Block proposal When a block gets proposed the block data is published on Ethereum and the block is appended to the proposed blocks list stored in the [TaikoL1](/docs/reference/contract-documentation/L1/TaikoL1) contract. Once registered, the protocol ensures that _all_ block properties are immutable. This makes the block execution _deterministic_: the post-block state can now be calculated by anyone. As such, the block is immediately _verified_. This also ensures that no one knows more about the latest state than anyone else, as that would create an unfair advantage. -### Block verification +## Block verification Because the block should already be verified once proposed, it should _not_ be possible for the prover to have any impact on how the block is executed and what the post-block state is. All relevant inputs for the proof generation are verified on L1 directly or indirectly to achieve deterministic block transitions. As all proposed blocks are deterministic, they can be proven in parallel, because all intermediate states between blocks are known and unique. Once a proof is submitted for the block and its parent block, we call the block _on-chain verified_. diff --git a/packages/website/pages/docs/concepts/taiko-nodes.mdx b/packages/website/pages/docs/concepts/taiko-nodes.mdx new file mode 100644 index 00000000000..7f5c9a3e225 --- /dev/null +++ b/packages/website/pages/docs/concepts/taiko-nodes.mdx @@ -0,0 +1,99 @@ +# Overview +There are two parts to a Taiko node, which are connected over the engine API: +- `taiko-geth` +- `taiko-client` + +![taiko nodes diagram](/images/diagrams/taiko-nodes.png) + +## Taiko geth + +The [taiko-geth](https://github.com/taikoxyz/taiko-geth) repo is a fork of [go-ethereum](https://github.com/ethereum/go-ethereum) with some changes according to Taiko protocol, it serves as a L2 execution engine, which needs to be coupled to a consensus client (in Taiko network, this will be the taiko client's driver software), like L1 ethereum execution engines, it will listen to new L2 transactions broadcasted in the L2 network, executes them in EVM, and holds the latest state and database of all current L2 data. + +## Taiko client + +The compiled binary `bin/taiko-client` is the main entrypoint which includes three sub-commands: + +- `driver`: keep the L2 execution engine's chain in sync with the `TaikoL1` contract, by directing the L2 [execution engine](https://ethereum.org/en/glossary/#execution-client). +- `proposer`: propose new transactions from the L2 execution engine's transaction pool to the `TaikoL1` contract. +- `prover`: request ZK proofs from the zkEVM, and send transactions to prove the proposed blocks are valid or invalid. + +### Driver + +Taiko client's driver software serves as an L2 consensus client. It will listen for new L2 blocks from the Taiko layer 1 protocol contract, then direct the connected L2 execution engine to insert them into its local chain through the Engine API. + +#### Engine API + +Driver directs a L2 execution engine to insert new blocks or reorg the local chain through the [Engine API](https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md). + +#### Chain synchronization process + +> NOTE: The Taiko protocol allows a block's timestamp to be equal to its parent block's timestamp, which differs from the original Ethereum protocol. So it's fine that there are two `TaikoL1.proposeBlock` transactions included in one L1 block. + +Driver will inform the L2 execution engine Taiko protocol contract's latest verified L2 head, and try to let it catch up the latest verified L2 block through P2P at first. Driver will monitor the execution engine's sync progress, if it is not able to make any new sync progress in a period of time, driver will switch to insert the verified blocks to its local chain through the Engine API one by one. + +After the L2 execution engine catches up the latest verified L2 head, driver will subscribe to `TaikoL1.BlockProposed` events, and when a new pending block is proposed: + +1. Get the corresponding `TaikoL1.proposeBlock` L1 transaction. +2. Decode the txList and block metadata from the transaction's calldata. +3. Check whether the txList is valid based on the rules defined in Taiko protocol. + +If the txList is **valid**: + +4. Assemble a deterministic `TaikoL2.anchor` transaction based on the rules defined in the protocol, and put it as the first transaction in the proposed txList. +5. Use this txList and the decoded block metadata to assemble a deterministic L2 block. +6. Direct L2 execution engine to insert this assembled block and set it as the current canonical chain's head via the Engine API. + +If the txList is **invalid**: + +4. Create a `TaikoL2.invalidateBlock` transaction and then assemble a L2 block only including this transaction. +5. Direct the L2 execution engine to insert this block, but does not set it as the chain's head via the Engine API. + +> NOTE: For more detailed information about: block metadata, please see `5.2.2 Block Metadata` in the white paper. + +> NOTE: For more detailed information about txList validation rules, please see `5.3.1 Validation` in the white paper. + +> NOTE: For more detailed information about the `TaikoL2.anchor` transaction and proposed block's determination, please see `5.4.1 Construction of Anchor Transactions` in the white paper. + +### Proposer + +Taiko client's proposer software will fetch pending transactions in a L2 execution engine's mempool intervally, then try to propose them to the Taiko layer 1 protocol contract. + +#### Proposing strategy + +Since tokenomics have not been fully implemented in the Taiko protocol, the current proposing strategy is simply based on time interval. + +#### Proposing process + +Proposing a block involves a few steps: + +1. Fetch the pending transactions from the L2 execution engine through the `txpool_content` RPC method. +2. If there are too many pending transactions in the L2 execution engine, split them into several smaller txLists. This is because the Taiko protocol restricts the max size of each proposed txList. +3. Commit hashes of the txLists by sending `TaikoL1.commitBlock` transactions to L1. +4. Wait for `TaikoData.Config.commitConfirmations` (currently `0`) L1 blocks confirmations. +5. Propose all splitted txLists by sending `TaikoL1.proposeBlock` transactions. + +### Prover + +#### Proving strategy + +Since tokenomics have not been fully implemented in the Taiko protocol, the prover software currently proves all proposed blocks. + +#### Proving process + +When a new block is proposed: + +1. Get the `TaikoL1.proposeBlock` L1 transaction calldata, decode it, and validate the txList. Just like what the `driver` software does. +2. Wait until the corresponding block is inserted by the L2 execution engine's `driver` software. +3. Generate a ZK proof for that block asynchronously. + +If the proposed block has a valid txList: + +4. Generate the merkel proof of the block's `TaikoL2.anchor` transaction to prove its existence in the `block.txRoot`'s [MPT](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/), and also this transaction receipt's merkel proof in the `block.receiptRoot`'s MPT from the L2 execution engine. +5. Submit the `TaikoL2.anchor` transaction's RLP encoded bytes, its receipt's RLP encoded bytes, generated merkel proofs, and ZK proof to prove this block **valid**, by sending a `TaikoL1.proveBlock` transaction. + +If the proposed block has an invalid txList: + +4. Generate the merkel proof of the block's `TaikoL2.invalidateBlock` transaction receipt to prove its existence in the `block.receiptRoot`'s MPT from the L2 execution engine. +5. Submit the `TaikoL2.invalidateBlock` transaction receipt's RLP encoded bytes, generated merkel proof, and ZK proof to prove this block **invalid**, by sending a `TaikoL1.proveBlockInvalid` transaction. + +> NOTE: For more information about why we need these merkel proofs when proving, please see `5.5 Proving Blocks` in the white paper. diff --git a/packages/website/pages/docs/guides/configure-wallet.mdx b/packages/website/pages/docs/guides/configure-wallet.mdx index df7e1f27d51..8425413fe05 100644 --- a/packages/website/pages/docs/guides/configure-wallet.mdx +++ b/packages/website/pages/docs/guides/configure-wallet.mdx @@ -1,7 +1,5 @@ import AddEthereumChainButton from "../../../components/AddEthereumChainButton"; -## Summary - These steps will help you connect your wallet to the Taiko A1 testnet. There are two networks to configure your wallet with: **Ethereum A1** and **Taiko A1**. ## Prerequisites diff --git a/packages/website/pages/docs/guides/deploy-a-contract.md b/packages/website/pages/docs/guides/deploy-a-contract.md index 2c8f85978f1..32521862618 100644 --- a/packages/website/pages/docs/guides/deploy-a-contract.md +++ b/packages/website/pages/docs/guides/deploy-a-contract.md @@ -1,5 +1,3 @@ -## Summary - These steps will show you how to deploy a smart contract to Taiko A1 using Foundry. Read the [Foundry Book](https://book.getfoundry.sh/getting-started/first-steps) for the latest docs on Foundry. ## Prerequisites diff --git a/packages/website/pages/docs/guides/request-from-faucet.md b/packages/website/pages/docs/guides/request-from-faucet.md index e0eb02c226f..511b17fd26d 100644 --- a/packages/website/pages/docs/guides/request-from-faucet.md +++ b/packages/website/pages/docs/guides/request-from-faucet.md @@ -1,5 +1,3 @@ -## Summary - These steps will help you receive testnet tokens from the faucet. ## Prerequisites diff --git a/packages/website/pages/docs/guides/run-a-node.mdx b/packages/website/pages/docs/guides/run-a-node.mdx index 84ebfedd521..0bec6f53819 100644 --- a/packages/website/pages/docs/guides/run-a-node.mdx +++ b/packages/website/pages/docs/guides/run-a-node.mdx @@ -1,249 +1,116 @@ -import { Tab, Tabs } from 'nextra-theme-docs' - -## Summary +import { Callout, Steps, Tab, Tabs } from "nextra-theme-docs"; Taiko's network is fully decentralized and relies on the community to run nodes. This guide will walk you through the process of operating a Taiko node via [simple-taiko-node](https://github.com/taikoxyz/simple-taiko-node), the easiest way to get started operating a Taiko node. With simple-taiko-node you can: + - Run a Taiko node easily from the command line on Windows, Mac, and Linux. - Run a Taiko node regularly or as a prover. - View a [Grafana](https://grafana.com/) dashboard which displays the node's status. ## Prerequisites -- [Docker](https://docs.docker.com/engine/install/) is installed and running. +- [Docker](https://docs.docker.com/engine/install/) is installed and **running**. - [Git](https://github.com/git-guides/install-git/) is installed. -- Will need some ETH on the Layer 1 (L1) network. To receive Ethereum A1 ETH (L1), follow the Directions in [Request from faucet](/docs/guides/request-from-faucet). - -> Note: On Windows, Docker may also require step 4 and 5 if using the [WSL 2 backend](https://learn.microsoft.com/nl-nl/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package) - -### System requirements +- Should have some ETH on the Sepolia network (see [request from faucet](/docs/guides/request-from-faucet)) if running a prover. +- Because we use a fork of geth, you can consult the [geth minimum requirements](https://github.com/ethereum/go-ethereum#hardware-requirements), with the exception of 1TB of free space, as the Taiko rollup state is much smaller. + - If you are running a prover, 32GB of RAM and 8 cores is recommended. -Because we use a fork of geth, you can consult the [geth minimum requirements](https://github.com/ethereum/go-ethereum#hardware-requirements), which are outlined below. +## Steps -**Minimum:** + + ### Clone simple-taiko-node + ```sh + git clone https://github.com/taikoxyz/simple-taiko-node.git + cd simple-taiko-node + ``` -- CPU with 2+ cores -- 4GB RAM -- 1TB free storage space to sync the Mainnet - - (**only ~50GB for Testnet**) -- 8 MBit/sec download Internet service +### Configure your node as a prover (optional) -**Recommended:** +First, copy the `.env.sample` to a new file `.env`: -- Fast CPU with 4+ cores -- 16GB+ RAM -- High-performance SSD with at least 1TB of free space -- 25+ MBit/sec download Internet service +```sh +cp .env.sample .env +``` -## Steps +Then, open the `.env` file in your preferred text editor: -### Setup and Configure Node - - - - **Steps - Windows** - - 1. Open the git bash terminal - - - Start a git bash terminal by searching for git bash in the Start menu. - - Enter the following commands in the sections below. If copy and pasting does not work, please try pasting with SHIFT + INSERT , otherwise it will need to be typed out. - - 2. Clone this repository - - ```sh - git clone https://github.com/taikoxyz/simple-taiko-node.git - cd simple-taiko-node - ``` - - 3. Configure your node - - Copy `.env.sample` to `.env`. - - ```sh - cp .env.sample .env - ``` - - Run the following commands to assure the files work - - ```sh - dos2unix docker-compose.yml - cd script - dos2unix start-proposer.sh - ``` - - To go back to the node folder (/simple-taiko-node), use the following command - ```sh - cd .. - ``` - - 4. Configure node as proposer (optional) - - To run your node as a proposer, configure the optional environment variables in this `.env` file: - - The `.evn` file can be edited in Notepad by running the command. - ```sh - notepad .env - ``` - - - Set `ENABLE_PROPOSER` to `true` (replacing the default `false` with `true`). - - Set `L1_PROPOSER_PRIVATE_KEY` to that of your wallet's private key - - The wallet will need a A1 ETH balance on layer 1 to propose blocks. - - If using MetaMask, follow these directions to retrieve the [MetaMask Private Key](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). - - Set `L2_SUGGESTED_FEE_RECIPIENT` to the layer 2 address you want to receive the tx fees from the block. - - ```javascript - ENABLE_PROPOSER=true - L1_PROPOSER_PRIVATE_KEY=your private key from a test wallet - L2_SUGGESTED_FEE_RECIPIENT=your test wallets address (starts with 0x...) - ``` - - Finally, save the `.env` and start proposing by starting up your node again. - - - - **Steps - Mac** - - 1. Open the terminal - - - Start the Terminal. The Spotlight search field can be brought up with Command+Spacebar and search for the Terminal. - - Enter the following commands below into the Terminal - - 2. Clone this repository - - ```sh - git clone https://github.com/taikoxyz/simple-taiko-node.git - cd simple-taiko-node - ``` - - 3. Configure your node - - Copy `.env.sample` to `.env`. - - ```sh - cp .env.sample .env - ``` - - 4. Configure node as proposer (optional) - - To run your node as a proposer, configure the optional environment variables in this `.env` file: - - The `.evn` file can be edited in TextEdit by running the command. - ```sh - open .env - ``` - - - Set `ENABLE_PROPOSER` to `true` (replacing the default `false` with `true`). - - Set `L1_PROPOSER_PRIVATE_KEY` to that of your wallet's private key - - The wallet will need a A1 ETH balance on layer 1 to propose blocks. - - If using MetaMask, follow these directions to retrieve the [MetaMask Private Key](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). - - Set `L2_SUGGESTED_FEE_RECIPIENT` to the layer 2 address you want to receive the tx fees from the block. - - ```javascript - ENABLE_PROPOSER=true - L1_PROPOSER_PRIVATE_KEY=your private key from a test wallet - L2_SUGGESTED_FEE_RECIPIENT=your test wallets address (starts with 0x...) - ``` - - Finally, save the `.env` and start proposing by starting up your node again. - - - - **Steps - Linux** - - 1. Open the terminal. - - - This can be brought up by Pressing Ctrl+Alt+T - - Enter the following commands below into the Terminal - - 2. Clone this repository - - ```sh - git clone https://github.com/taikoxyz/simple-taiko-node.git - cd simple-taiko-node - ``` - - 3. Configure your node - - Copy `.env.sample` to `.env`. - - ```sh - cp .env.sample .env - ``` - - 4. Configure node as proposer (optional) - - To run your node as a proposer, configure the optional environment variables in this `.env` file: - - The `.evn` file can be edited in the terminal by running the command. - ```sh - nano .env - ``` - - - Set `ENABLE_PROPOSER` to `true` (replacing the default `false` with `true`). - - Set `L1_PROPOSER_PRIVATE_KEY` to that of your wallet's private key - - The wallet will need a A1 ETH balance on layer 1 to propose blocks. - - If using MetaMask, follow these directions to retrieve the [MetaMask Private Key](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). - - Set `L2_SUGGESTED_FEE_RECIPIENT` to the layer 2 address you want to receive the tx fees from the block. - - ```javascript - ENABLE_PROPOSER=true - L1_PROPOSER_PRIVATE_KEY=your private key from a test wallet - L2_SUGGESTED_FEE_RECIPIENT=your test wallets address (starts with 0x...) - ``` - - Finally, save the `.env` and start proposing by starting up your node again. - +{" "} + + ```sh nano .env ``` + ```sh vim .env ``` + ```sh notepad .env ``` +Finally, set the following environment variables to configure your node: + - Set `ENABLE_PROVER` to `true` (replacing the default `false` with `true`). + - Set `L1_PROVER_PRIVATE_KEY` to that of your wallet's private key; it will need some balance on Sepolia to prove blocks (if using MetaMask, follow these directions to [retrieve the private key](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key)). + ### Start a node + +If this is the first time starting the node, it may take some time to synchronize from the genesis block. You can monitor this progress through logs, or in the local grafana dashboard and see the latest L2 chain status in the [Taiko Alpha-2 L2 block explorer](https://l2explorer.a2.taiko.xyz/). + + +Make sure Docker is running and then run the following command to start the node. If you want to run it in the background, please add the `-d` flag (`docker compose up -d`). + ```sh docker compose up ``` -This command starts the configured node. If you want to run it in the background, please add the `-d` flag (`docker compose up -d`). - -If this is the first time you start the node, it will synchronize from the genesis to the present, which may take some time. You can monitor this progress through logs, or in the local grafana dashboard and see the latest L2 chain status in [Taiko Alpha-1 L2 block explorer](https://l2explorer.a1.taiko.xyz/). +To run the node in the background (detached mode), use the `-d` flag: - If you get an error that the `daemon docker is not running` or `Cannot connect to the Docker daemon`, you need to start the Docker Desktop by searching for the Docker Desktop in the Start menu (or Spotlight/Finder for Mac) and run. Then rerun the command in the terminal `docker compose up -d` +```sh +docker compose up -d +``` ### Stop a node +This command shuts down the node, but will keep all volumes, so next time you restart the node, it won't need to synchronize from the genesis again. + ```sh docker compose down ``` -This command shuts down the node, but will keep all volumes, so next time you restart the node, it won't need to synchronize from the genesis again. - ### View the node's logs To view the Docker logs, the following commands can be ran: -All Logs +**View all logs** + ```sh docker compose logs -f ``` -The proposer image's logs +**View the prover image's logs + ```sh -docker compose logs -f taiko_client_proposer +docker compose logs -f taiko_client_prover ``` -L2 execution engine Logs +**View the L2 execution engine logs** + ```sh docker compose logs -f l2_execution_engine ``` -Stats (CPU/MEM USAGE %) +**View the live data streams of your running containers (CPU/MEM USAGE %), and consumption of your machine's resources (add prefix "`docker stats -a`" to display all containers)** + ```sh docker stats ``` -This command will show you live data streams of your running containers (CPU/MEM USAGE %), and consumption of your machine's resources. Add prefix "`docker stats -a`" to display all containers. + ### View the node's status dashboard A [Grafana](https://grafana.com/) dashboard with a [Prometheus](https://prometheus.io/) datasource is also included to display the L2 execution engine's real time status. You can visit it at [http://localhost:3000/d/L2ExecutionEngine/l2-execution-engine-overview?orgId=1&refresh=10s](http://localhost:3000/d/L2ExecutionEngine/l2-execution-engine-overview?orgId=1&refresh=10s). -image +{" "} +image + + ## Troubleshooting @@ -260,46 +127,14 @@ These commands completely remove the node by removing all volumes used by each c When running a node it's normal for the node to run into errors. This doesn't mean that your node isn't working correctly, as most of the time the node resolves the errors. Some errors are there for the developers to easily debug if something goes wrong, but can be ignored by users. The following table explains some error messages a bit more. -| Error message | Explanation | -| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `Proposing operation error`

`error="failed to propose transactions: transaction reverted` | There are other proposers competing for the limited block space. The node will try again after a while. | -| `Unhandled trie error: missing trie node` | You can ignore this error, it doesn't affect you and goes away after a while. | -| `Block batch iterator callback error`

`error="failed to fetch L2 parent block: not found` | You can ignore this error. | -| `Proposing operation error`

`error="failed to propose transactions: insufficient funds for gas * price + value"` | Your L1 wallet has ran out of funds to cover the fees. Please add funds to L1. | -| `Error starting ...: listen tcp4 0.0.0.0:{port} bind: address already in use` | The port is already in use by another service. You can either shut down the other program or change the port in the .env file. | -| `error parsing HTTP 403 response body: invalid character '<' looking for beginning of value` | Your IP address is being geo-blocked due to sanctions lists. Google seems to be accidentally blocking users from Germany for some reason as well. If you're affected, try changing hosting locations or utilize a VPN to change your IP address. | -| `Skip an invalid proposed transaction: hash=2a0330..576dd2 reason="nonce too low: address` | This is a common error. This one is okay to ignore. | -| `ERROR: The Compose file './docker-compose.yml' is invalid because: Unsupported config option for some_serivce 'pull_policy'` | Your docker installation is out of date. You need to update your docker compose installation https://docs.docker.com/compose/install/ | -|`daemon docker is not running`

`Cannot connect to the Docker daemon` | Need to start the Docker Desktop. Search for the Docker Desktop in the Start menu (or Spotlight/Finder for Mac) and run. Then rerun the command in the terminal `docker compose up -d` | - - -**Node Not Proposing Blocks** - -First, check that you have updated the optional environment variables in `.env` file correctly and are using the latest docker images (you can manually update local images with `docker compose down && docker compose pull`). - -Next, check the proposer image's log (`docker compose logs -f taiko_client_proposer`) to figure out what could be wrong. It's probably because: - -- Your local node is still catching up with the latest chain head. -- Your L1 proposer account ran out of ETH (needed to propose tx's). Please refer to [Request from faucet](/docs/guides/request-from-faucet). -- There is no available block slot to propose in the Taiko L1 smart contract, so you must wait for one to become available (you can check the protocol smart contract's status with [`TaikoL1.getStateVariables`](https://taiko.xyz/docs/smart-contracts/L1/TaikoL1#getstatevariables)). - -If the above does not work, please try resyncing the images and start over - -```sh -docker compose down -v -git pull -docker compose pull -docker compose up -d -``` - -The commands below will redo *Configure node as proposer (optional)* (found in the section **Steps**-Setup and Configure Node). - -```sh -rm .env -cp .env.sample .env -update 3 lines in .env with true, a new private key and new account -docker compose up -d -``` +| Error message | Explanation | +| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Unhandled trie error: missing trie node` | You can ignore this error, it doesn't affect you and goes away after a while. | +| `Block batch iterator callback error`

`error="failed to fetch L2 parent block: not found` | You can ignore this error. | +| `Error starting ...: listen tcp4 0.0.0.0:{port} bind: address already in use` | The port is already in use by another service. You can either shut down the other program or change the port in the .env file. | +| `error parsing HTTP 403 response body: invalid character '<' looking for beginning of value` | Your IP address is being geo-blocked due to sanctions lists. If you're affected, try changing hosting locations or utilize a VPN to change your IP address. | +| `ERROR: The Compose file './docker-compose.yml' is invalid because: Unsupported config option for some_serivce 'pull_policy'` | Your docker installation is out of date. You need to update your docker compose installation https://docs.docker.com/compose/install/ | +| `daemon docker is not running`

`Cannot connect to the Docker daemon` | Need to start the Docker Desktop. Search for the Docker Desktop in the Start menu (or Spotlight/Finder for Mac) and run. Then rerun the command in the terminal `docker compose up -d` | **Unclean Shutdown** @@ -307,15 +142,11 @@ When running a Node, you will need to ensure a clean shutdown by running the com If the node is being ran with `docker compose up` (without the `-d` flag) and the logs are displaying in the terminal, then Press CTRL+C to allow Docker to clean up the resources. -Please try running the following +Please try running the following: + ```sh docker compose down -v git pull docker compose pull docker compose up -d ``` - -## Community guides - -- Video instructions [How to run a Taiko node on Windows](https://www.youtube.com/watch?v=JuvtSGoRw1I) -- Video instructions [How to run a Taiko node on Linux](https://www.youtube.com/watch?v=ZKH58vM8czc) \ No newline at end of file diff --git a/packages/website/pages/docs/guides/use-the-bridge.mdx b/packages/website/pages/docs/guides/use-the-bridge.mdx index 0d02e0362d5..9cf82c07215 100644 --- a/packages/website/pages/docs/guides/use-the-bridge.mdx +++ b/packages/website/pages/docs/guides/use-the-bridge.mdx @@ -1,7 +1,5 @@ import { Callout } from 'nextra-theme-docs' -## Summary - The bridge is currently not deployed on the Taiko hackathon testnet, but you can build bridges with the Signal Service cross-chain messaging. diff --git a/packages/website/pages/docs/reference/_meta.json b/packages/website/pages/docs/reference/_meta.json index 50b835c98d2..ea683c92d3e 100644 --- a/packages/website/pages/docs/reference/_meta.json +++ b/packages/website/pages/docs/reference/_meta.json @@ -5,6 +5,9 @@ "contract-addresses": { "title": "Contract addresses" }, + "protocol-constants": { + "title": "Protocol constants" + }, "rpc-configuration": { "title": "RPC configuration" } diff --git a/packages/website/pages/docs/reference/protocol-constants.mdx b/packages/website/pages/docs/reference/protocol-constants.mdx new file mode 100644 index 00000000000..46428bc1f6b --- /dev/null +++ b/packages/website/pages/docs/reference/protocol-constants.mdx @@ -0,0 +1,29 @@ +import { Callout } from "nextra-theme-docs"; + + + These protocol constants are not finalized and might not represent the current + state, as they are frequently tweaked. + + +## Protocol Constants + +| Key | Value | +| ----------------------------- | --------------------------------------------------------------------------------------------------- | +| $K_{ChainID}$ | Taiko's chain ID. | +| $K_{MaxNumBlocks}$ | The maximum number of slots for proposed blocks. | +| $K_{MaxVerificationsPerTx}$ | The number of proven blocks that can be verified when a new block is proposed or a block is proven. | +| $K_{MaxProofsPerForkChoice}$ | The maximum number of proofs per fork choice. | +| $K_{BlockMaxGasLimit}$ | A Taiko block’s max gas limit besides $K_{AnchorTxGasLimit}$. | +| $K_{BlockMaxTxs}$ | The maximum number of transactions in a Taiko block besides the anchor transaction. | +| $K_{BlockDeadEndHash}$ | A special value to mark blocks proven invalid. | +| $K_{TxListMaxBytes}$ | A txList’s maximum number of bytes. | +| $K_{TxMinGasLimit}$ | A transaction’s minimum gas limit. | +| $K_{AnchorTxGasLimit}$ | Anchor transaction’s fixed gas limit. | +| $K_{GracePeriod}$ | Fees and rewards grace period multiplier. | +| $K_{MaxPeriod}$ | Fees and rewards max period multiplier. | +| $K_{RewardMultiplier}$ | The max reward multiplier for proofs. | +| $K_{AnchorTxSelector}$ | `0xa0ca2d08` | +| $K_{GoldenTouchAddress}$ | `0x0000777735367b36bC9B61C50022d9D0700dB4Ec` | +| $K_{GoldenTouchPrivateKey}$ | `0x92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38` | +| $K_{InvalidateBlockLogTopic}$ | `0x64b299ff9f8ba674288abb53380419048a4271dda03b837ecba6b40e6ddea4a2` | +| $K_{EmptyOmmersHash}$ | `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` | diff --git a/packages/website/public/images/diagrams/taiko-nodes.png b/packages/website/public/images/diagrams/taiko-nodes.png new file mode 100644 index 0000000000000000000000000000000000000000..6e9436fee80e96fe1b3be17e8f6de729f432b7ef GIT binary patch literal 49569 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 O{3yt%NS8{!2>d^I;Lu0_ literal 0 HcmV?d00001