From 550131c1d9baa0c4dae7b4ecb0053aec2aecf5bb Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 21 Nov 2024 14:54:56 -0600 Subject: [PATCH 01/75] reorganized how-arbitrum-works section --- .gitignore | 1 + .../01-a-gentle-introduction.mdx | 99 +++ ...cycle.mdx => 02-transaction-lifecycle.mdx} | 0 .../l1-l2-messaging.mdx => 03-sequencer.mdx} | 142 +++- .../04-state-transition-function.mdx | 62 ++ .../how-arbitrum-works/05-validation.mdx | 295 ++++++++ .../how-arbitrum-works/06-challenges.mdx | 150 ++++ .../07-anytrust-protocol.mdx | 147 ++++ .../how-arbitrum-works/08-gas-fees.mdx | 147 ++++ ...{why-nitro.mdx => 09-nitro-vs-classic.mdx} | 0 .../arbos/l2-l1-messaging.mdx | 27 - .../how-arbitrum-works/assertion-tree.mdx | 33 - arbitrum-docs/how-arbitrum-works/gas-fees.mdx | 36 - .../how-arbitrum-works/inside-anytrust.mdx | 71 -- .../inside-arbitrum-nitro.mdx | 677 ------------------ .../how-arbitrum-works/l1-gas-pricing.mdx | 60 -- .../how-arbitrum-works/sequencer.mdx | 34 - arbitrum-sdk | 2 +- 18 files changed, 1028 insertions(+), 955 deletions(-) create mode 100644 arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx rename arbitrum-docs/how-arbitrum-works/{tx-lifecycle.mdx => 02-transaction-lifecycle.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{arbos/l1-l2-messaging.mdx => 03-sequencer.mdx} (55%) create mode 100644 arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx create mode 100644 arbitrum-docs/how-arbitrum-works/05-validation.mdx create mode 100644 arbitrum-docs/how-arbitrum-works/06-challenges.mdx create mode 100644 arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx create mode 100644 arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx rename arbitrum-docs/how-arbitrum-works/{why-nitro.mdx => 09-nitro-vs-classic.mdx} (100%) delete mode 100644 arbitrum-docs/how-arbitrum-works/arbos/l2-l1-messaging.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/assertion-tree.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/gas-fees.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/inside-anytrust.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/l1-gas-pricing.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/sequencer.mdx diff --git a/.gitignore b/.gitignore index d5c300a76..83be1e7d7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ arbitrum-docs/sdk arbitrum-docs/stylus-by-example .vercel +.DS_Store diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx new file mode 100644 index 000000000..5f2fe8d1a --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -0,0 +1,99 @@ +--- +title: 'Inside Arbitrum Nitro' +sidebar_label: 'Deep dive: Inside Arbitrum' +description: 'Learn the fundamentals of Nitro, Arbitrum stack.' +author: dzgoldman +sme: dzgoldman +user_story: As a current or prospective Arbitrum user, I need learn more about Nitros design. +content_type: get-started +--- + +import ImageWithCaption from '@site/src/components/ImageCaptions/'; + +This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. + +The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. + + + + +## Why use Arbitrum? Why use Nitro? + +Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: + +- Trustless security: security rooted in Ethereum, with any one party able to ensure correct Layer 2 results +- Compatibility with Ethereum: able to run unmodified EVM contracts and unmodified Ethereum transactions +- Scalability: moving contracts’ computation and storage off of the main Ethereum chain, allowing much higher throughput +- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-transaction cost. + +Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. + +Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in several ways: + +- **Advanced Calldata Compression,** which further drives down transaction costs on Arbitrum by reducing the amount of data posted to L1. +- **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. +- **Ethereum L1 Gas Compatibility,** bringing pricing and accounting for EVM operations perfectly in line with Ethereum. +- **Additional L1 Interoperability,** including tighter synchronization with L1 Block numbers, and full support for all Ethereum L1 precompiles. +- **Safe Retryables,** eliminating the failure mode where a retryable ticket fails to get created. +- **Geth Tracing,** for even broader debugging support. +- And many, many more changes. + + + + +## The Big Picture + +At the most basic level, an Arbitrum chain works like this: + + + +Users and contracts put messages into the inbox. The chain reads the messages one at a time, and processes each one. This updates the state of the chain and produces some outputs. + +If you want an Arbitrum chain to process a transaction for you, you need to put that transaction into the chain’s inbox. Then the chain will see your transaction, execute it, and produce some outputs: a transaction receipt, and any withdrawals that your transaction initiated. + +Execution is deterministic -- which means that the chain’s behavior is uniquely determined by the contents of its inbox. Because of this, the result of your transaction is knowable as soon as your transaction has been put in the inbox. Any Arbitrum node will be able to tell you the result. (And you can run an Arbitrum node yourself if you want.) + +All of the technical detail in this document is connected to this diagram. To get from this diagram to a full description of Arbitrum, we’ll need to answer questions like these: + +- Who keeps track of the inbox, chain state, and outputs? +- How does Arbitrum make sure that the chain state and outputs are correct? +- How can Ethereum users and contracts interact with Arbitrum? +- How does Arbitrum support Ethereum-compatible contracts and transactions? +- How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? +- How can I run my own Arbitrum node or validator? + + + + +## Nitro's Design: The Four Big Ideas + +The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. + +**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic state transition function. + +**Big Idea: Geth at the Core**: Nitro supports Ethereum's data structures, formats, and virtual machine by compiling in the core code of the popular go-ethereum ("Geth") Ethereum node software. Using Geth as a library in this way ensures a very high degree of compatibility with Ethereum. + +**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. + +**Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. + +## Sequencing, Followed by Deterministic Execution + +This diagram summarizes how transactions are processed in Nitro. + +![seq-then-exec](../assets/seq-then-exec.png) + +Let's follow a user's transaction through this process. + +First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. + +Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. + +Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) + +The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. + +It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/tx-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/tx-lifecycle.mdx rename to arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx diff --git a/arbitrum-docs/how-arbitrum-works/arbos/l1-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx similarity index 55% rename from arbitrum-docs/how-arbitrum-works/arbos/l1-l2-messaging.mdx rename to arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index c7cf4e50c..64205f57d 100644 --- a/arbitrum-docs/how-arbitrum-works/arbos/l1-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -1,20 +1,60 @@ ---- -title: 'L1 to L2 messaging' -description: This concept page provides information about how arbitrary messages are passed from L1 to L2 -target_audience: developers who want to build on Arbitrum -content_type: concept ---- - -import { AddressAliasHelper } from '@site/src/components/AddressAliasHelper'; -import { - MermaidWithHtml, - Nodes, - Node, - Connection, - NodeDescriptions, - NodeDescription, -} from '/src/components/MermaidWithHtml/MermaidWithHtml'; +# The Sequencer and Censorship Resistance +The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. + +Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. + + + + +## The Core Inbox + +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. + + + + + +## Bridging + +We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. + +The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). + +### L1 contracts can submit L2 transactions + +An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. + +The advantage of this method is that it is simple and has relatively low latency. The disadvantage, compared to the other method we’ll describe soon, is that the L2 transaction might revert if the L1 caller doesn’t get the L2 gas price and max gas amount right. Because the L1 caller can’t see the result of its L2 transaction, it can’t be absolutely sure that its L2 transaction will succeed. + +This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. + +### L1 to L2 ticket-based transactions + +Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. + +When saving a transaction for retry, Nitro records the sender’s address, destination address, callvalue, and calldata. All of this is saved, and the callvalue is deducted from the sender’s account and (logically) attached to the saved transaction. + +If the redemption succeeds, the transaction is done, a receipt is issued for it, and the ticketID is canceled and can’t be used again. If the redemption fails, for example because the packaged transaction fails, the redemption reports failure and the ticketID remains available for redemption. + +Normally the original submitter will try to cause their transaction to succeed immediately, so it never needs to be recorded or retried. As an example, our "token deposit" use case above should, in the happy, common case, still only require a single signature from the user. If this initial execution fails, the ticketID will still exist as a backstop which others can redeem later. + +Submitting a transaction in this way carries a price in ETH which the submitter must pay, which varies based on the calldata size of the transaction. Once submitted, the ticket is valid for about a week. If the ticket has not been redeemed in that period, it is deleted. + +When the ticket is redeemed, the pre-packaged transaction runs with sender and origin equal to the original submitter, and with the destination, callvalue, and calldata the submitter provided at the time of submission. + +This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. + +### L2 to L1 ticket-based calls + +Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. + +These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) + + + + + ## Retryable Tickets Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. @@ -242,3 +282,73 @@ modifier onlyFromMyL1Contract() override { The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). + + + + + +Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. + +### Protocol Flow + +Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. + +### Client Flow + +From a client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. + +### Protocol Design Details + +An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock confirmation processed can't be griefed. + +Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. + +Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. +The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) + +\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. + + + + +## Happy/Common Case: Sequencer Is Live and Well-behaved + +Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. + +If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. +Once posted in a batch, the transactions have L1-level finality. + +Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. + +In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. + + + + +## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job + +Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: + +First, they submit their L2 message via L1 into the delayed Inbox as described above: note that although atomic cross-chain messages are the common case for using the delayed Inbox, it can in principle be used to submit _any_ L2 message. + +Once in the delayed Inbox, we obviously can’t rely on the Sequencer to include the transaction in a batch. Instead, we can use `SequencerInbox`’s `forceInclusion` method. Once a message has been in the delayed Inbox for a sufficient amount of time, `forceInclusion` can be called to move it from the delayed Inbox into the core Inbox, at which point it’s finalized. Crucially, any account can call `forceInclusion`. + +Currently, on Arbitrum One, this delay time between submission and force inclusion is roughly @arbOneForceIncludePeriodHours@ hours, as specified by `maxTimeVariation.delayBlocks` / `maxTimeVariation.delaySeconds`. A force inclusion from L1 would directly affect the state for any unconfirmed L2 transactions; keeping conservatively high delay value ensures it should only be used under extraordinary circumstances. + +On top of the delay itself, the `forceInclusion` path has the downside of uncertainty around transaction ordering; i.e., while waiting for a message's max delay to pass, a malicious Sequencer could, in principle, directly post messages in front of it. However, there’s ultimately nothing the Sequencer can do to stop it from being included in the core Inbox, at which point its ordering is finalized. + +While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. + + + +### How the Sequencer Publishes the Sequence + +So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. + +The real-time feed is published by the Sequencer so that anyone who subscribes to the feed receives instant notifications of each transaction as it is sequenced. Nitro nodes can subscribe to the feed directly from the Sequencer, or through a relay that forwards the feed. The feed represents the Sequencer's promise that it will record transactions in a particular order. If the Sequencer is honest and doesn't have a long downtime, this promise will be kept. So anyone who trusts the Sequencer to keep its promises can rely on the feed to get instant information about the transaction sequence--and they can run the sequenced transactions through the state transition function to learn the results of each transaction immediately. This is "soft finality" for transactions; it's "soft" because it depends on the Sequencer keeping its promises. + +The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically--perhaps every few minutes in production--the Sequencer concatenates the next group of transactions in the feed, compresses them for efficiency, and posts the result as calldata on Ethereum. This is the final and official record of the transaction sequence. As soon as this Ethereum transaction has finality on Ethereum, the Layer 2 Nitro transactions it records will have finality. These transactions are final because their position in the sequence has finality, and the outcome of the transactions is deterministic and knowable to any party. This is "hard finality". + +The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. + + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx new file mode 100644 index 000000000..1a6ec991b --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx @@ -0,0 +1,62 @@ + +## Geth at the Core + +The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. + +![geth-sandwich](../assets/geth-sandwich.png) + +The software that makes up a Nitro node can be thought of as built in three main layers, which are shown above: + +- The base layer is the core of geth--the parts of Geth that emulate the execution of EVM contracts and maintain the data structures that make up the Ethereum state. Nitro compiles in this code as a library, with a few minor modifications to add necessary hooks. +- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. +- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible blockchain node. + +Because the top and bottom layers rely heavily on code from geth, this structure has been dubbed a "geth sandwich." Strictly speaking, Geth plays the role of the bread in the sandwich, and ArbOS is the filling, but this sandwich is named for the bread. + +The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. + + + + +## Separating Execution from Proving + +One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. + +When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) + +Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. + + + + +#### WAVM + +The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. + +WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. + +Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. + +Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. + + + + +#### ReadPreImage and the Hash Oracle Trick + +The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. + +(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) + +As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. + +The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. + +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. + + + + + + + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/05-validation.mdx b/arbitrum-docs/how-arbitrum-works/05-validation.mdx new file mode 100644 index 000000000..16b9802f4 --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/05-validation.mdx @@ -0,0 +1,295 @@ + +## Optimistic Rollup + +Arbitrum is an optimistic rollup. Let’s unpack that term. + +_Rollup_ + +Arbitrum is a rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. + +This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. + +_Optimistic_ + +Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to challenge that claim. If the challenge period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). + +Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. + + + + +## Resolving disputes using interactive fraud proofs + +Among optimistic rollups, the most important design decision is how to resolve disputes. Suppose Alice claims that the chain will produce a certain result, and Bob disagrees. How will the protocol decide which version to accept? + +There are basically two choices: interactive proving, or re-executing transactions. Arbitrum uses interactive proving, which we believe is more efficient and more flexible. Much of the design of Arbitrum follows from this fact. + + + + +### Interactive proving + +The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. + +Arbitrum's approach is based on dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. + +The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. + + + + +### Re-executing transactions + +The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. + + + + +### Why interactive proving is better + +We believe strongly that interactive proving is the superior approach, for the following reasons. + +**More efficient in the optimistic case**: Because interactive proving can resolve disputes that are larger than one transaction, it can allow a rollup block to contain only a single claim about the end state of the chain after all of the execution covered by the block. By contrast, reexecution requires posting a state claim for each transaction within the rollup block. With hundred or thousands of transactions per rollup block, this is a substantial difference in L1 footprint -- and L1 footprint is the main component of cost. + +**More efficient in the pessimistic case**: In case of a dispute, interactive proving requires the L1 referee contract only to check that Alice and Bob's actions "have the right shape", for example, that Alice has divided her N-step claim into two claims half as large. (The referee doesn't need to evaluate the correctness of Alice's claims--Bob does that, off-chain.) Only one instruction needs to be reexecuted. By contrast, reexecution requires the L1 referee to emulate the execution of an entire transaction. + +**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. + +**More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. + + + + +### Interactive proving drives the design of Arbitrum + +Much of the design of Arbitrum is driven by the opportunities opened up by interactive proving. If you're reading about some feature of Arbitrum, and you're wondering why it exists, two good questions to ask are: "How does this support interactive proving?" and "How does this take advantage of interactive proving?" The answers to most "why" questions about Arbitrum relate to interactive proving. + + + + +## Arbitrum Rollup Protocol + +Before diving into the rollup protocol, there are two things we need to cover. + +First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. + +You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! + +The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) + +You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. + +With those preliminaries behind us, let’s jump into the details of the rollup protocol. + +The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). + +The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. + + + + +### The Rollup Chain + +The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. + +Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. + +:::note + +Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and chain state, whereas proposers can also assert and challenge the chain state. + +::: + +Each RBlock contains: + +- the RBlock number +- the predecessor RBlock number: RBlock number of the last RBlock before this one that is (claimed to be) correct +- the number of L2 blocks that have been created in the chain's history +- the number of inbox messages that have been consumed in the chain’s history +- a hash of the outputs produced over the chain’s history. + +Except for the RBlock number, the contents of the RBlock are all just claims by the RBlock's proposer. Arbitrum doesn’t know at first whether any of these fields are correct. If all of these fields are correct, the protocol should eventually confirm the RBlock. If one or more of these fields are incorrect, the protocol should eventually reject the RBlock. + +An RBlock is implicitly claiming that its predecessor RBlock is correct. This implies, transitively, that an RBlock implicitly claims the correctness of a complete history of the chain: a sequence of ancestor RBlocks that reaches all the way back to the birth of the chain. + +An RBlock is also implicitly claiming that its older siblings (older RBlocks with the same predecessor), if there are any, are incorrect. If two RBlocks are siblings, and the older sibling is correct, then the younger sibling is considered incorrect, even if everything else in the younger sibling is true. + +The RBlock is assigned a deadline, which says how long other validators have to respond to it. If you’re a validator, and you agree that an RBlock is correct, you don’t need to do anything. If you disagree with an RBlock, you can post another RBlock with a different result, and you’ll probably end up in a challenge against the first RBlock's bonder. (More on challenges below.) + +In the normal case, the rollup chain will look like this: + +```mermaid +graph RL + f["First unresolved block"] + l["Latest confirmed block"] + + 98-->97-->96-->95 + f-.-95 + + 95-->94 + l-.-94 + + 94-->93-->92-->91-->90-->x[...] + + subgraph Legend + direction RL + Confirmed + Unconfirmed + end + + classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff + class 94,93,92,91,90,x,Confirmed confirmed + + + classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff + class 98,97,96,95,Unconfirmed unconfirmed + + classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 + class l,f note +``` + +On the left, representing an earlier part of the chain’s history, we have confirmed RBlocks. These have been fully accepted and recorded by the Layer 1 contracts that manage the chain. The newest of the confirmed RBlocks, RBlock 94, is called the “latest confirmed RBlock.” On the right, we see a set of newer proposed RBlocks. The protocol can’t yet confirm or reject them, because their deadlines haven’t run out yet. The oldest RBlock whose fate has yet to be determined, RBlock 95, is called the “first unresolved RBlock.” + +Notice that a proposed RBlock can build on an earlier proposed RBlock. This allows validators to continue proposing RBlocks without needing to wait for the protocol to confirm the previous one. Normally, all of the proposed RBlocks will be valid, so they will all eventually be accepted. + +Here’s another example of what the chain state might look like, if several validators are being malicious. It’s a contrived example, designed to illustrate a variety of cases that can come up in the protocol, all smashed into a single scenario. + +```mermaid +graph RL + subgraph Legend + direction RL + Confirmed + Rejected + Unconfirmed + end + + f["First unresolved block"] + l["Latest confirmed block"] + + + l-.-103 + f-.-106 + + 108-->107-->106-->103 + 111-->104 + 101-->100 + 105-->104-->103 + 110-->109-->103-->102-->100-->x[...] + + classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff + class 100,102,103,x,Confirmed confirmed + + classDef rejected fill:#fdaa07,stroke:#fd8607,stroke-width:2px,color:#fff + class 101,104,105,Rejected rejected + + classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff + class 106,107,108,109,110,111,Unconfirmed unconfirmed + + classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 + class l,f note + +``` + +There’s a lot going on here, so let’s unpack it. + +- RBlock 100 has been confirmed. +- RBlock 101 claimed to be a correct successor to RBlock 100, but 101 was rejected (hence it is orange). +- RBlock 102 was eventually confirmed as the correct successor to 100. +- RBlock 103 was confirmed and is now the latest confirmed RBlock. +- RBlock 104 was proposed as a successor to RBlock 103, and 105 was proposed as a successor to 104. 104 was rejected as incorrect, and as a consequence 105 was rejected because its predecessor was rejected. +- RBlock 106 is unresolved. It claims to be a correct successor to RBlock 103 but the protocol hasn’t yet decided whether to confirm or reject it. It is the first unresolved RBlock. +- RBlocks 107 and 108 claim to chain from 106. They are also unresolved. If 106 is rejected, they will be automatically rejected too. +- RBlock 109 disagrees with RBlock 106, because they both claim the same predecessor. At least one of them will eventually be rejected, but the protocol hasn’t yet resolved them. +- RBlock 110 claims to follow 109. It is unresolved. If 109 is rejected, 110 will be automatically rejected too. +- RBlock 111 claims to follow 104. 111 will inevitably be rejected because its predecessor has already been rejected. But it hasn’t been rejected yet, because the protocol resolves RBlocks in RBlock number order, so the protocol will have to resolve 106 through 110, in order, before it can resolve 111. After 110 has been resolved, 111 can be rejected immediately. + +Again: this sort of thing is very unlikely in practice. In this diagram, at least four parties must have bonded on wrong RBlocks, and when the dust settles at least four parties will have lost their bonds. The protocol handles these cases correctly, of course, but they’re rare corner cases. This diagram is designed to illustrate the variety of situations that are possible in principle, and how the protocol would deal with them. + + + + +### Staking + +At any given time, some validators will be bonders, and some will not. Bonders deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the bonder loses a challenge. Nitro chains accept bonds in ETH. + +A single bond can cover a chain of RBlocks. Every bonder is bonded on the latest confirmed RBlock; and if you’re bonded on an RBlock, you can also bond on one successor of that RBlock. So you might be bonded on a sequence of RBlocks that represent a single coherent claim about the correct history of the chain. A single bond suffices to commit you to that sequence of RBlocks. + +In order to create a new RBlock, you must be a bonder, and you must already be bonded on the predecessor of the new RBlock you’re creating. The bond requirement for RBlock creation ensures that anyone who creates a new RBlock has something to lose if that RBlock is eventually rejected. + +The protocol keeps track of the current required bond amount. Normally this will equal the base bond amount, which is a parameter of the Nitro chain. But if the chain has been slow to make progress lately, the required bond will increase, as described in more detail below. + +The rules for staking are as follows: + +- If you’re not bonded, you can bond on the latest confirmed RBlock. When doing this, you deposit the current minimum bond amount. +- If you’re bonded on an RBlock, you can also add your bond to any one successor of that RBlock. (The protocol tracks the maximum RBlock number you’re bonded on, and lets you add your bond to any successor of that RBlock, updating your maximum to that successor.) This doesn’t require you to place a new bond. + - A special case of adding your bond to a successor RBlock is when you create a new RBlock as a successor to an RBlock you’re already bonded on. +- If you’re bonded only on the latest confirmed RBlock (and possibly earlier RBlocks), you or anyone else can ask to have your bond refunded. Your bonded funds will be returned to you, and you will no longer be a bonder. +- If you lose a challenge, your bond is removed from all RBlocks and you forfeit your bonded funds. + +Notice that once you are bonded on an RBlock, there is no way to unbond. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your bond. The only way to get your bond back is to wait until all of the RBlocks you are bonded on are confirmed. + + + + +#### Setting the current minimum bond amount + +One detail we deferred earlier is how the current minimum bond amount is set. Normally, this is just equal to the base bond amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the bond requirement will escalate temporarily. Specifically, the base bond amount is multiplied by a factor that is exponential in the time since the deadline of the first unresolved RBlock passed. This ensures that if malicious parties are placing false bonds to try to delay progress (despite the fact that they’re losing those bonds), the bond requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the bond requirement will go back down. + + + + +### Rules for Confirming or Rejecting RBlocks + +The rules for resolving RBlocks are fairly simple. + +The first unresolved RBlock can be confirmed if: + +- the RBlock's predecessor is the latest confirmed RBlock, and +- the RBlock's deadline has passed, and +- there is at least one bonder, and +- All bonders are bonded to the RBlock. + +The first unresolved RBlock can be rejected if: + +- the RBlock's predecessor has been rejected, or +- all of the following are true: + - the RBlock's deadline has passed, and + - there is at least one bonder, and + - no bonder is bonded on the RBlock. + +A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. + + + + + +# The Assertion Tree + +### Overview + +The state of an Arbitrum chain is confirmed back on Ethereum via "assertions," aka "disputable assertions" or "DAs." These are claims made by Arbitrum validators about the chain's state. To make an assertion, a validator must post a bond in Ether. + +In the happy / common case, all outstanding assertions will be valid; i.e., a valid assertion will build on another valid assertion, which builds on another valid assertion, and so on. After the dispute period (~ 1 week) passes and an assertion goes unchallenged, it can be confirmed back on L1. + +If, however, two or more conflicting assertions exist, the Assertion Tree bifurcates into multiple branches: + +![img](../assets/assertionTree.png) + +Crucially, the rules of advancing an Arbitrum chain are deterministic; this means that given a chain state and some new inputs, there is only one valid output. Thus, if the Assertion Tree contains more than one leaf, then at most only one leaf can represent the valid chain-state; if we assume there is at least one honest active validator, _exactly_ one leaf will be valid. + +Two conflicting assertions can be put into a dispute; see [Interactive Challenges](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx) for details on the dispute process. For the sake of understanding the Assertion Tree protocol, suffice it to say that 2-party disputes last at most a fixed amount of time (1 week), at the end of which one of the two conflicting assertions will be rejected, and the validator who posted it will lose their stake. + +In order for an assertion to be confirmed and for its stake to be recovered, two conditions must be met: sufficient time for disputes must have passed, and no other conflicting branches in the Assertion Tree can exist (i.e., they've all been disputed / "pruned" off.) + +These properties together ensure that as long as at least one honest, active validator exists, the valid chain state will ultimately be confirmed. + +### Delays + +Even if the Assertion Tree has multiple conflicting leaves and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid leaf (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. + +The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. + +### Detailed Spec + +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum#rollup#protocol). + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx new file mode 100644 index 000000000..cd4c0400a --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx @@ -0,0 +1,150 @@ + +## Challenges + +Suppose the rollup chain looks like this: + +![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) + +RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. + +At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) + +Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. + +The challenge is a game in which Alice and Bob alternate moves, with an Ethereum contract as the referee. Alice, the defender, moves first. + +The game will operate in two phases: dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. + +We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. + +### Dissection Protocol: Simplified Version + +Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. + +Alice’s first move requires her to dissect her claims about intermediate states between the beginning (0 instructions executed) and the end (N instructions executed). So we require Alice to divide her claim in half, and post the state at the half-way point, after N/2 instructions have been executed. + +Now Alice has effectively bisected her N-step assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. + +At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. + +### Why Dissection Correctly Identifies a Cheater + +Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. + +To prove (1), observe that if Alice’s initial claim is correct, she can offer a truthful midpoint claim, and both of the implied half-size claims will be correct. So whichever half Bob objects to, Alice will again be in the position of defending a correct claim. At each stage of the protocol, Alice will be defending a correct claim. At the end, Alice will have a correct one-step claim to prove, so that claim will be provable and Alice can win the challenge. + +To prove (2), observe that if Alice’s initial claim is incorrect, this can only be because her claimed endpoint after N steps is incorrect. Now when Alice offers her midpoint state claim, that midpoint claim is either correct or incorrect. If it’s incorrect, then Bob can challenge Alice’s first-half claim, which will be incorrect. If Alice’s midpoint state claim is correct, then her second-half claim must be incorrect, so Bob can challenge that. So whatever Alice does, Bob will be able to challenge an incorrect half-size claim. At each stage of the protocol, Bob can identify an incorrect claim to challenge. At the end, Alice will have an incorrect one-step claim to prove, which she will be unable to do, so Bob can win the challenge. + +(If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) + +### The Real Dissection Protocol + +The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. + +**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). + +**K-way dissection:** Rather than dividing a claim into two segments of size N/2, we divide it into K segments of size N/K. This requires posting K-1 intermediate claims, at points evenly spaced through the claimed execution. This reduces the number of rounds by a factor of log(K)/log(2). + +**Answer a dissection with a dissection:** Rather than having each round of the protocol require two moves, where Alice dissects and Bob chooses a segment to challenge, we instead require Bob, in challenging a segment, to post his own claimed endpoint state for that segment (which must differ from Alice’s) as well as his own dissection of his version of the segment. Alice will then respond by identifying a subsegment, posting an alternative endpoint for that segment, and dissecting it. This reduces the number of moves in the game by an additional factor of 2, because the size is cut by a factor of K for every move, rather than for every two moves. + +**Deal With the Empty-Inbox Case**: The real AVM can’t always execute N units of gas without getting stuck. The machine might halt, or it might have to wait because its inbox is exhausted so it can’t go on until more messages arrive. So Bob must be allowed to respond to Alice’s claim of N units of execution by claiming that N steps are not possible. The real protocol thus allows any response (but not the initial claim) to claim a special end state that means essentially that the specified amount of execution is not possible under the current conditions. + +**Time Limits:** Each player is given a time allowance. The total time a player uses for all of their moves must be less than the time allowance, or they lose the game. Think of the time allowance as being about a week. + +It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. + +### Efficiency + +The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. + +The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. + +## Validators + +Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. + +Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. + +Offchain Labs provides open source validator software, including a pre-built Docker image. + +Every validator can choose their own approach, but we expect validators to follow three common strategies: + +- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. +- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. +- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) + +Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. + +The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). + +Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. + +- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. +- Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. +- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. + +## ArbOS + +ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. + +### Why ArbOS? + +In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. + +Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. + +## Full Nodes + +As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. + +Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. + +## The Sequencer + +The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. + +Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. + +[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. + +### Instant confirmation + +Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. + +The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. + +### Inboxes, fast and slow + +When we add a Sequencer, the operation of the inbox changes. + +- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) +- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. + - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. + - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) + +### If the Sequencer is well-behaved... + +A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. + +It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. + +This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. + +This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. + +So a Sequencer is generally a win, if the Sequencer is well behaved. + +### If the Sequencer is malicious... + +A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. + +On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. + +Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. + +### Decentralized fair sequencing + +Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. + +How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx new file mode 100644 index 000000000..7834945f9 --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx @@ -0,0 +1,147 @@ +--- +author: dzgoldman +--- + +# Inside AnyTrust + +AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. + +The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. + +AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. + +## Keysets + +A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. + +A Keyset contains + +- the number of Committee members, and +- for each Committee member, a BLS public key, and +- the number of Committee signatures required. + +Keysets are identified by their hashes. + +An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. + +Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. + +## Data Availability Certificates + +A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: + +- the hash of a data block, and +- an expiration time, and +- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of + - the hash of the Keyset used in signing, and + - a bitmap saying which Committee members signed, and + - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. + +Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. + +In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. + +AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. + +The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that + +- the number of signers is at least the number required by the Keyset, and +- the aggregated signature is valid for the claimed signers, and +- the expiration time is at least two weeks after the current L2 timestamp. + +If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. + +## Data Availability Servers + +Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: + +- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. +- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. + +Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) + +The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. + +## Sequencer-Committee Interaction + +When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. + +Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. + +If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. + + + + + + + + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx new file mode 100644 index 000000000..880e5490c --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx @@ -0,0 +1,147 @@ +--- +author: dzgoldman +--- + +# Gas and Fees + +There are two parties a user pays when submitting a tx: + +- the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx +- the network fee account for L2 resources, which include the computation, storage, and other burdens L2 nodes must bear to service the tx + +The L1 component is the product of the transaction's estimated contribution to its batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. For details, see [L1 Pricing](/how-arbitrum-works/l1-gas-pricing.mdx). + +The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. + +## Gas Price Floor + +The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). + +## Estimating Gas + +Calling an Arbitrum Node's `eth_estimateGas` RPC gives a value sufficient to cover the full transaction fee at the given L2 gas price; i.e., the value returned from `eth_estimateGas` multiplied by the L2 gas price tells you how much total Ether is required for the transaction to succeed. Note that this means that for a given operation, the value returned by `eth_estimateGas` will change over time (as the L1 calldata price fluctuates.) (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and [How to estimate gas in Arbitrum](/build-decentralized-apps/02-how-to-estimate-gas.mdx) for more.) + +## Tips in L2 + +The sequencer prioritizes transactions on a first-come first-served basis. Because tips do not make sense in this model, they are ignored. Arbitrum users always just pay the basefee regardless of the tip they choose. + +## Gas Estimating Retryables + +When a transaction schedules another, the subsequent transaction's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A transaction's gas estimate, then, can only be found if all the transactions succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. + +Because a call to [`redeem`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) donates all of the call's gas, doing multiple requires limiting the amount of gas provided to each subcall. Otherwise the first will take all of the gas and force the second to necessarily fail irrespective of the estimation's gas limit. + +Gas estimation for Retryable submissions is possible via the [NodeInterface](/build-decentralized-apps/nodeinterface/02-reference.mdx) and similarly requires the auto-redeem attempt to succeed. + +[estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/d52739e6d54f2ea06146fdc44947af3488b89082/internal/ethapi/api.go#L999 + + + +## Gas and Fees + +NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. + +### The Speed Limit + +The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. + +This sets an effective speed limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. + +Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. + +### Fees + +User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. + +Fees are charged for two resources that a transaction can use: + +- _L2 gas_: an Ethereum-equivalent amount of gas, as required to execute the transaction on the Nitro chain, + +* _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, + +#### L2 gas fees + +L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. + +The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. + +The algorithm compares gas usage against a parameter called the "speed limit" which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. + +Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. + +To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. + +#### L1 calldata fees + +L1 calldata fees exist because the Sequencer, or the batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. + +Every transaction that comes in through the Sequencer will pay an L1 calldata fee. Transactions that come in through the delayed inbox do not pay this fee because they don't add to batch posting costs--but these transactions pay gas fees to Ethereum when they are put into the delayed inbox. + +The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transaction. First, it computes the transaction's size, which is an estimate of how many bytes the transaction will add to the compressed batch it is in; the formula for this includes an estimate of how compressible the transaction is. Second, it multiplies the computed size estimate by the current price per estimated byte, to determine the transaction's L1 calldata wei, in wei. Finally, it divides this cost by the current L2 basefee to translate the fee into L2 gas units. The result is reported as the "poster fee" for the transaction. + +The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. + +#### Total fee and gas estimation + +The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. + + + + +# L1 gas pricing + +ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amount collected in L1 gas fees is as close as possible to the costs that must be covered, over time. + +## L1 fee collection + +A transaction is charged for L1 gas if and only if it arrived as part of a sequencer batch. This means that someone would have paid for L1 gas to post the transaction on the L1 chain. + +The estimated cost of posting a transaction on L1 is the product of the transaction's estimated size, and the current L1 Gas Basefee. This estimated cost is divided by the current L2 gas basefee to obtain the amount of L2 gas that corresponds to the L1 operation (more information about this can be found in [this article][two_dimensional_fees_medium_article_link]). + +The estimated size is measured in L1 gas and is calculated as follows: first, compress the transaction's data using the brotli-zero algorithm, then multiply the size of the result by 16. (16 is because L1 charges 16 gas per byte. L1 charges less for bytes that are zero, but that doesn't make sense here.) Brotli-zero is used in order to reward users for posting transactions that are compressible. Ideally we would like to reward for posting transactions that contribute to the compressibility (using the brotli compressor) of the entire batch, but that is a difficult notion to define and in any case would be too expensive to compute at L2. Brotli-zero is an approximation that is cheap enough to compute. + +L1 gas fee funds that are collected from transactions are transferred to a special [`L1PricerFundsPool`][l1pricerfundspool_link] account, so that account's balance represents the amount of funds that have been collected and are available to pay for costs. + +The L1 pricer also records the total number of "data units" (the sum of the estimated sizes, after multiplying by 16) that have been received. + +[l1pricerfundspool_link]: https://github.com/OffchainLabs/nitro/blob/3f4939df1990320310e7f39e8abb32d5c4d8045f/arbos/l1pricing/l1pricing.go#L46 +[two_dimensional_fees_medium_article_link]: https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9 + +## L1 costs + +There are two types of L1 costs: batch posting costs, and rewards. + +Batch posting costs reflect the actual cost a batch poster pays to post batch data on L1. Whenever a batch is posted, the L1 contract that records the batch will send a special "batch posting report" message to L2 ArbOS, reporting who paid for the batch and what the L1 basefee was at the time. This message is placed in the chain's delayed inbox, so it will be delivered to L2 ArbOS after some delay. + +When a batch posting report message arrives at L2, ArbOS computes the cost of the referenced batch by multiplying the reported basefee by the batch's data cost. (ArbOS retrieves the batch's data from its inbox state, and computes the L1 gas that the batch would have used by counting the number of zero bytes and non-zero bytes in the batch.) The resulting cost is recorded by the pricer as funds due to the party who is reported to have submitted the batch. + +The second type of L1 cost is an optional (per chain) per-unit reward for handling transaction calldata. In general the reward might be paid to the sequencer, or to members of the Data Availability Committee in an AnyTrust chain, or to anyone else who incurs per-calldata-byte costs on behalf of the chain. The reward is a fixed number of wei per data unit, and is paid to a single address. + +The L1 pricer keeps track of the funds due to the reward address, based on the number of data units handled so far. This amount is updated whenever a batch posting report arrives at L2. + +## Allocating funds and paying what is owed + +When a batch posting report is processed at L2, the pricer allocates some of the collected funds to pay for costs incurred. To allocate funds, the pricer considers three timestamps: + +- `currentTime` is the current time, when the batch posting report message arrives at L2 +- `updateTime` is the time at which the reported batch was submitted (which will typically be around 20 minutes before currentTime) +- `lastUpdateTime` is the time at which the previous reported batch was submitted + +The pricer computes an allocation fraction `F = (updateTime-lastUpdateTime) / (currentTime-lastUpdateTime)` and allocates a fraction `F` of funds in the `L1PricerFundsPool` to the current report. The intuition is that the pricer knows how many funds have been collected between `lastUpdateTime` and `currentTime`, and we want to figure out how many of those funds to allocate to the interval between `lastUpdateTime` and `updateTime`. The given formula is the correct allocation, if we assume that funds arrived at a uniform rate during the interval between `lastUpdateTime` and `currentTime`. The pricer similarly allocates a portion of the total data units to the current report. + +Now the pricer pays out the allocated funds to cover the rewards due and the amounts due to batch posters, reducing the balance due to each party as a result. If the allocated funds aren't sufficient to cover everything that is due, some amount due will remain. If all of the amount due can be covered with the allocated funds, any remaining allocated funds are returned to the `L1PricerFundsPool`. + +## Adjusting the L1 gas basefee + +After allocating funds and paying what is owed, the L1 Pricer adjusts the L1 Gas Basefee. The goal of this process is to find a value that will cause the amount collected to equal the amount owed over time. + +The algorithm first computes the surplus (funds in the `L1PricerFundsPool`, minus total funds due), which might be negative. If the surplus is positive, the L1 Gas Basefee is reduced, so that the amount collected over a fixed future interval will be reduced by exactly the surplus. If the surplus is negative, the Basefee is increased so that the shortfall will be eliminated over the same fixed future interval. + +A second term is added to the L1 Gas Basefee, based on the derivative of the surplus (surplus at present, minus the surplus after the previous batch posting report was processed). This term, which is multiplied by a smoothing factor to reduce fluctuations, will reduce the Basefee if the surplus is increasing, and increase the Basefee if the surplus is shrinking. + +## Getting L1 fee info + +The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. + +Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/why-nitro.mdx b/arbitrum-docs/how-arbitrum-works/09-nitro-vs-classic.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/why-nitro.mdx rename to arbitrum-docs/how-arbitrum-works/09-nitro-vs-classic.mdx diff --git a/arbitrum-docs/how-arbitrum-works/arbos/l2-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/arbos/l2-l1-messaging.mdx deleted file mode 100644 index a7aae98db..000000000 --- a/arbitrum-docs/how-arbitrum-works/arbos/l2-l1-messaging.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: 'L2 to L1 messaging and the outbox' -description: This concept page provides information about how arbitrary messages are passed from L2 to L1 -target_audience: developers who want to build on Arbitrum -content_type: concept ---- - -Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. - -### Protocol Flow - -Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. - -### Client Flow - -From a client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. - -### Protocol Design Details - -An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock confirmation processed can't be griefed. - -Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. - -Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. -The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) - -\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx deleted file mode 100644 index a9ce366d5..000000000 --- a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -author: dzgoldman ---- - -# The Assertion Tree - -### Overview - -The state of an Arbitrum chain is confirmed back on Ethereum via "assertions," aka "disputable assertions" or "DAs." These are claims made by Arbitrum validators about the chain's state. To make an assertion, a validator must post a bond in Ether. - -In the happy / common case, all outstanding assertions will be valid; i.e., a valid assertion will build on another valid assertion, which builds on another valid assertion, and so on. After the dispute period (~ 1 week) passes and an assertion goes unchallenged, it can be confirmed back on L1. - -If, however, two or more conflicting assertions exist, the Assertion Tree bifurcates into multiple branches: - -![img](../assets/assertionTree.png) - -Crucially, the rules of advancing an Arbitrum chain are deterministic; this means that given a chain state and some new inputs, there is only one valid output. Thus, if the Assertion Tree contains more than one leaf, then at most only one leaf can represent the valid chain-state; if we assume there is at least one honest active validator, _exactly_ one leaf will be valid. - -Two conflicting assertions can be put into a dispute; see [Interactive Challenges](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx) for details on the dispute process. For the sake of understanding the Assertion Tree protocol, suffice it to say that 2-party disputes last at most a fixed amount of time (1 week), at the end of which one of the two conflicting assertions will be rejected, and the validator who posted it will lose their stake. - -In order for an assertion to be confirmed and for its stake to be recovered, two conditions must be met: sufficient time for disputes must have passed, and no other conflicting branches in the Assertion Tree can exist (i.e., they've all been disputed / "pruned" off.) - -These properties together ensure that as long as at least one honest, active validator exists, the valid chain state will ultimately be confirmed. - -### Delays - -Even if the Assertion Tree has multiple conflicting leaves and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid leaf (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. - -The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. - -### Detailed Spec - -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum#rollup#protocol). diff --git a/arbitrum-docs/how-arbitrum-works/gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/gas-fees.mdx deleted file mode 100644 index 1d8e8df99..000000000 --- a/arbitrum-docs/how-arbitrum-works/gas-fees.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -author: dzgoldman ---- - -# Gas and Fees - -There are two parties a user pays when submitting a tx: - -- the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx -- the network fee account for L2 resources, which include the computation, storage, and other burdens L2 nodes must bear to service the tx - -The L1 component is the product of the transaction's estimated contribution to its batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. For details, see [L1 Pricing](/how-arbitrum-works/l1-gas-pricing.mdx). - -The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. - -## Gas Price Floor - -The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). - -## Estimating Gas - -Calling an Arbitrum Node's `eth_estimateGas` RPC gives a value sufficient to cover the full transaction fee at the given L2 gas price; i.e., the value returned from `eth_estimateGas` multiplied by the L2 gas price tells you how much total Ether is required for the transaction to succeed. Note that this means that for a given operation, the value returned by `eth_estimateGas` will change over time (as the L1 calldata price fluctuates.) (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and [How to estimate gas in Arbitrum](/build-decentralized-apps/02-how-to-estimate-gas.mdx) for more.) - -## Tips in L2 - -The sequencer prioritizes transactions on a first-come first-served basis. Because tips do not make sense in this model, they are ignored. Arbitrum users always just pay the basefee regardless of the tip they choose. - -## Gas Estimating Retryables - -When a transaction schedules another, the subsequent transaction's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A transaction's gas estimate, then, can only be found if all the transactions succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. - -Because a call to [`redeem`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) donates all of the call's gas, doing multiple requires limiting the amount of gas provided to each subcall. Otherwise the first will take all of the gas and force the second to necessarily fail irrespective of the estimation's gas limit. - -Gas estimation for Retryable submissions is possible via the [NodeInterface](/build-decentralized-apps/nodeinterface/02-reference.mdx) and similarly requires the auto-redeem attempt to succeed. - -[estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/d52739e6d54f2ea06146fdc44947af3488b89082/internal/ethapi/api.go#L999 diff --git a/arbitrum-docs/how-arbitrum-works/inside-anytrust.mdx b/arbitrum-docs/how-arbitrum-works/inside-anytrust.mdx deleted file mode 100644 index c490cdcc2..000000000 --- a/arbitrum-docs/how-arbitrum-works/inside-anytrust.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -author: dzgoldman ---- - -# Inside AnyTrust - -AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. - -The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. - -AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. - -## Keysets - -A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. - -A Keyset contains - -- the number of Committee members, and -- for each Committee member, a BLS public key, and -- the number of Committee signatures required. - -Keysets are identified by their hashes. - -An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. - -Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. - -## Data Availability Certificates - -A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: - -- the hash of a data block, and -- an expiration time, and -- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of - - the hash of the Keyset used in signing, and - - a bitmap saying which Committee members signed, and - - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. - -Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. - -In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. - -AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. - -The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that - -- the number of signers is at least the number required by the Keyset, and -- the aggregated signature is valid for the claimed signers, and -- the expiration time is at least two weeks after the current L2 timestamp. - -If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. - -## Data Availability Servers - -Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: - -- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. -- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. - -Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) - -The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. - -## Sequencer-Committee Interaction - -When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. - -Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. - -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. diff --git a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx b/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx deleted file mode 100644 index e0ab2b921..000000000 --- a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx +++ /dev/null @@ -1,677 +0,0 @@ ---- -title: 'Inside Arbitrum Nitro' -sidebar_label: 'Deep dive: Inside Arbitrum' -description: 'Learn the fundamentals of Nitro, Arbitrum stack.' -author: dzgoldman -sme: dzgoldman -user_story: As a current or prospective Arbitrum user, I need learn more about Nitros design. -content_type: get-started ---- - -import ImageWithCaption from '@site/src/components/ImageCaptions/'; - -This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. - -The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. - -## Why use Arbitrum? Why use Nitro? - -Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: - -- Trustless security: security rooted in Ethereum, with any one party able to ensure correct Layer 2 results -- Compatibility with Ethereum: able to run unmodified EVM contracts and unmodified Ethereum transactions -- Scalability: moving contracts’ computation and storage off of the main Ethereum chain, allowing much higher throughput -- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-transaction cost. - -Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. - -Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in several ways: - -- **Advanced Calldata Compression,** which further drives down transaction costs on Arbitrum by reducing the amount of data posted to L1. -- **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. -- **Ethereum L1 Gas Compatibility,** bringing pricing and accounting for EVM operations perfectly in line with Ethereum. -- **Additional L1 Interoperability,** including tighter synchronization with L1 Block numbers, and full support for all Ethereum L1 precompiles. -- **Safe Retryables,** eliminating the failure mode where a retryable ticket fails to get created. -- **Geth Tracing,** for even broader debugging support. -- And many, many more changes. - -## The Big Picture - -At the most basic level, an Arbitrum chain works like this: - - - -Users and contracts put messages into the inbox. The chain reads the messages one at a time, and processes each one. This updates the state of the chain and produces some outputs. - -If you want an Arbitrum chain to process a transaction for you, you need to put that transaction into the chain’s inbox. Then the chain will see your transaction, execute it, and produce some outputs: a transaction receipt, and any withdrawals that your transaction initiated. - -Execution is deterministic -- which means that the chain’s behavior is uniquely determined by the contents of its inbox. Because of this, the result of your transaction is knowable as soon as your transaction has been put in the inbox. Any Arbitrum node will be able to tell you the result. (And you can run an Arbitrum node yourself if you want.) - -All of the technical detail in this document is connected to this diagram. To get from this diagram to a full description of Arbitrum, we’ll need to answer questions like these: - -- Who keeps track of the inbox, chain state, and outputs? -- How does Arbitrum make sure that the chain state and outputs are correct? -- How can Ethereum users and contracts interact with Arbitrum? -- How does Arbitrum support Ethereum-compatible contracts and transactions? -- How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? -- How can I run my own Arbitrum node or validator? - -## Nitro's Design: The Four Big Ideas - -The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. - -**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic state transition function. - -**Big Idea: Geth at the Core**: Nitro supports Ethereum's data structures, formats, and virtual machine by compiling in the core code of the popular go-ethereum ("Geth") Ethereum node software. Using Geth as a library in this way ensures a very high degree of compatibility with Ethereum. - -**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. - -**Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. - -## Sequencing, Followed by Deterministic Execution - -This diagram summarizes how transactions are processed in Nitro. - -![seq-then-exec](../assets/seq-then-exec.png) - -Let's follow a user's transaction through this process. - -First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. - -Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. - -Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) - -The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. - -It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. - -### How the Sequencer Publishes the Sequence - -So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. - -The real-time feed is published by the Sequencer so that anyone who subscribes to the feed receives instant notifications of each transaction as it is sequenced. Nitro nodes can subscribe to the feed directly from the Sequencer, or through a relay that forwards the feed. The feed represents the Sequencer's promise that it will record transactions in a particular order. If the Sequencer is honest and doesn't have a long downtime, this promise will be kept. So anyone who trusts the Sequencer to keep its promises can rely on the feed to get instant information about the transaction sequence--and they can run the sequenced transactions through the state transition function to learn the results of each transaction immediately. This is "soft finality" for transactions; it's "soft" because it depends on the Sequencer keeping its promises. - -The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically--perhaps every few minutes in production--the Sequencer concatenates the next group of transactions in the feed, compresses them for efficiency, and posts the result as calldata on Ethereum. This is the final and official record of the transaction sequence. As soon as this Ethereum transaction has finality on Ethereum, the Layer 2 Nitro transactions it records will have finality. These transactions are final because their position in the sequence has finality, and the outcome of the transactions is deterministic and knowable to any party. This is "hard finality". - -The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. - -## Geth at the Core - -The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. - -![geth-sandwich](../assets/geth-sandwich.png) - -The software that makes up a Nitro node can be thought of as built in three main layers, which are shown above: - -- The base layer is the core of geth--the parts of Geth that emulate the execution of EVM contracts and maintain the data structures that make up the Ethereum state. Nitro compiles in this code as a library, with a few minor modifications to add necessary hooks. -- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. -- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible blockchain node. - -Because the top and bottom layers rely heavily on code from geth, this structure has been dubbed a "geth sandwich." Strictly speaking, Geth plays the role of the bread in the sandwich, and ArbOS is the filling, but this sandwich is named for the bread. - -The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - -## Separating Execution from Proving - -One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. - -When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) - -Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. - -#### WAVM - -The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. - -WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. - -Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. - -Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. - -#### ReadPreImage and the Hash Oracle Trick - -The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. - -(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) - -As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. - -The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. - -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - -## Optimistic Rollup - -Arbitrum is an optimistic rollup. Let’s unpack that term. - -_Rollup_ - -Arbitrum is a rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. - -This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. - -_Optimistic_ - -Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to challenge that claim. If the challenge period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). - -Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. - -## Resolving disputes using interactive fraud proofs - -Among optimistic rollups, the most important design decision is how to resolve disputes. Suppose Alice claims that the chain will produce a certain result, and Bob disagrees. How will the protocol decide which version to accept? - -There are basically two choices: interactive proving, or re-executing transactions. Arbitrum uses interactive proving, which we believe is more efficient and more flexible. Much of the design of Arbitrum follows from this fact. - -### Interactive proving - -The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. - -Arbitrum's approach is based on dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. - -The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. - -### Re-executing transactions - -The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. - -### Why interactive proving is better - -We believe strongly that interactive proving is the superior approach, for the following reasons. - -**More efficient in the optimistic case**: Because interactive proving can resolve disputes that are larger than one transaction, it can allow a rollup block to contain only a single claim about the end state of the chain after all of the execution covered by the block. By contrast, reexecution requires posting a state claim for each transaction within the rollup block. With hundred or thousands of transactions per rollup block, this is a substantial difference in L1 footprint -- and L1 footprint is the main component of cost. - -**More efficient in the pessimistic case**: In case of a dispute, interactive proving requires the L1 referee contract only to check that Alice and Bob's actions "have the right shape", for example, that Alice has divided her N-step claim into two claims half as large. (The referee doesn't need to evaluate the correctness of Alice's claims--Bob does that, off-chain.) Only one instruction needs to be reexecuted. By contrast, reexecution requires the L1 referee to emulate the execution of an entire transaction. - -**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. - -**More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. - -### Interactive proving drives the design of Arbitrum - -Much of the design of Arbitrum is driven by the opportunities opened up by interactive proving. If you're reading about some feature of Arbitrum, and you're wondering why it exists, two good questions to ask are: "How does this support interactive proving?" and "How does this take advantage of interactive proving?" The answers to most "why" questions about Arbitrum relate to interactive proving. - -## Arbitrum Rollup Protocol - -Before diving into the rollup protocol, there are two things we need to cover. - -First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. - -You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! - -The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) - -You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. - -With those preliminaries behind us, let’s jump into the details of the rollup protocol. - -The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). - -The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. - -### The Rollup Chain - -The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. - -Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. - -:::note - -Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and chain state, whereas proposers can also assert and challenge the chain state. - -::: - -Each RBlock contains: - -- the RBlock number -- the predecessor RBlock number: RBlock number of the last RBlock before this one that is (claimed to be) correct -- the number of L2 blocks that have been created in the chain's history -- the number of inbox messages that have been consumed in the chain’s history -- a hash of the outputs produced over the chain’s history. - -Except for the RBlock number, the contents of the RBlock are all just claims by the RBlock's proposer. Arbitrum doesn’t know at first whether any of these fields are correct. If all of these fields are correct, the protocol should eventually confirm the RBlock. If one or more of these fields are incorrect, the protocol should eventually reject the RBlock. - -An RBlock is implicitly claiming that its predecessor RBlock is correct. This implies, transitively, that an RBlock implicitly claims the correctness of a complete history of the chain: a sequence of ancestor RBlocks that reaches all the way back to the birth of the chain. - -An RBlock is also implicitly claiming that its older siblings (older RBlocks with the same predecessor), if there are any, are incorrect. If two RBlocks are siblings, and the older sibling is correct, then the younger sibling is considered incorrect, even if everything else in the younger sibling is true. - -The RBlock is assigned a deadline, which says how long other validators have to respond to it. If you’re a validator, and you agree that an RBlock is correct, you don’t need to do anything. If you disagree with an RBlock, you can post another RBlock with a different result, and you’ll probably end up in a challenge against the first RBlock's bonder. (More on challenges below.) - -In the normal case, the rollup chain will look like this: - -```mermaid -graph RL - f["First unresolved block"] - l["Latest confirmed block"] - - 98-->97-->96-->95 - f-.-95 - - 95-->94 - l-.-94 - - 94-->93-->92-->91-->90-->x[...] - - subgraph Legend - direction RL - Confirmed - Unconfirmed - end - - classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff - class 94,93,92,91,90,x,Confirmed confirmed - - - classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff - class 98,97,96,95,Unconfirmed unconfirmed - - classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 - class l,f note -``` - -On the left, representing an earlier part of the chain’s history, we have confirmed RBlocks. These have been fully accepted and recorded by the Layer 1 contracts that manage the chain. The newest of the confirmed RBlocks, RBlock 94, is called the “latest confirmed RBlock.” On the right, we see a set of newer proposed RBlocks. The protocol can’t yet confirm or reject them, because their deadlines haven’t run out yet. The oldest RBlock whose fate has yet to be determined, RBlock 95, is called the “first unresolved RBlock.” - -Notice that a proposed RBlock can build on an earlier proposed RBlock. This allows validators to continue proposing RBlocks without needing to wait for the protocol to confirm the previous one. Normally, all of the proposed RBlocks will be valid, so they will all eventually be accepted. - -Here’s another example of what the chain state might look like, if several validators are being malicious. It’s a contrived example, designed to illustrate a variety of cases that can come up in the protocol, all smashed into a single scenario. - -```mermaid -graph RL - subgraph Legend - direction RL - Confirmed - Rejected - Unconfirmed - end - - f["First unresolved block"] - l["Latest confirmed block"] - - - l-.-103 - f-.-106 - - 108-->107-->106-->103 - 111-->104 - 101-->100 - 105-->104-->103 - 110-->109-->103-->102-->100-->x[...] - - classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff - class 100,102,103,x,Confirmed confirmed - - classDef rejected fill:#fdaa07,stroke:#fd8607,stroke-width:2px,color:#fff - class 101,104,105,Rejected rejected - - classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff - class 106,107,108,109,110,111,Unconfirmed unconfirmed - - classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 - class l,f note - -``` - -There’s a lot going on here, so let’s unpack it. - -- RBlock 100 has been confirmed. -- RBlock 101 claimed to be a correct successor to RBlock 100, but 101 was rejected (hence it is orange). -- RBlock 102 was eventually confirmed as the correct successor to 100. -- RBlock 103 was confirmed and is now the latest confirmed RBlock. -- RBlock 104 was proposed as a successor to RBlock 103, and 105 was proposed as a successor to 104. 104 was rejected as incorrect, and as a consequence 105 was rejected because its predecessor was rejected. -- RBlock 106 is unresolved. It claims to be a correct successor to RBlock 103 but the protocol hasn’t yet decided whether to confirm or reject it. It is the first unresolved RBlock. -- RBlocks 107 and 108 claim to chain from 106. They are also unresolved. If 106 is rejected, they will be automatically rejected too. -- RBlock 109 disagrees with RBlock 106, because they both claim the same predecessor. At least one of them will eventually be rejected, but the protocol hasn’t yet resolved them. -- RBlock 110 claims to follow 109. It is unresolved. If 109 is rejected, 110 will be automatically rejected too. -- RBlock 111 claims to follow 104. 111 will inevitably be rejected because its predecessor has already been rejected. But it hasn’t been rejected yet, because the protocol resolves RBlocks in RBlock number order, so the protocol will have to resolve 106 through 110, in order, before it can resolve 111. After 110 has been resolved, 111 can be rejected immediately. - -Again: this sort of thing is very unlikely in practice. In this diagram, at least four parties must have bonded on wrong RBlocks, and when the dust settles at least four parties will have lost their bonds. The protocol handles these cases correctly, of course, but they’re rare corner cases. This diagram is designed to illustrate the variety of situations that are possible in principle, and how the protocol would deal with them. - -### Staking - -At any given time, some validators will be bonders, and some will not. Bonders deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the bonder loses a challenge. Nitro chains accept bonds in ETH. - -A single bond can cover a chain of RBlocks. Every bonder is bonded on the latest confirmed RBlock; and if you’re bonded on an RBlock, you can also bond on one successor of that RBlock. So you might be bonded on a sequence of RBlocks that represent a single coherent claim about the correct history of the chain. A single bond suffices to commit you to that sequence of RBlocks. - -In order to create a new RBlock, you must be a bonder, and you must already be bonded on the predecessor of the new RBlock you’re creating. The bond requirement for RBlock creation ensures that anyone who creates a new RBlock has something to lose if that RBlock is eventually rejected. - -The protocol keeps track of the current required bond amount. Normally this will equal the base bond amount, which is a parameter of the Nitro chain. But if the chain has been slow to make progress lately, the required bond will increase, as described in more detail below. - -The rules for staking are as follows: - -- If you’re not bonded, you can bond on the latest confirmed RBlock. When doing this, you deposit the current minimum bond amount. -- If you’re bonded on an RBlock, you can also add your bond to any one successor of that RBlock. (The protocol tracks the maximum RBlock number you’re bonded on, and lets you add your bond to any successor of that RBlock, updating your maximum to that successor.) This doesn’t require you to place a new bond. - - A special case of adding your bond to a successor RBlock is when you create a new RBlock as a successor to an RBlock you’re already bonded on. -- If you’re bonded only on the latest confirmed RBlock (and possibly earlier RBlocks), you or anyone else can ask to have your bond refunded. Your bonded funds will be returned to you, and you will no longer be a bonder. -- If you lose a challenge, your bond is removed from all RBlocks and you forfeit your bonded funds. - -Notice that once you are bonded on an RBlock, there is no way to unbond. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your bond. The only way to get your bond back is to wait until all of the RBlocks you are bonded on are confirmed. - -#### Setting the current minimum bond amount - -One detail we deferred earlier is how the current minimum bond amount is set. Normally, this is just equal to the base bond amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the bond requirement will escalate temporarily. Specifically, the base bond amount is multiplied by a factor that is exponential in the time since the deadline of the first unresolved RBlock passed. This ensures that if malicious parties are placing false bonds to try to delay progress (despite the fact that they’re losing those bonds), the bond requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the bond requirement will go back down. - -### Rules for Confirming or Rejecting RBlocks - -The rules for resolving RBlocks are fairly simple. - -The first unresolved RBlock can be confirmed if: - -- the RBlock's predecessor is the latest confirmed RBlock, and -- the RBlock's deadline has passed, and -- there is at least one bonder, and -- All bonders are bonded to the RBlock. - -The first unresolved RBlock can be rejected if: - -- the RBlock's predecessor has been rejected, or -- all of the following are true: - - the RBlock's deadline has passed, and - - there is at least one bonder, and - - no bonder is bonded on the RBlock. - -A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. - -## Challenges - -Suppose the rollup chain looks like this: - -![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) - -RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. - -At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) - -Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. - -The challenge is a game in which Alice and Bob alternate moves, with an Ethereum contract as the referee. Alice, the defender, moves first. - -The game will operate in two phases: dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. - -We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. - -### Dissection Protocol: Simplified Version - -Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. - -Alice’s first move requires her to dissect her claims about intermediate states between the beginning (0 instructions executed) and the end (N instructions executed). So we require Alice to divide her claim in half, and post the state at the half-way point, after N/2 instructions have been executed. - -Now Alice has effectively bisected her N-step assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. - -At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. - -### Why Dissection Correctly Identifies a Cheater - -Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. - -To prove (1), observe that if Alice’s initial claim is correct, she can offer a truthful midpoint claim, and both of the implied half-size claims will be correct. So whichever half Bob objects to, Alice will again be in the position of defending a correct claim. At each stage of the protocol, Alice will be defending a correct claim. At the end, Alice will have a correct one-step claim to prove, so that claim will be provable and Alice can win the challenge. - -To prove (2), observe that if Alice’s initial claim is incorrect, this can only be because her claimed endpoint after N steps is incorrect. Now when Alice offers her midpoint state claim, that midpoint claim is either correct or incorrect. If it’s incorrect, then Bob can challenge Alice’s first-half claim, which will be incorrect. If Alice’s midpoint state claim is correct, then her second-half claim must be incorrect, so Bob can challenge that. So whatever Alice does, Bob will be able to challenge an incorrect half-size claim. At each stage of the protocol, Bob can identify an incorrect claim to challenge. At the end, Alice will have an incorrect one-step claim to prove, which she will be unable to do, so Bob can win the challenge. - -(If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) - -### The Real Dissection Protocol - -The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. - -**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). - -**K-way dissection:** Rather than dividing a claim into two segments of size N/2, we divide it into K segments of size N/K. This requires posting K-1 intermediate claims, at points evenly spaced through the claimed execution. This reduces the number of rounds by a factor of log(K)/log(2). - -**Answer a dissection with a dissection:** Rather than having each round of the protocol require two moves, where Alice dissects and Bob chooses a segment to challenge, we instead require Bob, in challenging a segment, to post his own claimed endpoint state for that segment (which must differ from Alice’s) as well as his own dissection of his version of the segment. Alice will then respond by identifying a subsegment, posting an alternative endpoint for that segment, and dissecting it. This reduces the number of moves in the game by an additional factor of 2, because the size is cut by a factor of K for every move, rather than for every two moves. - -**Deal With the Empty-Inbox Case**: The real AVM can’t always execute N units of gas without getting stuck. The machine might halt, or it might have to wait because its inbox is exhausted so it can’t go on until more messages arrive. So Bob must be allowed to respond to Alice’s claim of N units of execution by claiming that N steps are not possible. The real protocol thus allows any response (but not the initial claim) to claim a special end state that means essentially that the specified amount of execution is not possible under the current conditions. - -**Time Limits:** Each player is given a time allowance. The total time a player uses for all of their moves must be less than the time allowance, or they lose the game. Think of the time allowance as being about a week. - -It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. - -### Efficiency - -The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. - -The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - -## Validators - -Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. - -Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. - -Offchain Labs provides open source validator software, including a pre-built Docker image. - -Every validator can choose their own approach, but we expect validators to follow three common strategies: - -- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. -- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. -- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) - -Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. - -The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). - -Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. - -- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. -- Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. -- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - -## ArbOS - -ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. - -### Why ArbOS? - -In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. - -Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. - -## Full Nodes - -As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. - -Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. - -## The Sequencer - -The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. - -Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. - -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. - -### Instant confirmation - -Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. - -The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. - -### Inboxes, fast and slow - -When we add a Sequencer, the operation of the inbox changes. - -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) -- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. - - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) - -### If the Sequencer is well-behaved... - -A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. - -It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. - -This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. - -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. - -So a Sequencer is generally a win, if the Sequencer is well behaved. - -### If the Sequencer is malicious... - -A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. - -On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. - -Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. - -### Decentralized fair sequencing - -Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. - -How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. - -## Bridging - -We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. - -The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). - -### L1 contracts can submit L2 transactions - -An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. - -The advantage of this method is that it is simple and has relatively low latency. The disadvantage, compared to the other method we’ll describe soon, is that the L2 transaction might revert if the L1 caller doesn’t get the L2 gas price and max gas amount right. Because the L1 caller can’t see the result of its L2 transaction, it can’t be absolutely sure that its L2 transaction will succeed. - -This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. - -### L1 to L2 ticket-based transactions - -Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. - -When saving a transaction for retry, Nitro records the sender’s address, destination address, callvalue, and calldata. All of this is saved, and the callvalue is deducted from the sender’s account and (logically) attached to the saved transaction. - -If the redemption succeeds, the transaction is done, a receipt is issued for it, and the ticketID is canceled and can’t be used again. If the redemption fails, for example because the packaged transaction fails, the redemption reports failure and the ticketID remains available for redemption. - -Normally the original submitter will try to cause their transaction to succeed immediately, so it never needs to be recorded or retried. As an example, our "token deposit" use case above should, in the happy, common case, still only require a single signature from the user. If this initial execution fails, the ticketID will still exist as a backstop which others can redeem later. - -Submitting a transaction in this way carries a price in ETH which the submitter must pay, which varies based on the calldata size of the transaction. Once submitted, the ticket is valid for about a week. If the ticket has not been redeemed in that period, it is deleted. - -When the ticket is redeemed, the pre-packaged transaction runs with sender and origin equal to the original submitter, and with the destination, callvalue, and calldata the submitter provided at the time of submission. - -This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. - -### L2 to L1 ticket-based calls - -Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. - -These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) - -## Gas and Fees - -NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. - -### The Speed Limit - -The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. - -This sets an effective speed limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. - -Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. - -### Fees - -User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. - -Fees are charged for two resources that a transaction can use: - -- _L2 gas_: an Ethereum-equivalent amount of gas, as required to execute the transaction on the Nitro chain, - -* _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, - -#### L2 gas fees - -L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. - -The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. - -The algorithm compares gas usage against a parameter called the "speed limit" which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. - -Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. - -To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. - -#### L1 calldata fees - -L1 calldata fees exist because the Sequencer, or the batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. - -Every transaction that comes in through the Sequencer will pay an L1 calldata fee. Transactions that come in through the delayed inbox do not pay this fee because they don't add to batch posting costs--but these transactions pay gas fees to Ethereum when they are put into the delayed inbox. - -The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transaction. First, it computes the transaction's size, which is an estimate of how many bytes the transaction will add to the compressed batch it is in; the formula for this includes an estimate of how compressible the transaction is. Second, it multiplies the computed size estimate by the current price per estimated byte, to determine the transaction's L1 calldata wei, in wei. Finally, it divides this cost by the current L2 basefee to translate the fee into L2 gas units. The result is reported as the "poster fee" for the transaction. - -The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. - -#### Total fee and gas estimation - -The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. - -## Inside AnyTrust - -AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. - -The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to bond on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. - -AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. - -### Keysets - -A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. - -A Keyset contains - -- the number of Committee members, and -- for each Committee member, a BLS public key, and -- the number of Committee signatures required. - -Keysets are identified by their hashes. - -An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. - -Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. - -### Data Availability Certificates - -A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: - -- the hash of a data block, and -- an expiration time, and -- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of - - the hash of the Keyset used in signing, and - - a bitmap saying which Committee members signed, and - - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. - -Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. - -In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. - -AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. - -The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that - -- the number of signers is at least the number required by the Keyset, and -- the aggregated signature is valid for the claimed signers, and -- the expiration time is at least two weeks after the current L2 timestamp. - -If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. - -### Data Availability Servers - -Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: - -- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. -- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. - -Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) - -The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. - -### Sequencer-Committee Interaction - -When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. - -Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. - -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. diff --git a/arbitrum-docs/how-arbitrum-works/l1-gas-pricing.mdx b/arbitrum-docs/how-arbitrum-works/l1-gas-pricing.mdx deleted file mode 100644 index 042490f99..000000000 --- a/arbitrum-docs/how-arbitrum-works/l1-gas-pricing.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -author: dzgoldman ---- - -# L1 gas pricing - -ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amount collected in L1 gas fees is as close as possible to the costs that must be covered, over time. - -## L1 fee collection - -A transaction is charged for L1 gas if and only if it arrived as part of a sequencer batch. This means that someone would have paid for L1 gas to post the transaction on the L1 chain. - -The estimated cost of posting a transaction on L1 is the product of the transaction's estimated size, and the current L1 Gas Basefee. This estimated cost is divided by the current L2 gas basefee to obtain the amount of L2 gas that corresponds to the L1 operation (more information about this can be found in [this article][two_dimensional_fees_medium_article_link]). - -The estimated size is measured in L1 gas and is calculated as follows: first, compress the transaction's data using the brotli-zero algorithm, then multiply the size of the result by 16. (16 is because L1 charges 16 gas per byte. L1 charges less for bytes that are zero, but that doesn't make sense here.) Brotli-zero is used in order to reward users for posting transactions that are compressible. Ideally we would like to reward for posting transactions that contribute to the compressibility (using the brotli compressor) of the entire batch, but that is a difficult notion to define and in any case would be too expensive to compute at L2. Brotli-zero is an approximation that is cheap enough to compute. - -L1 gas fee funds that are collected from transactions are transferred to a special [`L1PricerFundsPool`][l1pricerfundspool_link] account, so that account's balance represents the amount of funds that have been collected and are available to pay for costs. - -The L1 pricer also records the total number of "data units" (the sum of the estimated sizes, after multiplying by 16) that have been received. - -[l1pricerfundspool_link]: https://github.com/OffchainLabs/nitro/blob/3f4939df1990320310e7f39e8abb32d5c4d8045f/arbos/l1pricing/l1pricing.go#L46 -[two_dimensional_fees_medium_article_link]: https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9 - -## L1 costs - -There are two types of L1 costs: batch posting costs, and rewards. - -Batch posting costs reflect the actual cost a batch poster pays to post batch data on L1. Whenever a batch is posted, the L1 contract that records the batch will send a special "batch posting report" message to L2 ArbOS, reporting who paid for the batch and what the L1 basefee was at the time. This message is placed in the chain's delayed inbox, so it will be delivered to L2 ArbOS after some delay. - -When a batch posting report message arrives at L2, ArbOS computes the cost of the referenced batch by multiplying the reported basefee by the batch's data cost. (ArbOS retrieves the batch's data from its inbox state, and computes the L1 gas that the batch would have used by counting the number of zero bytes and non-zero bytes in the batch.) The resulting cost is recorded by the pricer as funds due to the party who is reported to have submitted the batch. - -The second type of L1 cost is an optional (per chain) per-unit reward for handling transaction calldata. In general the reward might be paid to the sequencer, or to members of the Data Availability Committee in an AnyTrust chain, or to anyone else who incurs per-calldata-byte costs on behalf of the chain. The reward is a fixed number of wei per data unit, and is paid to a single address. - -The L1 pricer keeps track of the funds due to the reward address, based on the number of data units handled so far. This amount is updated whenever a batch posting report arrives at L2. - -## Allocating funds and paying what is owed - -When a batch posting report is processed at L2, the pricer allocates some of the collected funds to pay for costs incurred. To allocate funds, the pricer considers three timestamps: - -- `currentTime` is the current time, when the batch posting report message arrives at L2 -- `updateTime` is the time at which the reported batch was submitted (which will typically be around 20 minutes before currentTime) -- `lastUpdateTime` is the time at which the previous reported batch was submitted - -The pricer computes an allocation fraction `F = (updateTime-lastUpdateTime) / (currentTime-lastUpdateTime)` and allocates a fraction `F` of funds in the `L1PricerFundsPool` to the current report. The intuition is that the pricer knows how many funds have been collected between `lastUpdateTime` and `currentTime`, and we want to figure out how many of those funds to allocate to the interval between `lastUpdateTime` and `updateTime`. The given formula is the correct allocation, if we assume that funds arrived at a uniform rate during the interval between `lastUpdateTime` and `currentTime`. The pricer similarly allocates a portion of the total data units to the current report. - -Now the pricer pays out the allocated funds to cover the rewards due and the amounts due to batch posters, reducing the balance due to each party as a result. If the allocated funds aren't sufficient to cover everything that is due, some amount due will remain. If all of the amount due can be covered with the allocated funds, any remaining allocated funds are returned to the `L1PricerFundsPool`. - -## Adjusting the L1 gas basefee - -After allocating funds and paying what is owed, the L1 Pricer adjusts the L1 Gas Basefee. The goal of this process is to find a value that will cause the amount collected to equal the amount owed over time. - -The algorithm first computes the surplus (funds in the `L1PricerFundsPool`, minus total funds due), which might be negative. If the surplus is positive, the L1 Gas Basefee is reduced, so that the amount collected over a fixed future interval will be reduced by exactly the surplus. If the surplus is negative, the Basefee is increased so that the shortfall will be eliminated over the same fixed future interval. - -A second term is added to the L1 Gas Basefee, based on the derivative of the surplus (surplus at present, minus the surplus after the previous batch posting report was processed). This term, which is multiplied by a smoothing factor to reduce fluctuations, will reduce the Basefee if the surplus is increasing, and increase the Basefee if the surplus is shrinking. - -## Getting L1 fee info - -The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. - -Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. diff --git a/arbitrum-docs/how-arbitrum-works/sequencer.mdx b/arbitrum-docs/how-arbitrum-works/sequencer.mdx deleted file mode 100644 index 80a3d66ca..000000000 --- a/arbitrum-docs/how-arbitrum-works/sequencer.mdx +++ /dev/null @@ -1,34 +0,0 @@ -# The Sequencer and Censorship Resistance - -The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. - -Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. - -## The Core Inbox - -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. - -## Happy/Common Case: Sequencer Is Live and Well-behaved - -Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. - -If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. -Once posted in a batch, the transactions have L1-level finality. - -Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. - -In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. - -## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job - -Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: - -First, they submit their L2 message via L1 into the delayed Inbox as described above: note that although atomic cross-chain messages are the common case for using the delayed Inbox, it can in principle be used to submit _any_ L2 message. - -Once in the delayed Inbox, we obviously can’t rely on the Sequencer to include the transaction in a batch. Instead, we can use `SequencerInbox`’s `forceInclusion` method. Once a message has been in the delayed Inbox for a sufficient amount of time, `forceInclusion` can be called to move it from the delayed Inbox into the core Inbox, at which point it’s finalized. Crucially, any account can call `forceInclusion`. - -Currently, on Arbitrum One, this delay time between submission and force inclusion is roughly @arbOneForceIncludePeriodHours@ hours, as specified by `maxTimeVariation.delayBlocks` / `maxTimeVariation.delaySeconds`. A force inclusion from L1 would directly affect the state for any unconfirmed L2 transactions; keeping conservatively high delay value ensures it should only be used under extraordinary circumstances. - -On top of the delay itself, the `forceInclusion` path has the downside of uncertainty around transaction ordering; i.e., while waiting for a message's max delay to pass, a malicious Sequencer could, in principle, directly post messages in front of it. However, there’s ultimately nothing the Sequencer can do to stop it from being included in the core Inbox, at which point its ordering is finalized. - -While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. diff --git a/arbitrum-sdk b/arbitrum-sdk index f5b3d04ba..5ef44308d 160000 --- a/arbitrum-sdk +++ b/arbitrum-sdk @@ -1 +1 @@ -Subproject commit f5b3d04baf62356032f5475057637d989da5b6c1 +Subproject commit 5ef44308d3c89fd956c9dfdc59b6776b88afd251 From 6fc2384ff8fbbac485d8a01c01a3b4dff24c651e Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 21 Nov 2024 15:34:50 -0600 Subject: [PATCH 02/75] consolidated more how arbitrum works content --- .../04-state-transition-function.mdx | 400 +++++++++++++++++ .../how-arbitrum-works/06-challenges.mdx | 407 ++++++++++++++++++ .../how-arbitrum-works/arbos/geth.mdx | 292 ------------- .../how-arbitrum-works/arbos/introduction.mdx | 102 ----- .../fraud-proofs/challenge-manager.mdx | 64 --- .../fraud-proofs/osp-assumptions.mdx | 75 ---- .../fraud-proofs/wasm-wavm.mdx | 61 --- .../fraud-proofs/wavm-custom-opcodes.mdx | 74 ---- .../fraud-proofs/wavm-floats.mdx | 32 -- .../fraud-proofs/wavm-modules.mdx | 57 --- 10 files changed, 807 insertions(+), 757 deletions(-) delete mode 100644 arbitrum-docs/how-arbitrum-works/arbos/geth.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/challenge-manager.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/wasm-wavm.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-floats.mdx delete mode 100644 arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-modules.mdx diff --git a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx index 1a6ec991b..6cd48612e 100644 --- a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx @@ -59,4 +59,404 @@ This "hash oracle trick" of storing the Merkle hash of a data structure, and rel + + + + +# ArbOS + +ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS accounts for and manages network resources, produces blocks from incoming messages, and operates its instrumented instance of Geth for smart contract execution. + +## Precompiles + +ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. Visit the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx) for more information about how these work, and the [precompiles reference page](/build-decentralized-apps/precompiles/02-reference.mdx) for a full reference of the precompiles available in Arbitrum chains. + +A precompile consists of a solidity interface in [`contracts/src/precompiles/`][nitro_precompiles_dir] and a corresponding Golang implementation in [`precompiles/`][precompiles_dir]. Using Geth's ABI generator, [`solgen/gen.go`][gen_file] generates [`solgen/go/precompilesgen/precompilesgen.go`][precompilesgen_link], which collects the ABI data of the precompiles. The [runtime installer][installer_link] uses this generated file to check the type safety of each precompile's implementer. + +[The installer][installer_link] uses runtime reflection to ensure each implementer has all the right methods and signatures. This includes restricting access to stateful objects like the EVM and statedb based on the declared purity. Additionally, the installer verifies and populates event function pointers to provide each precompile the ability to emit logs and know their gas costs. Additional configuration like restricting a precompile's methods to only be callable by chain owners is possible by adding precompile wrappers like [`ownerOnly`][owneronly_link] and [`debugOnly`][debugonly_link] to their [installation entry][installation_link]. + +The calling, dispatching, and recording of precompile methods are done via runtime reflection as well. This avoids any human error manually parsing and writing bytes could introduce, and uses Geth's stable APIs for [packing and unpacking][packing_link] values. + +Each time a transaction calls a method of an L2-specific precompile, a [`call context`][call_context_link] is created to track and record the gas burnt. For convenience, it also provides access to the public fields of the underlying [`TxProcessor`][txprocessor_link]. Because sub-transactions could revert without updates to this struct, the [`TxProcessor`][txprocessor_link] only makes public that which is safe, such as the amount of L1 calldata paid by the top level transaction. + +[nitro_precompiles_dir]: https://github.com/OffchainLabs/nitro-contracts/tree/main/src/precompiles +[precompiles_dir]: https://github.com/OffchainLabs/nitro/tree/master/precompiles +[installer_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L379 +[installation_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L403 +[gen_file]: https://github.com/OffchainLabs/nitro/blob/master/solgen/gen.go +[owneronly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L58 +[debugonly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L23 +[precompilesgen_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/solgen/gen.go#L55 +[packing_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L438 +[call_context_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/context.go#L26 + +## Messages + +An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. + +[l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 +[produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 + +## Retryables + +A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). + +## ArbOS State + +ArbOS's state is viewed and modified via [`ArbosState`][arbosstate_link] objects, which provide convenient abstractions for working with the underlying data of its [`backingStorage`][backingstorage_link]. The backing storage's [keyed subspace strategy][subspace_link] makes possible [`ArbosState`][arbosstate_link]'s convenient getters and setters, minimizing the need to directly work with the specific keys and values of the underlying storage's [`stateDB`][statedb_link]. + +Because two [`ArbosState`][arbosstate_link] objects with the same [`backingStorage`][backingstorage_link] contain and mutate the same underlying state, different [`ArbosState`][arbosstate_link] objects can provide different views of ArbOS's contents. [`Burner`][burner_link] objects, which track gas usage while working with the [`ArbosState`][arbosstate_link], provide the internal mechanism for doing so. Some are read-only, causing transactions to revert with `vm.ErrWriteProtection` upon a mutating request. Others demand the caller have elevated privileges. While yet others dynamically charge users when doing stateful work. For safety the kind of view is chosen when [`OpenArbosState()`][openarbosstate_link] creates the object and may never change. + +Much of ArbOS's state exists to facilitate its [precompiles](/build-decentralized-apps/precompiles/02-reference.mdx). The parts that aren't are detailed below. + +[arbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L36 +[backingstorage_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L51 +[statedb_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L66 +[subspace_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L21 +[openarbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L57 +[burner_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/burn/burn.go#L11 + +### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] + +ArbOS upgrades are scheduled to happen [when finalizing the first block][finalizeblock_link] after the [`upgradeTimestamp`][upgradetimestamp_link]. + +[arbosversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L37 +[upgradeversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L38 +[upgradetimestamp_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L39 +[finalizeblock_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L350 + +### [`blockhashes`][blockhashes_link] + +This component maintains the last 256 L1 block hashes in a circular buffer. This allows the [`TxProcessor`][txprocessor_link] to implement the `BLOCKHASH` and `NUMBER` opcodes as well as support precompile methods that involve the outbox. To avoid changing ArbOS state outside of a transaction, blocks made from messages with a new L1 block number update this info during an [`InternalTxUpdateL1BlockNumber`][internaltxupdatel1blocknumber_link] [`ArbitrumInternalTx`][arbitruminternaltx_link] that is included as the first transaction in the block. + +[blockhashes_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/blockhash/blockhash.go#L15 +[internaltxupdatel1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/internal_tx.go#L24 +[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L116 +[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 + +### [`l1PricingState`][l1pricingstate_link] + +In addition to supporting the [`ArbAggregator precompile`](/build-decentralized-apps/precompiles/02-reference.mdx#arbaggregator), the L1 pricing state provides tools for determining the L1 component of a transaction's gas costs. This part of the state tracks both the total amount of funds collected from transactions in L1 gas fees, as well as the funds spent by batch posters to post data batches on L1. + +Based on this information, ArbOS maintains an L1 data fee, also tracked as part of this state, which determines how much transactions will be charged for L1 fees. ArbOS dynamically adjusts this value so that fees collected are approximately equal to batch posting costs, over time. + +[l1pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l1pricing/l1pricing.go#L16 + +### [`l2PricingState`][l2pricingstate_link] + +The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. + +While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth#Hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. + +ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](geth#GasChargingHook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. + +The reason we need a per-block gas limit is that Arbitrator WAVM execution is much slower than native transaction execution. This means that there can only be so much gas -- which roughly translates to wall-clock time -- in an L2 block. It also provides an opportunity for ArbOS to limit the size of blocks should demand continue to surge even as the price rises. + +ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [sets sufficiently high][geth_pool_set_link] so as to never run out. This is safe since Geth's block limit exists to constrain the amount of work done per block, which ArbOS already does via its own per-block gas limit. Though it'll never run out, a block's transactions use the [same Geth gas pool][same_geth_pool_link] to maintain the invariant that the pool decreases monotonically after each tx. Block headers [use the Geth block limit][use_geth_pool_link] for internal consistency and to ensure gas estimation works. These are both distinct from the [`gasLeft`][per_block_limit_link] variable, which ephemerally exists outside of global state to both keep L2 blocks from exceeding ArbOS's per-block gas limit and to [deduct space][deduct_space_link] in situations where the state transition failed or [used negligible amounts][negligible_amounts_link] of compute gas. ArbOS does not need to persist [`gasLeft`][per_block_limit_link] because it is its _pool_ that induces a revert and because transactions use the Geth block limit during EVM execution. + +[l2pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l2pricing/l2pricing.go#L14 +[block_production_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L77 +[notify_pricer_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L336 +[maintain_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l2pricing/pools.go#L98 +[per_block_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L146 +[first_transaction_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L237 +[geth_pool_set_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L166 +[same_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L199 +[use_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L67 +[deduct_space_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L272 +[negligible_amounts_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L328 + + + + + +# Geth + +Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This document will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. + +We store ArbOS's state at an address inside a Geth `statedb`. In doing so, ArbOS inherits the `statedb`'s statefulness and lifetime properties. For example, a transaction's direct state changes to ArbOS are discarded upon a revert. + +**0xA4B05FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF**
+The fictional account representing ArbOS + +:::info + +Please note any links on this page may be referencing old releases of Nitro or our fork of Geth. While we try to keep this up to date and most of this should be stable, please check against latest releases for [Nitro](https://github.com/OffchainLabs/nitro/releases) and [Geth](https://github.com/OffchainLabs/go-ethereum/releases) for most recent changes. + +::: + +## Hooks + +Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. + +Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#EnableArbOS) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. + +- `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` + - `core.NewStateTransition` + - [`ReadyEVMForL2`](#ReadyEVMForL2) + - `core.TransitionDb` + - [`StartTxHook`](#StartTxHook) + - `core.transitionDbImpl` + - if `IsArbitrum()` remove tip + - [`GasChargingHook`](#GasChargingHook) + - `evm.Call` + - `core.vm.EVMInterpreter.Run` + - [`PushCaller`](#PushCaller) + - [`PopCaller`](#PopCaller) + - `core.StateTransition.refundGas` + - [`ForceRefundGas`](#ForceRefundGas) + - [`NonrefundableGas`](#NonrefundableGas) + - [`EndTxHook`](#EndTxHook) + - added return parameter: `transactionResult` + +What follows is an overview of each hook, in chronological order. + +### [`ReadyEVMForL2`][readyevmforl2_link]{#ReadyEVMForL2} + +A call to [`ReadyEVMForL2`][readyevmforl2_link] installs the other transaction-specific hooks into each Geth [`EVM`][evm_link] right before it performs a state transition. Without this call, the state transition will instead use the default [`DefaultTxProcessor`][defaulttxprocessor_link] and get exactly the same results as vanilla Geth. A [`TxProcessor`][txprocessor_link] object is what carries these hooks and the associated Arbitrum-specific state during the transaction's lifetime. + +### [`StartTxHook`][starttxhook_link]{#StartTxHook} + +The [`StartTxHook`][starttxhook_link] is called by Geth before a transaction starts executing. This allows ArbOS to handle two Arbitrum-specific transaction types. + +If the transaction is `ArbitrumDepositTx`, ArbOS adds balance to the destination account. This is safe because the L1 bridge submits such a transaction only after collecting the same amount of funds on L1. + +If the transaction is an `ArbitrumSubmitRetryableTx`, ArbOS creates a retryable based on the transaction's fields. If the transaction includes sufficient gas, ArbOS schedules a retry of the new retryable. + +The hook returns `true` for both of these transaction types, signifying that the state transition is complete. + +### [`GasChargingHook`][gascharginghook_link]{#GasChargingHook} + +This fallible hook ensures the user has enough funds to pay their poster's L1 calldata costs. If not, the transaction is reverted and the [`EVM`][evm_link] does not start. In the common case that the user can pay, the amount paid for calldata is set aside for later reimbursement of the poster. All other fees go to the network account, as they represent the transaction's burden on validators and nodes more generally. + +If the user attempts to purchase compute gas in excess of ArbOS's per-block gas limit, the difference is [set aside][difference_set_aside_link] and [refunded later][refunded_later_link] via [`ForceRefundGas`](#ForceRefundGas) so that only the gas limit is used. Note that the limit observed may not be the same as that seen [at the start of the block][that_seen_link] if ArbOS's larger gas pool falls below the [`MaxPerBlockGasLimit`][max_perblock_limit_link] while processing the block's previous transactions. + +[difference_set_aside_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L407 +[refunded_later_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L419 +[that_seen_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L176 +[max_perblock_limit_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/l2pricing/l2pricing.go#L86 + +### [`PushCaller`][pushcaller_link]{#PushCaller} + +These hooks track the callers within the EVM callstack, pushing and popping as calls are made and complete. This provides [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) with info about the callstack, which it uses to implement the methods `WasMyCallersAddressAliased` and `MyCallersAddressWithoutAliasing`. + +### [`L1BlockHash`][l1blockhash_link] + +In Arbitrum, the BlockHash and Number operations return data that relies on underlying L1 blocks instead of L2 blocks, to accommodate the normal use-case of these opcodes, which often assume Ethereum-like time passes between different blocks. The L1BlockHash and L1BlockNumber hooks have the required data for these operations. + +### [`ForceRefundGas`][forcerefundgas_link]{#ForceRefundGas} + +This hook allows ArbOS to add additional refunds to the user's tx. This is currently only used to refund any compute gas purchased in excess of ArbOS's per-block gas limit during the [`GasChargingHook`](#GasChargingHook). + +### [`NonrefundableGas`][nonrefundablegas_link]{#NonrefundableGas} + +Because poster costs come at the expense of L1 aggregators and not the network more broadly, the amounts paid for L1 calldata should not be refunded. This hook provides Geth access to the equivalent amount of L2 gas the poster's cost equals, ensuring this amount is not reimbursed for network-incentivized behaviors like freeing storage slots. + +### [`EndTxHook`][endtxhook_link]{#EndTxHook} + +The [`EndTxHook`][endtxhook_link] is called after the [`EVM`][evm_link] has returned a transaction's result, allowing one last opportunity for ArbOS to intervene before the state transition is finalized. Final gas amounts are known at this point, enabling ArbOS to credit the network and poster each's share of the user's gas expenditures as well as adjust the pools. The hook returns from the [`TxProcessor`][txprocessor_link] a final time, in effect discarding its state as the system moves on to the next transaction where the hook's contents will be set afresh. + +[applytransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_processor.go#L152 +[evm_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/vm/evm.go#L101 +[defaulttxprocessor_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/vm/evm_arbitrum.go#L42 +[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L38 +[starttxhook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L100 +[readyevmforl2_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbstate/geth-hook.go#L47 +[gascharginghook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L354 +[pushcaller_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L76 +[popcaller_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L80 +[forcerefundgas_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L425 +[nonrefundablegas_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L418 +[endtxhook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L429 +[l1blockhash_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L617 +[l1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L600 + +## Interfaces and components + +### [`APIBackend`][apibackend_link] + +APIBackend implements the [`ethapi.Backend`][ethapi.backend_link] interface, which allows simple integration of the Arbitrum chain to existing Geth API. Most calls are answered using the Backend member. + +[apibackend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/apibackend.go#L34 +[ethapi.backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/backend.go#L42 + +### [`Backend`][backend_link] + +This struct was created as an Arbitrum equivalent to the [`Ethereum`][ethereum_link] struct. It is mostly glue logic, including a pointer to the ArbInterface interface. + +[backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/backend.go#L15 +[ethereum_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/eth/backend.go#L68 + +### [`ArbInterface`][arbinterface_link] + +This interface is the main interaction-point between geth-standard APIs and the Arbitrum chain. Geth APIs mostly either check status by working on the Blockchain struct retrieved from the [`Blockchain`][blockchain_link] call, or send transactions to Arbitrum using the [`PublishTransactions`][publishtransactions_link] call. + +[arbinterface_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L10 +[blockchain_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L12 +[publishtransactions_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L11 + +### [`RecordingKV`][recordingkv_link] + +RecordingKV is a read-only key-value store, which retrieves values from an internal trie database. All values accessed by a RecordingKV are also recorded internally. This is used to record all preimages accessed during block creation, which will be needed to prove execution of this particular block. +A [`RecordingChainContext`][recordingchaincontext_link] should also be used, to record which block headers the block execution reads (another option would be to always assume the last 256 block headers were accessed). +The process is simplified using two functions: [`PrepareRecording`][preparerecording_link] creates a stateDB and chaincontext objects, running block creation process using these objects records the required preimages, and [`PreimagesFromRecording`][preimagesfromrecording_link] function extracts the preimages recorded. + +[recordingkv_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L22 +[recordingchaincontext_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L123 +[preparerecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L152 +[preimagesfromrecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L174 + +## Transaction Types + +Nitro Geth includes a few L2-specific transaction types. Click on any to jump to their section. + +| Tx Type | Represents | Last Hook Reached   | Source | +| :------------------------------------------------ | :----------------------------------- | :------------------------- | ------ | +| [`ArbitrumUnsignedTx`][arbtxunsigned] | An L1 to L2 message | [`EndTxHook`][he] | Bridge | +| [`ArbitrumContractTx`][arbtxcontract] | A nonce-less L1 to L2 message   | [`EndTxHook`][he] | Bridge | +| [`ArbitrumDepositTx`][arbtxdeposit] | A user deposit | [`StartTxHook`][hs] | Bridge | +| [`ArbitrumSubmitRetryableTx`][arbtxsubmit]   | Creating a retryable | [`StartTxHook`][hs]   | Bridge | +| [`ArbitrumRetryTx`][arbtxretry] | A retryable redeem attempt | [`EndTxHook`][he] | L2 | +| [`ArbitrumInternalTx`][arbtxinternal] | ArbOS state update | [`StartTxHook`][hs] | ArbOS | + +[arbtxunsigned]: #ArbitrumUnsignedTx +[arbtxcontract]: #ArbitrumContractTx +[arbtxsubmit]: #ArbitrumSubmitRetryableTx +[arbtxretry]: #ArbitrumRetryTx +[arbtxdeposit]: #ArbitrumDepositTx +[arbtxinternal]: #ArbitrumInternalTx +[hs]: #StartTxHook +[he]: #EndTxHook + +The following reference documents each type. + +### [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link]{#ArbitrumUnsignedTx} + +Provides a mechanism for a user on L1 to message a contract on L2. This uses the bridge for authentication rather than requiring the user's signature. Note, the user's acting address will be remapped on L2 to distinguish them from a normal L2 caller. + +### [`ArbitrumContractTx`][arbitrumcontracttx_link]{#ArbitrumContractTx} + +These are like an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link] but are intended for smart contracts. These use the bridge's unique, sequential nonce rather than requiring the caller specify their own. An L1 contract may still use an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link], but doing so may necessitate tracking the nonce in L1 state. + +### [`ArbitrumDepositTx`][arbitrumdeposittx_link]{#ArbitrumDepositTx} + +Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. + +### [`ArbitrumSubmitRetryableTx`][arbitrumsubmitretryabletx_link]{#ArbitrumSubmitRetryableTx} + +Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. + +### [`ArbitrumRetryTx`][arbitrumretrytx_link]{#ArbitrumRetryTx} + +These are scheduled by calls to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile and via retryable auto-redemption. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. + +### [`ArbitrumInternalTx`][arbitruminternaltx_link]{#ArbitrumInternalTx} + +Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a transaction of this type to update its state in-between user-generated transactions. Such a transaction has a [`Type`][internaltype_link] field signifying the state it will update, though currently this is just future-proofing as there's only one value it may have. Below are the internal transaction types. + +#### [`InternalTxStartBlock`][arbinternaltxstartblock_link] + +Updates the L1 block number and L1 base fee. This transaction [is generated][block_generated_link] whenever a new block is created. They are [guaranteed to be the first][block_first_link] in their L2 block. + +[arbitrumunsignedtx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L43 +[arbitrumcontracttx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L104 +[arbitrumsubmitretryabletx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L232 +[arbitrumretrytx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L161 +[arbitrumdeposittx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L338 +[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/internal_tx.go +[internaltype_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L387 +[arbinternaltxstartblock_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/internal_tx.go#L22 +[block_generated_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L181 +[block_first_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L182 + +## Transaction Run Modes and Underlying Transactions + +A [geth message][geth_message_link] may be processed for various purposes. For example, a message may be used to estimate the gas of a contract call, whereas another may perform the corresponding state transition. Nitro Geth denotes the intent behind a message by means of its [`TxRunMode`][txrunmode_link], [which it sets][set_run_mode_link] before processing it. ArbOS uses this info to make decisions about the transaction the message ultimately constructs. + +A message [derived from a transaction][asmessage_link] will carry that transaction in a field accessible via its [`UnderlyingTransaction`][underlying_link] method. While this is related to the way a given message is used, they are not one-to-one. The table below shows the various run modes and whether each could have an underlying transaction. + +| Run Mode | Scope | Carries an Underlying Tx? | +| :--------------------------------------- | :---------------------- | :----------------------------------------------------------------------------------------------------------- | +| [`MessageCommitMode`][mc0] | state transition   | always | +| [`MessageGasEstimationMode`][mc1]   | gas estimation | when created via [NodeInterface](/build-decentralized-apps/nodeinterface/02-reference.mdx) or when scheduled | +| [`MessageEthcallMode`][mc2] | eth_calls | never | + +[mc0]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L654 +[mc1]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L655 +[mc2]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L656 +[geth_message_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L634 +[txrunmode_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L701 +[set_run_mode_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/api.go#L955 +[asmessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L676 +[underlying_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L700 + +## Arbitrum Chain Parameters + +Nitro's Geth may be configured with the following [l2-specific chain parameters][chain_params_link]. These allow the rollup creator to customize their rollup at genesis. + +### `EnableArbos` + +Introduces [ArbOS](/how-arbitrum-works/arbos/introduction.mdx), converting what would otherwise be a vanilla L1 chain into an L2 Arbitrum rollup. + +### `AllowDebugPrecompiles` + +Allows access to debug precompiles. Not enabled for Arbitrum One. When false, calls to debug precompiles will always revert. + +### `DataAvailabilityCommittee` + +Currently does nothing besides indicate that the rollup will access a data availability service for preimage resolution in the future. This is not enabled for Arbitrum One, which is a strict state-function of its L1 inbox messages. + +[chain_params_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/params/config_arbitrum.go#L25 + +## Miscellaneous Geth Changes + +### ABI Gas Margin + +Vanilla Geth's abi library submits txes with the exact estimate the node returns, employing no padding. This means a transaction may revert should another arriving just before even slightly change the transaction's codepath. To account for this, we've added a `GasMargin` field to `bind.TransactOpts` that [pads estimates][pad_estimates_link] by the number of basis points set. + +### Conservation of L2 ETH + +The total amount of L2 ether in the system should not change except in controlled cases, such as when bridging. As a safety precaution, ArbOS checks Geth's [balance delta][conservation_link] each time a block is created, [alerting or panicking][alert_link] should conservation be violated. + +### MixDigest and ExtraData + +To aid with [outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners/stakers, to store the send count. + +### Retryable Support + +Retryables are mostly implemented in [ArbOS](/how-arbitrum-works/arbos/introduction.mdx#retryables). Some modifications were required in Geth to support them. + +- Added ScheduledTxes field to ExecutionResult. This lists transactions scheduled during the execution. To enable using this field, we also pass the ExecutionResult to callers of ApplyTransaction. +- Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retryable activated by the original call. This allows estimating gas to enable retryables. + +### Added accessors + +Added [`UnderlyingTransaction`][underlyingtransaction_link] to Message interface +Added [`GetCurrentTxLogs`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state/statedb_arbitrum.go) to StateDB +We created the AdvancedPrecompile interface, which executes and charges gas with the same function call. This is used by [Arbitrum's precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), and also wraps Geth's standard precompiles. + +### WASM build support + +The WASM Arbitrum executable does not support file operations. We created [`fileutil.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. + +### Types + +Arbitrum introduces a new [`signer`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arbitrum_signer.go), and multiple new [`transaction types`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go). + +### ReorgToOldBlock + +Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the [`ReorgToOldBlock`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/blockchain_arbitrum.go#L38) function to support reorging to a block that's an ancestor of current head. + +### Genesis block creation + +Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out [`WriteHeadBlock`][writeheadblock_link] from genesis.Commit and use it to commit non-zero genesis blocks. + +[pad_estimates_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/accounts/abi/bind/base.go#L355 +[conservation_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state/statedb.go#L42 +[alert_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L424 +[proof_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/system_tests/outbox_test.go#L26 +[merkle_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/merkleAccumulator/merkleAccumulator.go#L13 +[underlyingtransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L69 +[writeheadblock_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/genesis.go#L415 + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx index cd4c0400a..e92b34908 100644 --- a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx @@ -147,4 +147,411 @@ Because the Sequencer will be run by a trusted party at first, and will be decen Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. + + + + +import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; + +# ChallengeManager + +The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: + + + +## Block challenge + +The challenge begins by bisecting over global states (including block hashes). +Before actual machine execution is disputed, the dispute is narrowed down to an individual block. +Once the challenge has been bisected down to an individual block, +`challengeExecution` can be called by the current responder. +This operates similarly to a bisection in that the responder must provide a competing global state and machine state, +but it uses that information to transition to the execution challenge phase. + +## Execution challenge + +Once narrowed down to an individual block, the actual machine execution can be bisected. +Once the execution has been bisected down to an individual step, +`oneStepProveExecution` can be called by the current responder. +The current responder must provide proof data to execute a step of the machine. +If executing that step ends in a different state than was previously asserted, +the current responder wins the challenge. + +## General bisection protocol + +_**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ + +The `ChallengeLib` helper library contains a `hashChallengeState` method which hashes a list of segment hashes, +a start position, and a total segments length, which generates the `ChallengeLib.Challenge`'s `challengeStateHash`. +This is enough information to infer the position of each segment hash. +The challenge "degree" refers to the number of segment hashes minus one. +The distance (in steps) between one segment and the next is `floor(segmentsLength / degree)`, except for the +last pair of segments, where `segmentsLength % degree` is added to the normal distance, so that +the total distance is `segmentsLength`. + +A challenge begins with only two segments (a degree of one), which is the asserter's initial assertion. +Then, the bisection game begins on the challenger's turn. +In each round of the game, the current responder must choose an adjacent pair of segments to challenge. +By doing so, they are disputing their opponent's claim that starting with the first segment and executing +for the specified distance (number of steps) will result in the second segment. At this point the two parties +agree on the correctness of the first segment but disagree about the correctness of the second segment. +The responder must provide a bisection with a start segment equal to the first segment, but an end segment +different from the second segment. +In doing so, they break the challenge down into smaller distances, and it becomes their opponent's turn. +Each bisection must have degree `min(40, numStepsInChallengedSegment)`, ensuring the challenge makes progress. + +In addition, a segment with a length of only one step cannot be bisected. +What happens there is specific to the phase of the challenge, as either a `challengeExecution` or `oneStepProveExecution`. + +Note that unlike in a traditional bisection protocol, where one party proposes segments and the other decides which to challenge, +this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections +when challenging. + +## Winning the challenge + +Note that for the time being, winning the challenge isn't instant. +Instead, it simply makes the current responder the winner's opponent, +and sets the state hash to 0. In that state the party does not have any +valid moves, so it will eventually lose by timeout. +This is done as a precaution, so that if a challenge is resolved incorrectly, +there is time to diagnose and fix the error with a contract upgrade. + + + + + + + + +# One Step Proof Assumptions + +The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise +in a correct execution. This documents those assumptions about what's being executed. + +If a case is "unreachable", that is, the case is assumed to never arise in correct execution, +then the OSP can implement any instruction semantics in that case. + +- In a challenge between malicious parties, any case can arise. The challenge protocol must do + something safe in every case. But the instruction semantics can be weird in such cases because + if both parties to a challenge are malicious, the protocol doesn't care who wins the challenge. +- In a challenge with one honest party, the honest party will never need to one-step prove an + unreachable case. The honest party will only assert correct executions, so it will only have to + prove reachable cases. +- In a challenge with one honest party, the dishonest party could assert an execution that transitions + into an unreachable case, but such an execution must include an invalid execution of a reachable case + earlier in the assertion. Because a challenge involving an honest party will eventually require an OSP + over the first instruction where the parties disagree, the eventual OSP will be over the earlier point + of divergence, and not over the later execution from an unreachable case. + +In general, some unreachable cases will be detectable by the OSP checker and some will not. For safety, the +detectable unreachable cases should be defined by transition the machine into an error state, allowing +governance to eventually push an upgrade to recover from the error. An undetectable unreachable case, if +such a case were reached in correct execution, could lead to a security failure. + +The following assumptions, together, must prevent an unreachable case from arising in correct execution. + +## The WAVM code is generated by Arbitrator from valid WASM + +WAVM is the name of the custom instruction set similar to WASM used for proving. +Arbitrator transpiles WASM code into WAVM. +It also invokes wasm-validate from [wabt](https://github.com/WebAssembly/wabt) +(the WebAssembly Binary Toolkit) to ensure the input WASM is valid. +WAVM produced otherwise may not be executable, as it may try to close a non-existent block, +mismatch types, or do any other number of invalid things which are prevented by WASM validation. + +WAVM code generated from by Arbitrator from valid WASM is assumed to never encounter an unreachable case. + +## Inbox messages must not be too large + +The current method of inbox hashing requires the full inbox message be available for proving. +That message must not be too large as to prevent it from being supplied for proving, +which is enforced by the inboxes. + +The current length limit is 117,964 bytes, which is 90% of the +[max transaction size Geth will accept](https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/core/tx_pool.go#L53), +leaving 13,108 bytes for other proving data. + +## Requested preimages must be known and not too large + +WAVM has an opcode which resolves the preimage of a Keccak-256 hash. +This can only be executed if the preimage is already known to all nodes, +and can only be proven if the preimage isn't too long. Violations of this assumption are +undetectable by the OSP checker. + +The current length limit is 117,964 bytes for the reasons mentioned above. +Here's a list of which preimages may be requested by Nitro, and why they're known to all parties, +and not too large: + +### Block headers + +Nitro may request up to the last 256 L2 block headers. +The last block header is required to determine the current state, +and blocks before it are required to implement the `BLOCKHASH` evm instruction. + +This is safe as previous block headers are a fixed size, and are known to all nodes. + +### State trie access + +To resolve state, Nitro traverses the state trie by resolving preimages. + +This is safe as validators retain archive state of unconfirmed blocks, +each trie branch is of a fixed size, +and the only variable sized entry in the trie is contract code, +which is limited by EIP-170 to about 24KB. + + + + + + + + +# WASM to WAVM + +Not all WASM instructions are 1:1 with WAVM opcodes. +This document lists those which are not, and explains how they're expressed in WAVM. +Many of the WAVM representations use opcodes not in WASM, +which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). + +## `block` and `loop` + +In WASM, a block contains instructions. +Branch instructions exit a fixed number of blocks, jumping to their destination. +A normal `block`'s destination is the end of the block, whereas a `loop`'s destination is the start of the loop. + +In WAVM, instructions are flat. +At transpilation time, any branch instructions are replaced with jumps to the corresponding block's destination. +This means that WAVM interpreters don't need to track blocks, and thus block instructions are unnecessary. + +## `if` and `else` + +These are translated to a block with an `ArbitraryJumpIf` as follows: + +``` +begin block with endpoint end + conditional jump to else + [instructions inside if statement] + branch + else: [instructions inside else statement] +end +``` + +## `br` and `br_if` + +`br` and `br_if` are translated into `ArbitraryJump` and `ArbitraryJumpIf` respectively. +The jump locations can be known at transpilation time, making blocks obsolete. + +## `br_table` + +`br_table` is translated to a check for each possible branch in the table, +and then if none of the checks hit, a branch of the default level. + +Each of the non-default branches has a conditional jump to a section afterwards, +containing a `drop` for the selector, and then a jump to the target branch. + +## `local.tee` + +`local.tee` is translated to a WAVM `Dup` and then a `LocalSet`. + +## `return` + +To translate a return, the number of return values must be known from the function signature. +A WAVM `MoveFromStackToInternal` is added for each return value. +Then, a loop checks `IsStackBoundary` (which implicitly pops a value) until it's true and the stack boundary has been popped. +Next, a `MoveFromInternalToStack` is added for each return value to put the return values back on the stack. +Finally, a WAVM `Return` is added, returning control flow to the caller. + +## Floating point instructions + +A floating point library module must be present to translate floating point instructions. +They are translated by bitcasting `f32` and `f64` arguments to `i32`s and `i64`s, +then a cross module call to the floating point library, +and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and `f64`s. + + + + + + + + +# WAVM Custom opcodes not in WASM + +In addition to the MVP WASM specification, +WAVM implements the multi value and sign extension ops WASM proposals. + +WAVM also implements the following unique opcodes, +which are not part of WASM nor any WASM proposal. + +## Invariants + +Many of these opcodes have implicit invariants about what's on the stack, +e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. +If these conditions are not satisfied, execution is generally not possible. +These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) + +## Codegen internal + +These are generated when breaking down a WASM instruction that does many things into many WAVM instructions which each do one thing. +For instance, a WASM `local.tee` is implemented in WAVM with `dup` and then `local.set`, the former of which doesn't exist in WASM. + +Other times, these opcodes help out an existing WASM opcode by splitting out functionality. +For instance, the WAVM `return` opcode by itself does not clean up the stack, +but its WASM->WAVM codegen includes a loop that utilizes `IsStackBoundary` to perform the stack cleanup +specified for WASM's `return`. + +| Opcode | Name | Description | +| ------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0x8000 | EndBlock | Pops an item from the block stack. | +| 0x8001 | EndBlockIf | Peeks the top value on the stack, assumed an i32. If non-zero, pops an item from the block stack. | +| 0x8002 | InitFrame | Pops a caller module index i32, then a caller module internals offset i32, and finally a return InternalRef from the stack. Creates a stack frame with the popped info and the locals merkle root in proving argument data. | +| 0x8003 | ArbitraryJumpIf | Pops an i32 from the stack. If non-zero, jumps to the program counter in the argument data. | +| 0x8004 | PushStackBoundary | Pushes a stack boundary to the stack. | +| 0x8005 | MoveFromStackToInternal | Pops an item from the stack and pushes it to the internal stack. | +| 0x8006 | MoveFromInternalToStack | Pops an item from the internal stack and pushes it to the stack. | +| 0x8007 | IsStackBoundary | Pops an item from the stack. If a stack boundary, pushes an i32 with value 1. Otherwise, pushes an i32 with value 0. | +| 0x8008 | Dup | Peeks an item from the stack and pushes another copy of that item to the stack. | + +The above opcodes eliminate the need for the following WASM opcodes (which are transpiled into other WAVM opcodes): + +- loop +- if/else +- br_table +- local.tee + +## Linking + +This is only generated to link modules together. +Each import is replaced with a local function consisting primarily of this opcode, +which handles the actual work needed to change modules. + +| Opcode | Name | Description | +| ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0x8009 | CrossModuleCall | Pushes the current program counter, module number, and module's internals offset to the stack. Then splits its argument data into the lower 32 bits being a function index, and the upper 32 bits being a module index, and jumps to the beginning of that function. | + +## Host calls + +These are only used in the implementation of "host calls". +Each of these has an equivalent host call method, which can be invoked from libraries. +The exception is `CallerModuleInternalCall`, +which is used for the implementation of all of the `wavm_caller_*` host calls. +Those calls are documented in `wavm-modules.mdx`. + +For these instruction descriptions, all pointers and offsets are represented as WASM i32s. + +| Opcode | Name | Description | +| ------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0x800A | CallerModuleInternalCall | Pushes the current program counter, module number, and module's internals offset (all i32s) to the stack. Then, it retrieves the caller module internals offset from the current stack frame. If 0, errors, otherwise, jumps to the caller module at function (internals offset + opcode argument data) and instruction 0. | +| 0x8010 | GetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, writes the global state bytes32 value of the specified index to the specified pointer in memory. | +| 0x8011 | SetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, reads a bytes32 from the specified pointer in memory and sets the global state bytes32 value of the specified index to it. | +| 0x8012 | GetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, writes the global state u32 value of the specified index to the specified pointer in memory. | +| 0x8013 | SetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, reads a u64 from the specified pointer in memory and sets the global state u64 value of the specified index to it. | +| 0x8020 | ReadPreImage | Pops an offset and then a pointer from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Reads a 32 byte Keccak-256 hash from the specified pointer in memory. Writes up to 32 bytes of the preimage to that hash, beginning with the `offset` byte of the preimage. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | +| 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the sequencer inbox, 1 for the delayed inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | +| 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | + + + + + + + + +# WAVM Floating point implementation + +Implementing correct, consistent, and deterministic floating point operations directly in WAVM +(meaning both a Rust Arbitrator implementation and Solidity OSP implementation) +would be an extremely tricky endeavor. +WASM specifies floating point operations as being compliant to IEEE 754-2019, +which is not deterministic, and full of edge cases. + +Instead, floating point operations (apart from trivial bit-casts like i32 \<-\> f32) +are implemented using the C Berkeley SoftFloat-3e library running inside WAVM. +Arbitrator links other WAVM guests against this, +by replacing float point operations with cross module calls to the library. + +Berkeley SoftFloat does not implement all necessary floating point operations, however. +Most importantly, it does not provide a min function, despite IEEE 754-2019 specifying one. +The implementation of these operations, +along with the export of convenient APIs for WASM opcode implementations, +are contained in bindings32.c for 32 bit integers and bindings64.c for 64 bit integers. + +This ensures that floating point operations are deterministic and consistent between Arbitrator and the OSP, +as they are implemented exclusively using operations already known to be deterministic and consistent. +However, it does not ensure that the floating point operations are perfectly compliant to the WASM specification. +Go uses floating points in its JS\<-\>Go WASM interface, +and floating points may be used outside core state transition code for imprecise computations, +but the former is well exercised as used in Nitro, +and the latter generally doesn't rely on details like the minimum of NaN and infinity. + +## Known divergences from the WASM specification + +Floating point to integer truncation will saturate on overflow, instead of erroring. +This is generally safer, because on x86, overflowing simply produces an undefined result. +A WASM proposal exists to add new opcodes which are defined to saturate, but it's not widely adopted. + + + + + + + +# WAVM Modules + +WASM natively has a notion of modules. +Normally, in WASM, a module is the entire program. +A `.wasm` file represents one module, and generally they aren't combined. +An exception to this is C compiled via Clang, where wasm files are also used as object files, +but [its linking scheme](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.mdx) is not supported in other languages. + +In WAVM this is extended to make the executing program composed of multiple modules. +These may call each other, and library modules may write to their caller's memory to return results. + +## The entrypoint module + +The entrypoint module is where execution begins. +It calls modules' `start` functions if specified, +and then calls the main module's main function, which is language specific. +For Go it sets argv to `["js"]` to match the JS environment, and calls `run`. +For Rust it calls `main` with no arguments. + +## Library exports + +Libraries may export functions with the name pattern `module__name`, +which future libraries or the main module can import as `"module" "name"`. + +For instance, this is used for wasi-stub to provide functions rust imports according +to the WebAssembly System Interface. + +## Floating point operations + +To provide floating point operations for future libraries, +the soft float library exports functions which perform floating point ops. +These have the same name as the WASM instruction names, except `.` is replaced with `_`. +Their type signature is also the same, except all `f32`s and `f64`s are bitcasted to `i32`s and `i64`s. + +Future modules can implicitly use these by using WASM floating point operations, +which are replaced at the WASM->WAVM level with bitcasts and cross module calls to these functions. + +## WAVM guest calls + +Libraries may call the main module's exports via `"env" "wavm_guest_call__*"`. + +For instance, go-stub calls Go's resume function when queueing async events +via `wavm_guest_call_resume()`, and then retrieves the new stack pointer with +`wavm_guest_call_getsp()`. + +## Caller module internals call + +Every stack frame retains its caller module and its caller module's "internals offset", +which is the first internal function index. +WAVM appends 4 "internal" functions to each module, which perform a memory load or store of 1 or 4 bytes. + +Via `wavm_caller_{load,store}{8,32}`, a library may access its caller's memory, +which is implemented by calling these internal functions of the caller's module. +Only libraries can access their caller's memory; the main module cannot. + +For instance, this is used to read arguments from and write return values to the Go stack, +when Go calls into go-stub. + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/arbos/geth.mdx b/arbitrum-docs/how-arbitrum-works/arbos/geth.mdx deleted file mode 100644 index 0abe01222..000000000 --- a/arbitrum-docs/how-arbitrum-works/arbos/geth.mdx +++ /dev/null @@ -1,292 +0,0 @@ ---- -author: dzgoldman ---- - -# Geth - -Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This document will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. - -We store ArbOS's state at an address inside a Geth `statedb`. In doing so, ArbOS inherits the `statedb`'s statefulness and lifetime properties. For example, a transaction's direct state changes to ArbOS are discarded upon a revert. - -**0xA4B05FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF**
-The fictional account representing ArbOS - -:::info - -Please note any links on this page may be referencing old releases of Nitro or our fork of Geth. While we try to keep this up to date and most of this should be stable, please check against latest releases for [Nitro](https://github.com/OffchainLabs/nitro/releases) and [Geth](https://github.com/OffchainLabs/go-ethereum/releases) for most recent changes. - -::: - -## Hooks - -Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. - -Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#EnableArbOS) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. - -- `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` - - `core.NewStateTransition` - - [`ReadyEVMForL2`](#ReadyEVMForL2) - - `core.TransitionDb` - - [`StartTxHook`](#StartTxHook) - - `core.transitionDbImpl` - - if `IsArbitrum()` remove tip - - [`GasChargingHook`](#GasChargingHook) - - `evm.Call` - - `core.vm.EVMInterpreter.Run` - - [`PushCaller`](#PushCaller) - - [`PopCaller`](#PopCaller) - - `core.StateTransition.refundGas` - - [`ForceRefundGas`](#ForceRefundGas) - - [`NonrefundableGas`](#NonrefundableGas) - - [`EndTxHook`](#EndTxHook) - - added return parameter: `transactionResult` - -What follows is an overview of each hook, in chronological order. - -### [`ReadyEVMForL2`][readyevmforl2_link]{#ReadyEVMForL2} - -A call to [`ReadyEVMForL2`][readyevmforl2_link] installs the other transaction-specific hooks into each Geth [`EVM`][evm_link] right before it performs a state transition. Without this call, the state transition will instead use the default [`DefaultTxProcessor`][defaulttxprocessor_link] and get exactly the same results as vanilla Geth. A [`TxProcessor`][txprocessor_link] object is what carries these hooks and the associated Arbitrum-specific state during the transaction's lifetime. - -### [`StartTxHook`][starttxhook_link]{#StartTxHook} - -The [`StartTxHook`][starttxhook_link] is called by Geth before a transaction starts executing. This allows ArbOS to handle two Arbitrum-specific transaction types. - -If the transaction is `ArbitrumDepositTx`, ArbOS adds balance to the destination account. This is safe because the L1 bridge submits such a transaction only after collecting the same amount of funds on L1. - -If the transaction is an `ArbitrumSubmitRetryableTx`, ArbOS creates a retryable based on the transaction's fields. If the transaction includes sufficient gas, ArbOS schedules a retry of the new retryable. - -The hook returns `true` for both of these transaction types, signifying that the state transition is complete. - -### [`GasChargingHook`][gascharginghook_link]{#GasChargingHook} - -This fallible hook ensures the user has enough funds to pay their poster's L1 calldata costs. If not, the transaction is reverted and the [`EVM`][evm_link] does not start. In the common case that the user can pay, the amount paid for calldata is set aside for later reimbursement of the poster. All other fees go to the network account, as they represent the transaction's burden on validators and nodes more generally. - -If the user attempts to purchase compute gas in excess of ArbOS's per-block gas limit, the difference is [set aside][difference_set_aside_link] and [refunded later][refunded_later_link] via [`ForceRefundGas`](#ForceRefundGas) so that only the gas limit is used. Note that the limit observed may not be the same as that seen [at the start of the block][that_seen_link] if ArbOS's larger gas pool falls below the [`MaxPerBlockGasLimit`][max_perblock_limit_link] while processing the block's previous transactions. - -[difference_set_aside_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L407 -[refunded_later_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L419 -[that_seen_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L176 -[max_perblock_limit_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/l2pricing/l2pricing.go#L86 - -### [`PushCaller`][pushcaller_link]{#PushCaller} - -These hooks track the callers within the EVM callstack, pushing and popping as calls are made and complete. This provides [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) with info about the callstack, which it uses to implement the methods `WasMyCallersAddressAliased` and `MyCallersAddressWithoutAliasing`. - -### [`L1BlockHash`][l1blockhash_link] - -In Arbitrum, the BlockHash and Number operations return data that relies on underlying L1 blocks instead of L2 blocks, to accommodate the normal use-case of these opcodes, which often assume Ethereum-like time passes between different blocks. The L1BlockHash and L1BlockNumber hooks have the required data for these operations. - -### [`ForceRefundGas`][forcerefundgas_link]{#ForceRefundGas} - -This hook allows ArbOS to add additional refunds to the user's tx. This is currently only used to refund any compute gas purchased in excess of ArbOS's per-block gas limit during the [`GasChargingHook`](#GasChargingHook). - -### [`NonrefundableGas`][nonrefundablegas_link]{#NonrefundableGas} - -Because poster costs come at the expense of L1 aggregators and not the network more broadly, the amounts paid for L1 calldata should not be refunded. This hook provides Geth access to the equivalent amount of L2 gas the poster's cost equals, ensuring this amount is not reimbursed for network-incentivized behaviors like freeing storage slots. - -### [`EndTxHook`][endtxhook_link]{#EndTxHook} - -The [`EndTxHook`][endtxhook_link] is called after the [`EVM`][evm_link] has returned a transaction's result, allowing one last opportunity for ArbOS to intervene before the state transition is finalized. Final gas amounts are known at this point, enabling ArbOS to credit the network and poster each's share of the user's gas expenditures as well as adjust the pools. The hook returns from the [`TxProcessor`][txprocessor_link] a final time, in effect discarding its state as the system moves on to the next transaction where the hook's contents will be set afresh. - -[applytransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_processor.go#L152 -[evm_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/vm/evm.go#L101 -[defaulttxprocessor_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/vm/evm_arbitrum.go#L42 -[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L38 -[starttxhook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L100 -[readyevmforl2_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbstate/geth-hook.go#L47 -[gascharginghook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L354 -[pushcaller_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L76 -[popcaller_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L80 -[forcerefundgas_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L425 -[nonrefundablegas_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L418 -[endtxhook_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L429 -[l1blockhash_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L617 -[l1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L600 - -## Interfaces and components - -### [`APIBackend`][apibackend_link] - -APIBackend implements the [`ethapi.Backend`][ethapi.backend_link] interface, which allows simple integration of the Arbitrum chain to existing Geth API. Most calls are answered using the Backend member. - -[apibackend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/apibackend.go#L34 -[ethapi.backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/backend.go#L42 - -### [`Backend`][backend_link] - -This struct was created as an Arbitrum equivalent to the [`Ethereum`][ethereum_link] struct. It is mostly glue logic, including a pointer to the ArbInterface interface. - -[backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/backend.go#L15 -[ethereum_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/eth/backend.go#L68 - -### [`ArbInterface`][arbinterface_link] - -This interface is the main interaction-point between geth-standard APIs and the Arbitrum chain. Geth APIs mostly either check status by working on the Blockchain struct retrieved from the [`Blockchain`][blockchain_link] call, or send transactions to Arbitrum using the [`PublishTransactions`][publishtransactions_link] call. - -[arbinterface_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L10 -[blockchain_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L12 -[publishtransactions_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L11 - -### [`RecordingKV`][recordingkv_link] - -RecordingKV is a read-only key-value store, which retrieves values from an internal trie database. All values accessed by a RecordingKV are also recorded internally. This is used to record all preimages accessed during block creation, which will be needed to prove execution of this particular block. -A [`RecordingChainContext`][recordingchaincontext_link] should also be used, to record which block headers the block execution reads (another option would be to always assume the last 256 block headers were accessed). -The process is simplified using two functions: [`PrepareRecording`][preparerecording_link] creates a stateDB and chaincontext objects, running block creation process using these objects records the required preimages, and [`PreimagesFromRecording`][preimagesfromrecording_link] function extracts the preimages recorded. - -[recordingkv_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L22 -[recordingchaincontext_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L123 -[preparerecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L152 -[preimagesfromrecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L174 - -## Transaction Types - -Nitro Geth includes a few L2-specific transaction types. Click on any to jump to their section. - -| Tx Type | Represents | Last Hook Reached   | Source | -| :------------------------------------------------ | :----------------------------------- | :------------------------- | ------ | -| [`ArbitrumUnsignedTx`][arbtxunsigned] | An L1 to L2 message | [`EndTxHook`][he] | Bridge | -| [`ArbitrumContractTx`][arbtxcontract] | A nonce-less L1 to L2 message   | [`EndTxHook`][he] | Bridge | -| [`ArbitrumDepositTx`][arbtxdeposit] | A user deposit | [`StartTxHook`][hs] | Bridge | -| [`ArbitrumSubmitRetryableTx`][arbtxsubmit]   | Creating a retryable | [`StartTxHook`][hs]   | Bridge | -| [`ArbitrumRetryTx`][arbtxretry] | A retryable redeem attempt | [`EndTxHook`][he] | L2 | -| [`ArbitrumInternalTx`][arbtxinternal] | ArbOS state update | [`StartTxHook`][hs] | ArbOS | - -[arbtxunsigned]: #ArbitrumUnsignedTx -[arbtxcontract]: #ArbitrumContractTx -[arbtxsubmit]: #ArbitrumSubmitRetryableTx -[arbtxretry]: #ArbitrumRetryTx -[arbtxdeposit]: #ArbitrumDepositTx -[arbtxinternal]: #ArbitrumInternalTx -[hs]: #StartTxHook -[he]: #EndTxHook - -The following reference documents each type. - -### [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link]{#ArbitrumUnsignedTx} - -Provides a mechanism for a user on L1 to message a contract on L2. This uses the bridge for authentication rather than requiring the user's signature. Note, the user's acting address will be remapped on L2 to distinguish them from a normal L2 caller. - -### [`ArbitrumContractTx`][arbitrumcontracttx_link]{#ArbitrumContractTx} - -These are like an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link] but are intended for smart contracts. These use the bridge's unique, sequential nonce rather than requiring the caller specify their own. An L1 contract may still use an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link], but doing so may necessitate tracking the nonce in L1 state. - -### [`ArbitrumDepositTx`][arbitrumdeposittx_link]{#ArbitrumDepositTx} - -Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. - -### [`ArbitrumSubmitRetryableTx`][arbitrumsubmitretryabletx_link]{#ArbitrumSubmitRetryableTx} - -Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. - -### [`ArbitrumRetryTx`][arbitrumretrytx_link]{#ArbitrumRetryTx} - -These are scheduled by calls to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile and via retryable auto-redemption. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. - -### [`ArbitrumInternalTx`][arbitruminternaltx_link]{#ArbitrumInternalTx} - -Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a transaction of this type to update its state in-between user-generated transactions. Such a transaction has a [`Type`][internaltype_link] field signifying the state it will update, though currently this is just future-proofing as there's only one value it may have. Below are the internal transaction types. - -#### [`InternalTxStartBlock`][arbinternaltxstartblock_link] - -Updates the L1 block number and L1 base fee. This transaction [is generated][block_generated_link] whenever a new block is created. They are [guaranteed to be the first][block_first_link] in their L2 block. - -[arbitrumunsignedtx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L43 -[arbitrumcontracttx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L104 -[arbitrumsubmitretryabletx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L232 -[arbitrumretrytx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L161 -[arbitrumdeposittx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L338 -[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/internal_tx.go -[internaltype_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L387 -[arbinternaltxstartblock_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/internal_tx.go#L22 -[block_generated_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L181 -[block_first_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L182 - -## Transaction Run Modes and Underlying Transactions - -A [geth message][geth_message_link] may be processed for various purposes. For example, a message may be used to estimate the gas of a contract call, whereas another may perform the corresponding state transition. Nitro Geth denotes the intent behind a message by means of its [`TxRunMode`][txrunmode_link], [which it sets][set_run_mode_link] before processing it. ArbOS uses this info to make decisions about the transaction the message ultimately constructs. - -A message [derived from a transaction][asmessage_link] will carry that transaction in a field accessible via its [`UnderlyingTransaction`][underlying_link] method. While this is related to the way a given message is used, they are not one-to-one. The table below shows the various run modes and whether each could have an underlying transaction. - -| Run Mode | Scope | Carries an Underlying Tx? | -| :--------------------------------------- | :---------------------- | :----------------------------------------------------------------------------------------------------------- | -| [`MessageCommitMode`][mc0] | state transition   | always | -| [`MessageGasEstimationMode`][mc1]   | gas estimation | when created via [NodeInterface](/build-decentralized-apps/nodeinterface/02-reference.mdx) or when scheduled | -| [`MessageEthcallMode`][mc2] | eth_calls | never | - -[mc0]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L654 -[mc1]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L655 -[mc2]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L656 -[geth_message_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L634 -[txrunmode_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L701 -[set_run_mode_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/api.go#L955 -[asmessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L676 -[underlying_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L700 - -## Arbitrum Chain Parameters - -Nitro's Geth may be configured with the following [l2-specific chain parameters][chain_params_link]. These allow the rollup creator to customize their rollup at genesis. - -### `EnableArbos` - -Introduces [ArbOS](/how-arbitrum-works/arbos/introduction.mdx), converting what would otherwise be a vanilla L1 chain into an L2 Arbitrum rollup. - -### `AllowDebugPrecompiles` - -Allows access to debug precompiles. Not enabled for Arbitrum One. When false, calls to debug precompiles will always revert. - -### `DataAvailabilityCommittee` - -Currently does nothing besides indicate that the rollup will access a data availability service for preimage resolution in the future. This is not enabled for Arbitrum One, which is a strict state-function of its L1 inbox messages. - -[chain_params_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/params/config_arbitrum.go#L25 - -## Miscellaneous Geth Changes - -### ABI Gas Margin - -Vanilla Geth's abi library submits txes with the exact estimate the node returns, employing no padding. This means a transaction may revert should another arriving just before even slightly change the transaction's codepath. To account for this, we've added a `GasMargin` field to `bind.TransactOpts` that [pads estimates][pad_estimates_link] by the number of basis points set. - -### Conservation of L2 ETH - -The total amount of L2 ether in the system should not change except in controlled cases, such as when bridging. As a safety precaution, ArbOS checks Geth's [balance delta][conservation_link] each time a block is created, [alerting or panicking][alert_link] should conservation be violated. - -### MixDigest and ExtraData - -To aid with [outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners/stakers, to store the send count. - -### Retryable Support - -Retryables are mostly implemented in [ArbOS](/how-arbitrum-works/arbos/introduction.mdx#retryables). Some modifications were required in Geth to support them. - -- Added ScheduledTxes field to ExecutionResult. This lists transactions scheduled during the execution. To enable using this field, we also pass the ExecutionResult to callers of ApplyTransaction. -- Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retryable activated by the original call. This allows estimating gas to enable retryables. - -### Added accessors - -Added [`UnderlyingTransaction`][underlyingtransaction_link] to Message interface -Added [`GetCurrentTxLogs`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state/statedb_arbitrum.go) to StateDB -We created the AdvancedPrecompile interface, which executes and charges gas with the same function call. This is used by [Arbitrum's precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), and also wraps Geth's standard precompiles. - -### WASM build support - -The WASM Arbitrum executable does not support file operations. We created [`fileutil.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. - -### Types - -Arbitrum introduces a new [`signer`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arbitrum_signer.go), and multiple new [`transaction types`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go). - -### ReorgToOldBlock - -Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the [`ReorgToOldBlock`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/blockchain_arbitrum.go#L38) function to support reorging to a block that's an ancestor of current head. - -### Genesis block creation - -Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out [`WriteHeadBlock`][writeheadblock_link] from genesis.Commit and use it to commit non-zero genesis blocks. - -[pad_estimates_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/accounts/abi/bind/base.go#L355 -[conservation_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state/statedb.go#L42 -[alert_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L424 -[proof_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/system_tests/outbox_test.go#L26 -[merkle_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/merkleAccumulator/merkleAccumulator.go#L13 -[underlyingtransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L69 -[writeheadblock_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/genesis.go#L415 diff --git a/arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx b/arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx deleted file mode 100644 index d9083022c..000000000 --- a/arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx +++ /dev/null @@ -1,102 +0,0 @@ -# ArbOS - -ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS accounts for and manages network resources, produces blocks from incoming messages, and operates its instrumented instance of Geth for smart contract execution. - -## Precompiles - -ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. Visit the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx) for more information about how these work, and the [precompiles reference page](/build-decentralized-apps/precompiles/02-reference.mdx) for a full reference of the precompiles available in Arbitrum chains. - -A precompile consists of a solidity interface in [`contracts/src/precompiles/`][nitro_precompiles_dir] and a corresponding Golang implementation in [`precompiles/`][precompiles_dir]. Using Geth's ABI generator, [`solgen/gen.go`][gen_file] generates [`solgen/go/precompilesgen/precompilesgen.go`][precompilesgen_link], which collects the ABI data of the precompiles. The [runtime installer][installer_link] uses this generated file to check the type safety of each precompile's implementer. - -[The installer][installer_link] uses runtime reflection to ensure each implementer has all the right methods and signatures. This includes restricting access to stateful objects like the EVM and statedb based on the declared purity. Additionally, the installer verifies and populates event function pointers to provide each precompile the ability to emit logs and know their gas costs. Additional configuration like restricting a precompile's methods to only be callable by chain owners is possible by adding precompile wrappers like [`ownerOnly`][owneronly_link] and [`debugOnly`][debugonly_link] to their [installation entry][installation_link]. - -The calling, dispatching, and recording of precompile methods are done via runtime reflection as well. This avoids any human error manually parsing and writing bytes could introduce, and uses Geth's stable APIs for [packing and unpacking][packing_link] values. - -Each time a transaction calls a method of an L2-specific precompile, a [`call context`][call_context_link] is created to track and record the gas burnt. For convenience, it also provides access to the public fields of the underlying [`TxProcessor`][txprocessor_link]. Because sub-transactions could revert without updates to this struct, the [`TxProcessor`][txprocessor_link] only makes public that which is safe, such as the amount of L1 calldata paid by the top level transaction. - -[nitro_precompiles_dir]: https://github.com/OffchainLabs/nitro-contracts/tree/main/src/precompiles -[precompiles_dir]: https://github.com/OffchainLabs/nitro/tree/master/precompiles -[installer_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L379 -[installation_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L403 -[gen_file]: https://github.com/OffchainLabs/nitro/blob/master/solgen/gen.go -[owneronly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L58 -[debugonly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L23 -[precompilesgen_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/solgen/gen.go#L55 -[packing_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L438 -[call_context_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/context.go#L26 - -## Messages - -An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. - -[l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 -[produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 - -## Retryables - -A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). - -## ArbOS State - -ArbOS's state is viewed and modified via [`ArbosState`][arbosstate_link] objects, which provide convenient abstractions for working with the underlying data of its [`backingStorage`][backingstorage_link]. The backing storage's [keyed subspace strategy][subspace_link] makes possible [`ArbosState`][arbosstate_link]'s convenient getters and setters, minimizing the need to directly work with the specific keys and values of the underlying storage's [`stateDB`][statedb_link]. - -Because two [`ArbosState`][arbosstate_link] objects with the same [`backingStorage`][backingstorage_link] contain and mutate the same underlying state, different [`ArbosState`][arbosstate_link] objects can provide different views of ArbOS's contents. [`Burner`][burner_link] objects, which track gas usage while working with the [`ArbosState`][arbosstate_link], provide the internal mechanism for doing so. Some are read-only, causing transactions to revert with `vm.ErrWriteProtection` upon a mutating request. Others demand the caller have elevated privileges. While yet others dynamically charge users when doing stateful work. For safety the kind of view is chosen when [`OpenArbosState()`][openarbosstate_link] creates the object and may never change. - -Much of ArbOS's state exists to facilitate its [precompiles](/build-decentralized-apps/precompiles/02-reference.mdx). The parts that aren't are detailed below. - -[arbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L36 -[backingstorage_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L51 -[statedb_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L66 -[subspace_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L21 -[openarbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L57 -[burner_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/burn/burn.go#L11 - -### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] - -ArbOS upgrades are scheduled to happen [when finalizing the first block][finalizeblock_link] after the [`upgradeTimestamp`][upgradetimestamp_link]. - -[arbosversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L37 -[upgradeversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L38 -[upgradetimestamp_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L39 -[finalizeblock_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L350 - -### [`blockhashes`][blockhashes_link] - -This component maintains the last 256 L1 block hashes in a circular buffer. This allows the [`TxProcessor`][txprocessor_link] to implement the `BLOCKHASH` and `NUMBER` opcodes as well as support precompile methods that involve the outbox. To avoid changing ArbOS state outside of a transaction, blocks made from messages with a new L1 block number update this info during an [`InternalTxUpdateL1BlockNumber`][internaltxupdatel1blocknumber_link] [`ArbitrumInternalTx`][arbitruminternaltx_link] that is included as the first transaction in the block. - -[blockhashes_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/blockhash/blockhash.go#L15 -[internaltxupdatel1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/internal_tx.go#L24 -[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L116 -[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 - -### [`l1PricingState`][l1pricingstate_link] - -In addition to supporting the [`ArbAggregator precompile`](/build-decentralized-apps/precompiles/02-reference.mdx#arbaggregator), the L1 pricing state provides tools for determining the L1 component of a transaction's gas costs. This part of the state tracks both the total amount of funds collected from transactions in L1 gas fees, as well as the funds spent by batch posters to post data batches on L1. - -Based on this information, ArbOS maintains an L1 data fee, also tracked as part of this state, which determines how much transactions will be charged for L1 fees. ArbOS dynamically adjusts this value so that fees collected are approximately equal to batch posting costs, over time. - -[l1pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l1pricing/l1pricing.go#L16 - -### [`l2PricingState`][l2pricingstate_link] - -The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. - -While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth#Hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. - -ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](geth#GasChargingHook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. - -The reason we need a per-block gas limit is that Arbitrator WAVM execution is much slower than native transaction execution. This means that there can only be so much gas -- which roughly translates to wall-clock time -- in an L2 block. It also provides an opportunity for ArbOS to limit the size of blocks should demand continue to surge even as the price rises. - -ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [sets sufficiently high][geth_pool_set_link] so as to never run out. This is safe since Geth's block limit exists to constrain the amount of work done per block, which ArbOS already does via its own per-block gas limit. Though it'll never run out, a block's transactions use the [same Geth gas pool][same_geth_pool_link] to maintain the invariant that the pool decreases monotonically after each tx. Block headers [use the Geth block limit][use_geth_pool_link] for internal consistency and to ensure gas estimation works. These are both distinct from the [`gasLeft`][per_block_limit_link] variable, which ephemerally exists outside of global state to both keep L2 blocks from exceeding ArbOS's per-block gas limit and to [deduct space][deduct_space_link] in situations where the state transition failed or [used negligible amounts][negligible_amounts_link] of compute gas. ArbOS does not need to persist [`gasLeft`][per_block_limit_link] because it is its _pool_ that induces a revert and because transactions use the Geth block limit during EVM execution. - -[l2pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l2pricing/l2pricing.go#L14 -[block_production_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L77 -[notify_pricer_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L336 -[maintain_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l2pricing/pools.go#L98 -[per_block_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L146 -[first_transaction_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L237 -[geth_pool_set_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L166 -[same_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L199 -[use_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L67 -[deduct_space_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L272 -[negligible_amounts_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L328 diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/challenge-manager.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/challenge-manager.mdx deleted file mode 100644 index 407347853..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/challenge-manager.mdx +++ /dev/null @@ -1,64 +0,0 @@ -import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; - -# ChallengeManager - -The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: - - - -## Block challenge - -The challenge begins by bisecting over global states (including block hashes). -Before actual machine execution is disputed, the dispute is narrowed down to an individual block. -Once the challenge has been bisected down to an individual block, -`challengeExecution` can be called by the current responder. -This operates similarly to a bisection in that the responder must provide a competing global state and machine state, -but it uses that information to transition to the execution challenge phase. - -## Execution challenge - -Once narrowed down to an individual block, the actual machine execution can be bisected. -Once the execution has been bisected down to an individual step, -`oneStepProveExecution` can be called by the current responder. -The current responder must provide proof data to execute a step of the machine. -If executing that step ends in a different state than was previously asserted, -the current responder wins the challenge. - -## General bisection protocol - -_**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ - -The `ChallengeLib` helper library contains a `hashChallengeState` method which hashes a list of segment hashes, -a start position, and a total segments length, which generates the `ChallengeLib.Challenge`'s `challengeStateHash`. -This is enough information to infer the position of each segment hash. -The challenge "degree" refers to the number of segment hashes minus one. -The distance (in steps) between one segment and the next is `floor(segmentsLength / degree)`, except for the -last pair of segments, where `segmentsLength % degree` is added to the normal distance, so that -the total distance is `segmentsLength`. - -A challenge begins with only two segments (a degree of one), which is the asserter's initial assertion. -Then, the bisection game begins on the challenger's turn. -In each round of the game, the current responder must choose an adjacent pair of segments to challenge. -By doing so, they are disputing their opponent's claim that starting with the first segment and executing -for the specified distance (number of steps) will result in the second segment. At this point the two parties -agree on the correctness of the first segment but disagree about the correctness of the second segment. -The responder must provide a bisection with a start segment equal to the first segment, but an end segment -different from the second segment. -In doing so, they break the challenge down into smaller distances, and it becomes their opponent's turn. -Each bisection must have degree `min(40, numStepsInChallengedSegment)`, ensuring the challenge makes progress. - -In addition, a segment with a length of only one step cannot be bisected. -What happens there is specific to the phase of the challenge, as either a `challengeExecution` or `oneStepProveExecution`. - -Note that unlike in a traditional bisection protocol, where one party proposes segments and the other decides which to challenge, -this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections -when challenging. - -## Winning the challenge - -Note that for the time being, winning the challenge isn't instant. -Instead, it simply makes the current responder the winner's opponent, -and sets the state hash to 0. In that state the party does not have any -valid moves, so it will eventually lose by timeout. -This is done as a precaution, so that if a challenge is resolved incorrectly, -there is time to diagnose and fix the error with a contract upgrade. diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx deleted file mode 100644 index 93e04a2d1..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx +++ /dev/null @@ -1,75 +0,0 @@ -# One Step Proof Assumptions - -The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise -in a correct execution. This documents those assumptions about what's being executed. - -If a case is "unreachable", that is, the case is assumed to never arise in correct execution, -then the OSP can implement any instruction semantics in that case. - -- In a challenge between malicious parties, any case can arise. The challenge protocol must do - something safe in every case. But the instruction semantics can be weird in such cases because - if both parties to a challenge are malicious, the protocol doesn't care who wins the challenge. -- In a challenge with one honest party, the honest party will never need to one-step prove an - unreachable case. The honest party will only assert correct executions, so it will only have to - prove reachable cases. -- In a challenge with one honest party, the dishonest party could assert an execution that transitions - into an unreachable case, but such an execution must include an invalid execution of a reachable case - earlier in the assertion. Because a challenge involving an honest party will eventually require an OSP - over the first instruction where the parties disagree, the eventual OSP will be over the earlier point - of divergence, and not over the later execution from an unreachable case. - -In general, some unreachable cases will be detectable by the OSP checker and some will not. For safety, the -detectable unreachable cases should be defined by transition the machine into an error state, allowing -governance to eventually push an upgrade to recover from the error. An undetectable unreachable case, if -such a case were reached in correct execution, could lead to a security failure. - -The following assumptions, together, must prevent an unreachable case from arising in correct execution. - -## The WAVM code is generated by Arbitrator from valid WASM - -WAVM is the name of the custom instruction set similar to WASM used for proving. -Arbitrator transpiles WASM code into WAVM. -It also invokes wasm-validate from [wabt](https://github.com/WebAssembly/wabt) -(the WebAssembly Binary Toolkit) to ensure the input WASM is valid. -WAVM produced otherwise may not be executable, as it may try to close a non-existent block, -mismatch types, or do any other number of invalid things which are prevented by WASM validation. - -WAVM code generated from by Arbitrator from valid WASM is assumed to never encounter an unreachable case. - -## Inbox messages must not be too large - -The current method of inbox hashing requires the full inbox message be available for proving. -That message must not be too large as to prevent it from being supplied for proving, -which is enforced by the inboxes. - -The current length limit is 117,964 bytes, which is 90% of the -[max transaction size Geth will accept](https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/core/tx_pool.go#L53), -leaving 13,108 bytes for other proving data. - -## Requested preimages must be known and not too large - -WAVM has an opcode which resolves the preimage of a Keccak-256 hash. -This can only be executed if the preimage is already known to all nodes, -and can only be proven if the preimage isn't too long. Violations of this assumption are -undetectable by the OSP checker. - -The current length limit is 117,964 bytes for the reasons mentioned above. -Here's a list of which preimages may be requested by Nitro, and why they're known to all parties, -and not too large: - -### Block headers - -Nitro may request up to the last 256 L2 block headers. -The last block header is required to determine the current state, -and blocks before it are required to implement the `BLOCKHASH` evm instruction. - -This is safe as previous block headers are a fixed size, and are known to all nodes. - -### State trie access - -To resolve state, Nitro traverses the state trie by resolving preimages. - -This is safe as validators retain archive state of unconfirmed blocks, -each trie branch is of a fixed size, -and the only variable sized entry in the trie is contract code, -which is limited by EIP-170 to about 24KB. diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wasm-wavm.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/wasm-wavm.mdx deleted file mode 100644 index 941fefb5a..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wasm-wavm.mdx +++ /dev/null @@ -1,61 +0,0 @@ -# WASM to WAVM - -Not all WASM instructions are 1:1 with WAVM opcodes. -This document lists those which are not, and explains how they're expressed in WAVM. -Many of the WAVM representations use opcodes not in WASM, -which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). - -## `block` and `loop` - -In WASM, a block contains instructions. -Branch instructions exit a fixed number of blocks, jumping to their destination. -A normal `block`'s destination is the end of the block, whereas a `loop`'s destination is the start of the loop. - -In WAVM, instructions are flat. -At transpilation time, any branch instructions are replaced with jumps to the corresponding block's destination. -This means that WAVM interpreters don't need to track blocks, and thus block instructions are unnecessary. - -## `if` and `else` - -These are translated to a block with an `ArbitraryJumpIf` as follows: - -``` -begin block with endpoint end - conditional jump to else - [instructions inside if statement] - branch - else: [instructions inside else statement] -end -``` - -## `br` and `br_if` - -`br` and `br_if` are translated into `ArbitraryJump` and `ArbitraryJumpIf` respectively. -The jump locations can be known at transpilation time, making blocks obsolete. - -## `br_table` - -`br_table` is translated to a check for each possible branch in the table, -and then if none of the checks hit, a branch of the default level. - -Each of the non-default branches has a conditional jump to a section afterwards, -containing a `drop` for the selector, and then a jump to the target branch. - -## `local.tee` - -`local.tee` is translated to a WAVM `Dup` and then a `LocalSet`. - -## `return` - -To translate a return, the number of return values must be known from the function signature. -A WAVM `MoveFromStackToInternal` is added for each return value. -Then, a loop checks `IsStackBoundary` (which implicitly pops a value) until it's true and the stack boundary has been popped. -Next, a `MoveFromInternalToStack` is added for each return value to put the return values back on the stack. -Finally, a WAVM `Return` is added, returning control flow to the caller. - -## Floating point instructions - -A floating point library module must be present to translate floating point instructions. -They are translated by bitcasting `f32` and `f64` arguments to `i32`s and `i64`s, -then a cross module call to the floating point library, -and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and `f64`s. diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx deleted file mode 100644 index bbf75c3b6..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx +++ /dev/null @@ -1,74 +0,0 @@ -# WAVM Custom opcodes not in WASM - -In addition to the MVP WASM specification, -WAVM implements the multi value and sign extension ops WASM proposals. - -WAVM also implements the following unique opcodes, -which are not part of WASM nor any WASM proposal. - -## Invariants - -Many of these opcodes have implicit invariants about what's on the stack, -e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. -If these conditions are not satisfied, execution is generally not possible. -These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) - -## Codegen internal - -These are generated when breaking down a WASM instruction that does many things into many WAVM instructions which each do one thing. -For instance, a WASM `local.tee` is implemented in WAVM with `dup` and then `local.set`, the former of which doesn't exist in WASM. - -Other times, these opcodes help out an existing WASM opcode by splitting out functionality. -For instance, the WAVM `return` opcode by itself does not clean up the stack, -but its WASM->WAVM codegen includes a loop that utilizes `IsStackBoundary` to perform the stack cleanup -specified for WASM's `return`. - -| Opcode | Name | Description | -| ------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0x8000 | EndBlock | Pops an item from the block stack. | -| 0x8001 | EndBlockIf | Peeks the top value on the stack, assumed an i32. If non-zero, pops an item from the block stack. | -| 0x8002 | InitFrame | Pops a caller module index i32, then a caller module internals offset i32, and finally a return InternalRef from the stack. Creates a stack frame with the popped info and the locals merkle root in proving argument data. | -| 0x8003 | ArbitraryJumpIf | Pops an i32 from the stack. If non-zero, jumps to the program counter in the argument data. | -| 0x8004 | PushStackBoundary | Pushes a stack boundary to the stack. | -| 0x8005 | MoveFromStackToInternal | Pops an item from the stack and pushes it to the internal stack. | -| 0x8006 | MoveFromInternalToStack | Pops an item from the internal stack and pushes it to the stack. | -| 0x8007 | IsStackBoundary | Pops an item from the stack. If a stack boundary, pushes an i32 with value 1. Otherwise, pushes an i32 with value 0. | -| 0x8008 | Dup | Peeks an item from the stack and pushes another copy of that item to the stack. | - -The above opcodes eliminate the need for the following WASM opcodes (which are transpiled into other WAVM opcodes): - -- loop -- if/else -- br_table -- local.tee - -## Linking - -This is only generated to link modules together. -Each import is replaced with a local function consisting primarily of this opcode, -which handles the actual work needed to change modules. - -| Opcode | Name | Description | -| ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0x8009 | CrossModuleCall | Pushes the current program counter, module number, and module's internals offset to the stack. Then splits its argument data into the lower 32 bits being a function index, and the upper 32 bits being a module index, and jumps to the beginning of that function. | - -## Host calls - -These are only used in the implementation of "host calls". -Each of these has an equivalent host call method, which can be invoked from libraries. -The exception is `CallerModuleInternalCall`, -which is used for the implementation of all of the `wavm_caller_*` host calls. -Those calls are documented in `wavm-modules.mdx`. - -For these instruction descriptions, all pointers and offsets are represented as WASM i32s. - -| Opcode | Name | Description | -| ------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0x800A | CallerModuleInternalCall | Pushes the current program counter, module number, and module's internals offset (all i32s) to the stack. Then, it retrieves the caller module internals offset from the current stack frame. If 0, errors, otherwise, jumps to the caller module at function (internals offset + opcode argument data) and instruction 0. | -| 0x8010 | GetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, writes the global state bytes32 value of the specified index to the specified pointer in memory. | -| 0x8011 | SetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, reads a bytes32 from the specified pointer in memory and sets the global state bytes32 value of the specified index to it. | -| 0x8012 | GetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, writes the global state u32 value of the specified index to the specified pointer in memory. | -| 0x8013 | SetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, reads a u64 from the specified pointer in memory and sets the global state u64 value of the specified index to it. | -| 0x8020 | ReadPreImage | Pops an offset and then a pointer from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Reads a 32 byte Keccak-256 hash from the specified pointer in memory. Writes up to 32 bytes of the preimage to that hash, beginning with the `offset` byte of the preimage. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | -| 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the sequencer inbox, 1 for the delayed inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | -| 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-floats.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-floats.mdx deleted file mode 100644 index 1431c5b06..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-floats.mdx +++ /dev/null @@ -1,32 +0,0 @@ -# WAVM Floating point implementation - -Implementing correct, consistent, and deterministic floating point operations directly in WAVM -(meaning both a Rust Arbitrator implementation and Solidity OSP implementation) -would be an extremely tricky endeavor. -WASM specifies floating point operations as being compliant to IEEE 754-2019, -which is not deterministic, and full of edge cases. - -Instead, floating point operations (apart from trivial bit-casts like i32 \<-\> f32) -are implemented using the C Berkeley SoftFloat-3e library running inside WAVM. -Arbitrator links other WAVM guests against this, -by replacing float point operations with cross module calls to the library. - -Berkeley SoftFloat does not implement all necessary floating point operations, however. -Most importantly, it does not provide a min function, despite IEEE 754-2019 specifying one. -The implementation of these operations, -along with the export of convenient APIs for WASM opcode implementations, -are contained in bindings32.c for 32 bit integers and bindings64.c for 64 bit integers. - -This ensures that floating point operations are deterministic and consistent between Arbitrator and the OSP, -as they are implemented exclusively using operations already known to be deterministic and consistent. -However, it does not ensure that the floating point operations are perfectly compliant to the WASM specification. -Go uses floating points in its JS\<-\>Go WASM interface, -and floating points may be used outside core state transition code for imprecise computations, -but the former is well exercised as used in Nitro, -and the latter generally doesn't rely on details like the minimum of NaN and infinity. - -## Known divergences from the WASM specification - -Floating point to integer truncation will saturate on overflow, instead of erroring. -This is generally safer, because on x86, overflowing simply produces an undefined result. -A WASM proposal exists to add new opcodes which are defined to saturate, but it's not widely adopted. diff --git a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-modules.mdx b/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-modules.mdx deleted file mode 100644 index 1bace30e4..000000000 --- a/arbitrum-docs/how-arbitrum-works/fraud-proofs/wavm-modules.mdx +++ /dev/null @@ -1,57 +0,0 @@ -# WAVM Modules - -WASM natively has a notion of modules. -Normally, in WASM, a module is the entire program. -A `.wasm` file represents one module, and generally they aren't combined. -An exception to this is C compiled via Clang, where wasm files are also used as object files, -but [its linking scheme](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.mdx) is not supported in other languages. - -In WAVM this is extended to make the executing program composed of multiple modules. -These may call each other, and library modules may write to their caller's memory to return results. - -## The entrypoint module - -The entrypoint module is where execution begins. -It calls modules' `start` functions if specified, -and then calls the main module's main function, which is language specific. -For Go it sets argv to `["js"]` to match the JS environment, and calls `run`. -For Rust it calls `main` with no arguments. - -## Library exports - -Libraries may export functions with the name pattern `module__name`, -which future libraries or the main module can import as `"module" "name"`. - -For instance, this is used for wasi-stub to provide functions rust imports according -to the WebAssembly System Interface. - -## Floating point operations - -To provide floating point operations for future libraries, -the soft float library exports functions which perform floating point ops. -These have the same name as the WASM instruction names, except `.` is replaced with `_`. -Their type signature is also the same, except all `f32`s and `f64`s are bitcasted to `i32`s and `i64`s. - -Future modules can implicitly use these by using WASM floating point operations, -which are replaced at the WASM->WAVM level with bitcasts and cross module calls to these functions. - -## WAVM guest calls - -Libraries may call the main module's exports via `"env" "wavm_guest_call__*"`. - -For instance, go-stub calls Go's resume function when queueing async events -via `wavm_guest_call_resume()`, and then retrieves the new stack pointer with -`wavm_guest_call_getsp()`. - -## Caller module internals call - -Every stack frame retains its caller module and its caller module's "internals offset", -which is the first internal function index. -WAVM appends 4 "internal" functions to each module, which perform a memory load or store of 1 or 4 bytes. - -Via `wavm_caller_{load,store}{8,32}`, a library may access its caller's memory, -which is implemented by calling these internal functions of the caller's module. -Only libraries can access their caller's memory; the main module cannot. - -For instance, this is used to read arguments from and write return values to the Go stack, -when Go calls into go-stub. From 2f5214d2d5eddf15a1b540132251082c56783380 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 21 Nov 2024 15:51:23 -0600 Subject: [PATCH 03/75] adjusted sidebars.js --- website/sidebars.js | 178 ++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 130 deletions(-) diff --git a/website/sidebars.js b/website/sidebars.js index 54aac2874..fa584892e 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -814,136 +814,56 @@ const sidebars = { collapsed: true, items: [ { - type: 'category', - label: 'Introductory concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/tx-lifecycle', - label: 'Transaction lifecycle', - }, - { - type: 'doc', - id: 'how-arbitrum-works/sequencer', - label: 'Sequencer', - }, - { - type: 'doc', - id: 'how-arbitrum-works/inside-anytrust', - label: 'AnyTrust protocol', - }, - { - type: 'category', - label: 'Gas / fees', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/gas-fees', - label: 'L2 gas and fees', - }, - { - type: 'doc', - id: 'how-arbitrum-works/l1-gas-pricing', - label: 'L1 pricing', - }, - ], - }, - ], + type: 'doc', + id: 'how-arbitrum-works/a-gentle-introduction', + label: 'A gentle introduction', }, { - type: 'category', - label: 'Advanced concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/inside-arbitrum-nitro', - label: 'Deep dive: Inside Arbitrum', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf', - label: 'Deeper dive: Whitepaper', - }, - { - type: 'doc', - id: 'how-arbitrum-works/assertion-tree', - label: 'Assertion tree', - }, - { - type: 'doc', - id: 'how-arbitrum-works/why-nitro', - label: 'Nitro vs. Classic', - }, - { - type: 'category', - label: 'Cross-chain messaging', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/arbos/l1-l2-messaging', - label: 'L1-to-L2 messaging', - }, - { - type: 'doc', - id: 'how-arbitrum-works/arbos/l2-l1-messaging', - label: 'L2-to-L1 messaging', - }, - ], - }, - { - type: 'category', - label: 'ArbOS', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/arbos/introduction', - label: 'ArbOS', - }, - { - type: 'doc', - id: 'how-arbitrum-works/arbos/geth', - label: 'Geth', - }, - ], - }, - { - type: 'category', - label: 'Fraud proofs', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/challenge-manager', - label: 'Interactive challenges', - }, - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/osp-assumptions', - label: 'One step proof assumptions', - }, - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/wasm-wavm', - label: 'Wasm To WAVM', - }, - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/wavm-custom-opcodes', - label: 'Custom WAVM opcodes', - }, - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/wavm-floats', - label: 'WAVM floats', - }, - { - type: 'doc', - id: 'how-arbitrum-works/fraud-proofs/wavm-modules', - label: 'WAVM modules', - }, - ], - }, + type: 'doc', + id: 'how-arbitrum-works/transaction-lifecycle', + label: 'Transaction Lifecycle', + }, + { + type: 'doc', + id: 'how-arbitrum-works/sequencer', + label: 'Sequencer', + }, + { + type: 'doc', + id: 'how-arbitrum-works/state-transition-function', + label: 'State Transition Function', + }, + { + type: 'doc', + id: 'how-arbitrum-works/validation', + label: 'Validation', + }, + { + type: 'doc', + id: 'how-arbitrum-works/challenges', + label: 'Challenges', + }, + { + type: 'doc', + id: 'how-arbitrum-works/anytrust-protocol', + label: 'AnyTrust protocol', + }, + { + type: 'doc', + id: 'how-arbitrum-works/gas-fees', + label: 'Gas/fees', + }, + { + type: 'doc', + id: 'how-arbitrum-works/nitro-vs-classic', + label: 'Nitro vs. Classic', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf', + label: 'Nitro whitepaper', + }, + { type: 'category', label: 'The BoLD dispute protocol', @@ -996,8 +916,6 @@ const sidebars = { ], }, ], - }, - ], }, { type: 'doc', From 918832cfd2cb68714836b1e45776bf169f327f7f Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 22 Nov 2024 09:55:30 -0600 Subject: [PATCH 04/75] Restructured content further, added new sections --- .../how-arbitrum-works/03-sequencer.mdx | 254 ------------------ .../how-arbitrum-works/06-challenges.mdx | 85 ++++++ .../09-retryable-tickets.mdx | 247 +++++++++++++++++ .../10-cross-chain-messaging.mdx | 23 ++ ...vs-classic.mdx => 11-nitro-vs-classic.mdx} | 0 5 files changed, 355 insertions(+), 254 deletions(-) create mode 100644 arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx create mode 100644 arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx rename arbitrum-docs/how-arbitrum-works/{09-nitro-vs-classic.mdx => 11-nitro-vs-classic.mdx} (100%) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 64205f57d..a00303343 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -54,261 +54,7 @@ These L2-to-L1 tickets have unlimited lifetime, until they’re successfully red - -## Retryable Tickets - -Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. - -## Retryable Tickets Lifecycle - -Here we walk through the different stages of the lifecycle of a retryable ticket; (1) submission, (2) auto-redemption, and (3) manual redemption. - -### Submission - -1. Creating a retryable ticket is initiated with a call (direct or internal) to the `createRetryableTicket` function of the [`inbox` contract][inbox_link]. A ticket is guaranteed to be created if this call succeeds. Here, we describe parameters that need to be carefully set. Note that, this function forces the sender to provide a _reasonable_ amount of funds (at least enough to submitting, and _attempting_ to executing the ticket), but that doesn't guarantee a successful auto-redemption. - -| Parameter | Description | -| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `l1CallValue (also referred to as deposit)` | Not a real function parameter, it is rather the callValue that is sent along with the transaction | -| `address to` | The destination L2 address | -| `uint256 l2CallValue` | The callvalue for retryable L2 message that is supplied within the deposit (l1CallValue) | -| `uint256 maxSubmissionCost` | The maximum amount of ETH to be paid for submitting the ticket. This amount is (1) supplied within the deposit (l1CallValue) to be later deducted from sender's L2 balance and is (2) directly proportional to the size of the retryable’s data and L1 basefee | -| `address excessFeeRefundAddress` | The unused gas cost and submssion cost will deposit to this address, formula is: `(gasLimit x maxFeePerGas - execution cost) + (maxSubmission - (autoredeem ? 0 : submission cost))`. (Note: excess deposit will transfer to the alias address of the parent chain tx's `msg.sender` rather than this address) | -| `address callValueRefundAddress` | The L2 address to which the l2CallValue is credited if the ticket times out or gets cancelled (this is also called the `beneficiary`, who's got a critical permission to cancel the ticket) | -| `uint256 gasLimit` | Maximum amount of gas used to cover L2 execution of the ticket | -| `uint256 maxFeePerGas` | The gas price bid for L2 execution of the ticket that is supplied within the deposit (l1CallValue) | -| `bytes calldata data` | The calldata to the destination L2 address | - -2. Sender's deposit must be enough to make the L1 submission succeed and for the L2 execution to be _attempted_. If provided correctly, a new ticket with a unique `TicketID` is created and added to retryable buffer. Also, funds (`submissionCost` + `l2CallValue`) are deducted from the sender and placed into the escrow for later use in redeeming the ticket. - -3. Ticket creation causes the [`ArbRetryableTx`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile to emit a `TicketCreated` event containing the `TicketID` on L2. - -[inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol - - - - 🧍 - Initiating an L1-L2 message - Enough deposit? - Ticket creation fails - Ticket is created - - - - - - - - 🧍 The user who initiates an L1-L2 message - - - Initiating an L1-L2 message A call to `inbox.createRetryableTicket` function that - puts the message in the L2 inbox that can be re-executed for some fixed amount of time if it - reverts - - - Check user's deposit Logic that checks if the user have enough funds to create a - ticket. This is done by checking if the `msg.value` provided by the user is greater than or - equal to maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas - - - Ticket creation fails Ticket creation fails and no funds are deducted from the - user - - - Ticket is created A ticket is created and added to the retryable buffer on L2 - Funds (l2CallValue + submissionCost) are deducted to cover the callvalue from the - user and placed into escrow (on L2) for later use in redeeming the ticket - - - - -### Automatic Redemption - -4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: - -- If the ticket is successfully auto-redeemed, it will execute with the sender, destination, callvalue, and calldata of the original submission. The submission fee is refunded to the user on L2 (`excessFeeRefundAddress`). Note that to ensure successful auto-redeem of the ticket, one could use the Arbitrum SDK which provides a [convenience function](https://github.com/OffchainLabs/arbitrum-sdk/blob/4cedb1fcf1c7302a4c3d0f8e75fb33d82bc8338d/src/lib/message/L1ToL2MessageGasEstimator.ts#L215) that returns the desired gas parameters when sending L1-L2 messages. - -- If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). - - - - Auto-redeem succeeds? - Ticket is executed - Ticket is deleted - callValueRefundAddress gets refunded - - - - - - - Does the auto-redeem succeed? Logic that determines if the user's L2 Balance is - greater than (or equal to) maxFeePerGas * gasLimit && maxFeePerGas{' '} - is greater than (or equal to) the l2Basefee - - - Ticket is executed Ticket is executed, the actual submissionFee is - refunded to the excessFeeRefundAddress since the ticket was not kept in the - buffer on L2 - - - Ticket is deleted Ticket gets deleted from the L2 retryable buffer - - - callValueRefundAddress gets refunded callValueRefundAddress gets - refunded with (maxGas - gasUsed) * gasPrice. Note that this amount is capped by{' '} - l1CallValue in the auto-redeem - - - - -### Manual Redemption - -5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. - -6. If the fixed period (one week) elapses without a successful redeem, the ticket **expires** and will be [automatically **discarded**][discard_link], unless some party has paid a fee to [**keep the ticket alive**][renew_link] for another full period. A ticket can live indefinitely as long as it is renewed each time before it expires. - -[enqueue_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L245 -[exhaust_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L135 -[discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 -[renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 - - - - Ticket manually cancelled or not redeemed in 7 days? - callValueRefundAddress gets refunded - Ticket is deleted - Ticket manually redeemed? - - - - - - - - - Is the ticket manually cancelled or not redeemed within 7 days? Logic that - determines if the ticket is manually cancelled or not redeemed within 7 days (i.e., is - expired) - - - callValueRefundAddress gets refunded callValueRefundAddress is refunded with the{' '} - l2CallValue - - - Ticket is deleted Ticket gets deleted from the L2 retryable buffer - - - Is the ticket manually redeemed Logic that determines if the ticket is manually - redeemed - - - -:::caution Avoid Losing Funds! - -If a ticket expires after 7 days without being redeemed or re-scheduled to a future date, any message and value (other than the escrowed `callvalue`) it carries could be lost without possibility of being recovered. - -::: - -On success, the `To` address receives the escrowed callvalue, and any unused gas is returned to ArbOS's gas pools. On failure, the callvalue is returned to the escrow for the future redeem attempt. In either case, the network fee was paid during the scheduling tx, so no fees are charged and no refunds are made. - -Note that during redemption of a ticket, attempts to cancel the same ticket, or to schedule another redeem of the same ticket, will revert. In this manner retryable tickets are not self-modifying. - -If a ticket with a callvalue is eventually discarded (cancelled or expired), having never successfully run, the escrowed callvalue will be paid out to a `callValueRefundAddress` account that was specified in the initial submission (step 1). - -:::note Important Notes: - -If a redeem is not done at submission or the submission's initial redeem fails, anyone can attempt to redeem the retryable again by calling [`ArbRetryableTx`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the transaction that scheduled it. Note that the redeem attempt's gas comes from the call to `redeem`, so there's no chance the block's gas limit is reached before execution. - -- One can redeem live tickets using the [Arbitrum Retryables Transaction Panel][retryable_dashboard_link] -- The calldata of a ticket is saved on L2 until it is redeemed or expired -- Redeeming cost of a ticket will not increase over time, it only depends on the current gas price and gas required for execution - -::: - -[retryable_dashboard_link]: https://retryable-dashboard.arbitrum.io/tx - -### Receipts - -In the lifecycle of a retryable ticket, two types of L2 transaction receipts will be emitted: - -- **Ticket Creation Receipt**: This receipt indicates that a ticket was successfully created; any successful L1 call to the `Inbox`'s `createRetryableTicket` method is guaranteed to create a ticket. The ticket creation receipt includes a `TicketCreated` event (from `ArbRetryableTx`), which includes a `ticketId` field. This `ticketId` is computable via RLP encoding and hashing the transaction; see [calculateSubmitRetryableId](https://github.com/OffchainLabs/arbitrum-sdk/blob/6cc143a3bb019dc4c39c8bcc4aeac9f1a48acb01/src/lib/message/L1ToL2Message.ts#L109). -- **Redeem Attempt**: A redeem attempt receipt represents the result of an attempted L2 execution of a ticket, i.e, success / failure of that specific redeem attempt. It includes a `RedeemScheduled` event from `ArbRetryableTx`, with a `ticketId` field. At most, one successful redeem attempt can ever exist for a given ticket; if, e.g., the auto-redeem upon initial creation succeeds, only the receipt from the auto-redeem will ever get emitted for that ticket. If the auto-redeem fails (or was never attempted — i.e., the provided L2 gas limit \* L2 gas price = 0), each initial attempt will emit a redeem attempt receipt until one succeeds. - -### Alternative "unsafe" Retryable Ticket Creation - -The `Inbox.createRetryableTicket` convenience method includes sanity checks to help minimize the risk of user error: the method will ensure that enough funds are provided directly from L1 to cover the current cost of ticket creation. It also will convert the provided `callValueRefundAddress` and `excessFeeRefundAddress` to their address alias (see below) if either is a contract (determined by if the address has code during the call), providing a path for the L1 contract to recover funds. A power-user may bypass these sanity-check measures via the `Inbox`'s `unsafeCreateRetryableTicket` method; as the method's name desperately attempts to warn you, it should only be accessed by a user who truly knows what they're doing. - -## Eth deposits - -A special message type exists for simple Eth deposits; i.e., sending Eth from L1 to L2. Eth can be deposited via a call to the `Inbox`'s `depositEth` method. If the L1 caller is EOA, the Eth will be deposited to the same EOA address on L2; the L1 caller is a contract, the funds will deposited to the contract's aliased address (see below). - -Note that depositing Eth via `depositEth` into a contract on L2 will _not_ trigger the contract's fallback function. - -In principle, retryable tickets can alternatively be used to deposit Ether; this could be preferable to the special eth-deposit message type if, e.g., more flexibility for the destination address is needed, or if one wants to trigger the fallback function on the L2 side. - -## Transacting via the Delayed Inbox - -While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. - -### Address Aliasing - -Unsigned messages submitted via the Delayed Inbox get their sender's addressed "aliased": when these messages are executed on L2, the sender's address —i.e., that which is returned by `msg.sender` — will _not_ simply be the L1 address that sent the message; rather it will be the address's "L2 Alias." An address's L2 alias is its value increased by the hex value `0x1111000000000000000000000000000000001111`: - -```sol -L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111 -``` - -:::tip Try it out - - -::: - -The Arbitrum protocol's usage of L2 Aliases for L1-to-L2 messages prevents cross-chain exploits that would otherwise be possible if we simply reused the same L1 addresses as the L2 sender; i.e., tricking an L2 contract that expects a call from a given contract address by sending retryable ticket from the expected contract address on L1. - -If for some reason you need to compute the L1 address from an L2 alias on chain, you can use our `AddressAliasHelper` library: - -```sol -modifier onlyFromMyL1Contract() override { - require(AddressAliasHelper.undoL1ToL2Alias(msg.sender) == myL1ContractAddress, "ONLY_COUNTERPART_CONTRACT"); - _; -} -``` - -### Signed Messages - -The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. - -These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). - - - - - -Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. - -### Protocol Flow - -Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. - -### Client Flow - -From a client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. - -### Protocol Design Details - -An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock confirmation processed can't be griefed. - -Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. - -Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. -The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) - -\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. - - ## Happy/Common Case: Sequencer Is Live and Well-behaved diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx index e92b34908..8fee1d232 100644 --- a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx @@ -17,6 +17,9 @@ The game will operate in two phases: dissection, followed by one-step proof. Dis We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. + + + ### Dissection Protocol: Simplified Version Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. @@ -27,6 +30,9 @@ Now Alice has effectively bisected her N-step assertion into two (N/2)-step asse At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. + + + ### Why Dissection Correctly Identifies a Cheater Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. @@ -37,6 +43,9 @@ To prove (2), observe that if Alice’s initial claim is incorrect, this can onl (If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) + + + ### The Real Dissection Protocol The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. @@ -53,12 +62,18 @@ The real dissection protocol is conceptually similar to the simplified one descr It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. + + + ### Efficiency The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. + + + ## Validators Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. @@ -83,22 +98,32 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. - Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. + + + ## ArbOS ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. + + + ### Why ArbOS? In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. + + ## Full Nodes As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. + + ## The Sequencer The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. @@ -107,12 +132,18 @@ Clients interact with the Sequencer in exactly the same way they would interact [Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. + + + ### Instant confirmation Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. + + + ### Inboxes, fast and slow When we add a Sequencer, the operation of the inbox changes. @@ -122,6 +153,8 @@ When we add a Sequencer, the operation of the inbox changes. - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) + + ### If the Sequencer is well-behaved... A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. @@ -134,6 +167,8 @@ This is the basic tradeoff of having a Sequencer: if your message uses the Seque So a Sequencer is generally a win, if the Sequencer is well behaved. + + ### If the Sequencer is malicious... A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. @@ -142,6 +177,8 @@ On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/stat Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. + + ### Decentralized fair sequencing Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. @@ -159,6 +196,8 @@ The `ChallengeManager` arbitrates challenge games. Here's a diagram of the chall + + ## Block challenge The challenge begins by bisecting over global states (including block hashes). @@ -168,6 +207,8 @@ Once the challenge has been bisected down to an individual block, This operates similarly to a bisection in that the responder must provide a competing global state and machine state, but it uses that information to transition to the execution challenge phase. + + ## Execution challenge Once narrowed down to an individual block, the actual machine execution can be bisected. @@ -177,6 +218,8 @@ The current responder must provide proof data to execute a step of the machine. If executing that step ends in a different state than was previously asserted, the current responder wins the challenge. + + ## General bisection protocol _**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ @@ -207,6 +250,8 @@ Note that unlike in a traditional bisection protocol, where one party proposes s this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections when challenging. + + ## Winning the challenge Note that for the time being, winning the challenge isn't instant. @@ -250,6 +295,8 @@ such a case were reached in correct execution, could lead to a security failure. The following assumptions, together, must prevent an unreachable case from arising in correct execution. + + ## The WAVM code is generated by Arbitrator from valid WASM WAVM is the name of the custom instruction set similar to WASM used for proving. @@ -261,6 +308,8 @@ mismatch types, or do any other number of invalid things which are prevented by WAVM code generated from by Arbitrator from valid WASM is assumed to never encounter an unreachable case. + + ## Inbox messages must not be too large The current method of inbox hashing requires the full inbox message be available for proving. @@ -271,6 +320,8 @@ The current length limit is 117,964 bytes, which is 90% of the [max transaction size Geth will accept](https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/core/tx_pool.go#L53), leaving 13,108 bytes for other proving data. + + ## Requested preimages must be known and not too large WAVM has an opcode which resolves the preimage of a Keccak-256 hash. @@ -282,6 +333,8 @@ The current length limit is 117,964 bytes for the reasons mentioned above. Here's a list of which preimages may be requested by Nitro, and why they're known to all parties, and not too large: + + ### Block headers Nitro may request up to the last 256 L2 block headers. @@ -290,6 +343,8 @@ and blocks before it are required to implement the `BLOCKHASH` evm instruction. This is safe as previous block headers are a fixed size, and are known to all nodes. + + ### State trie access To resolve state, Nitro traverses the state trie by resolving preimages. @@ -313,6 +368,8 @@ This document lists those which are not, and explains how they're expressed in W Many of the WAVM representations use opcodes not in WASM, which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). + + ## `block` and `loop` In WASM, a block contains instructions. @@ -323,6 +380,8 @@ In WAVM, instructions are flat. At transpilation time, any branch instructions are replaced with jumps to the corresponding block's destination. This means that WAVM interpreters don't need to track blocks, and thus block instructions are unnecessary. + + ## `if` and `else` These are translated to a block with an `ArbitraryJumpIf` as follows: @@ -341,6 +400,8 @@ end `br` and `br_if` are translated into `ArbitraryJump` and `ArbitraryJumpIf` respectively. The jump locations can be known at transpilation time, making blocks obsolete. + + ## `br_table` `br_table` is translated to a check for each possible branch in the table, @@ -349,10 +410,14 @@ and then if none of the checks hit, a branch of the default level. Each of the non-default branches has a conditional jump to a section afterwards, containing a `drop` for the selector, and then a jump to the target branch. + + ## `local.tee` `local.tee` is translated to a WAVM `Dup` and then a `LocalSet`. + + ## `return` To translate a return, the number of return values must be known from the function signature. @@ -361,6 +426,8 @@ Then, a loop checks `IsStackBoundary` (which implicitly pops a value) until it's Next, a `MoveFromInternalToStack` is added for each return value to put the return values back on the stack. Finally, a WAVM `Return` is added, returning control flow to the caller. + + ## Floating point instructions A floating point library module must be present to translate floating point instructions. @@ -383,6 +450,8 @@ WAVM implements the multi value and sign extension ops WASM proposals. WAVM also implements the following unique opcodes, which are not part of WASM nor any WASM proposal. + + ## Invariants Many of these opcodes have implicit invariants about what's on the stack, @@ -390,6 +459,8 @@ e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. If these conditions are not satisfied, execution is generally not possible. These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) + + ## Codegen internal These are generated when breaking down a WASM instruction that does many things into many WAVM instructions which each do one thing. @@ -419,6 +490,8 @@ The above opcodes eliminate the need for the following WASM opcodes (which are t - br_table - local.tee + + ## Linking This is only generated to link modules together. @@ -429,6 +502,8 @@ which handles the actual work needed to change modules. | ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 0x8009 | CrossModuleCall | Pushes the current program counter, module number, and module's internals offset to the stack. Then splits its argument data into the lower 32 bits being a function index, and the upper 32 bits being a module index, and jumps to the beginning of that function. | + + ## Host calls These are only used in the implementation of "host calls". @@ -484,6 +559,8 @@ and floating points may be used outside core state transition code for imprecise but the former is well exercised as used in Nitro, and the latter generally doesn't rely on details like the minimum of NaN and infinity. + + ## Known divergences from the WASM specification Floating point to integer truncation will saturate on overflow, instead of erroring. @@ -515,6 +592,8 @@ and then calls the main module's main function, which is language specific. For Go it sets argv to `["js"]` to match the JS environment, and calls `run`. For Rust it calls `main` with no arguments. + + ## Library exports Libraries may export functions with the name pattern `module__name`, @@ -523,6 +602,8 @@ which future libraries or the main module can import as `"module" "name"`. For instance, this is used for wasi-stub to provide functions rust imports according to the WebAssembly System Interface. + + ## Floating point operations To provide floating point operations for future libraries, @@ -533,6 +614,8 @@ Their type signature is also the same, except all `f32`s and `f64`s are bitcaste Future modules can implicitly use these by using WASM floating point operations, which are replaced at the WASM->WAVM level with bitcasts and cross module calls to these functions. + + ## WAVM guest calls Libraries may call the main module's exports via `"env" "wavm_guest_call__*"`. @@ -541,6 +624,8 @@ For instance, go-stub calls Go's resume function when queueing async events via `wavm_guest_call_resume()`, and then retrieves the new stack pointer with `wavm_guest_call_getsp()`. + + ## Caller module internals call Every stack frame retains its caller module and its caller module's "internals offset", diff --git a/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx b/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx new file mode 100644 index 000000000..a8cc6781f --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx @@ -0,0 +1,247 @@ + +## Retryable Tickets + +Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. + +## Retryable Tickets Lifecycle + +Here we walk through the different stages of the lifecycle of a retryable ticket; (1) submission, (2) auto-redemption, and (3) manual redemption. + + + +### Submission + +1. Creating a retryable ticket is initiated with a call (direct or internal) to the `createRetryableTicket` function of the [`inbox` contract][inbox_link]. A ticket is guaranteed to be created if this call succeeds. Here, we describe parameters that need to be carefully set. Note that, this function forces the sender to provide a _reasonable_ amount of funds (at least enough to submitting, and _attempting_ to executing the ticket), but that doesn't guarantee a successful auto-redemption. + +| Parameter | Description | +| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `l1CallValue (also referred to as deposit)` | Not a real function parameter, it is rather the callValue that is sent along with the transaction | +| `address to` | The destination L2 address | +| `uint256 l2CallValue` | The callvalue for retryable L2 message that is supplied within the deposit (l1CallValue) | +| `uint256 maxSubmissionCost` | The maximum amount of ETH to be paid for submitting the ticket. This amount is (1) supplied within the deposit (l1CallValue) to be later deducted from sender's L2 balance and is (2) directly proportional to the size of the retryable’s data and L1 basefee | +| `address excessFeeRefundAddress` | The unused gas cost and submssion cost will deposit to this address, formula is: `(gasLimit x maxFeePerGas - execution cost) + (maxSubmission - (autoredeem ? 0 : submission cost))`. (Note: excess deposit will transfer to the alias address of the parent chain tx's `msg.sender` rather than this address) | +| `address callValueRefundAddress` | The L2 address to which the l2CallValue is credited if the ticket times out or gets cancelled (this is also called the `beneficiary`, who's got a critical permission to cancel the ticket) | +| `uint256 gasLimit` | Maximum amount of gas used to cover L2 execution of the ticket | +| `uint256 maxFeePerGas` | The gas price bid for L2 execution of the ticket that is supplied within the deposit (l1CallValue) | +| `bytes calldata data` | The calldata to the destination L2 address | + +2. Sender's deposit must be enough to make the L1 submission succeed and for the L2 execution to be _attempted_. If provided correctly, a new ticket with a unique `TicketID` is created and added to retryable buffer. Also, funds (`submissionCost` + `l2CallValue`) are deducted from the sender and placed into the escrow for later use in redeeming the ticket. + +3. Ticket creation causes the [`ArbRetryableTx`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile to emit a `TicketCreated` event containing the `TicketID` on L2. + +[inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol + + + + 🧍 + Initiating an L1-L2 message + Enough deposit? + Ticket creation fails + Ticket is created + + + + + + + + 🧍 The user who initiates an L1-L2 message + + + Initiating an L1-L2 message A call to `inbox.createRetryableTicket` function that + puts the message in the L2 inbox that can be re-executed for some fixed amount of time if it + reverts + + + Check user's deposit Logic that checks if the user have enough funds to create a + ticket. This is done by checking if the `msg.value` provided by the user is greater than or + equal to maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas + + + Ticket creation fails Ticket creation fails and no funds are deducted from the + user + + + Ticket is created A ticket is created and added to the retryable buffer on L2 + Funds (l2CallValue + submissionCost) are deducted to cover the callvalue from the + user and placed into escrow (on L2) for later use in redeeming the ticket + + + + + + +### Automatic Redemption + +4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: + +- If the ticket is successfully auto-redeemed, it will execute with the sender, destination, callvalue, and calldata of the original submission. The submission fee is refunded to the user on L2 (`excessFeeRefundAddress`). Note that to ensure successful auto-redeem of the ticket, one could use the Arbitrum SDK which provides a [convenience function](https://github.com/OffchainLabs/arbitrum-sdk/blob/4cedb1fcf1c7302a4c3d0f8e75fb33d82bc8338d/src/lib/message/L1ToL2MessageGasEstimator.ts#L215) that returns the desired gas parameters when sending L1-L2 messages. + +- If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). + + + + Auto-redeem succeeds? + Ticket is executed + Ticket is deleted + callValueRefundAddress gets refunded + + + + + + + Does the auto-redeem succeed? Logic that determines if the user's L2 Balance is + greater than (or equal to) maxFeePerGas * gasLimit && maxFeePerGas{' '} + is greater than (or equal to) the l2Basefee + + + Ticket is executed Ticket is executed, the actual submissionFee is + refunded to the excessFeeRefundAddress since the ticket was not kept in the + buffer on L2 + + + Ticket is deleted Ticket gets deleted from the L2 retryable buffer + + + callValueRefundAddress gets refunded callValueRefundAddress gets + refunded with (maxGas - gasUsed) * gasPrice. Note that this amount is capped by{' '} + l1CallValue in the auto-redeem + + + + + + +### Manual Redemption + +5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. + +6. If the fixed period (one week) elapses without a successful redeem, the ticket **expires** and will be [automatically **discarded**][discard_link], unless some party has paid a fee to [**keep the ticket alive**][renew_link] for another full period. A ticket can live indefinitely as long as it is renewed each time before it expires. + +[enqueue_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L245 +[exhaust_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L135 +[discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 +[renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 + + + + Ticket manually cancelled or not redeemed in 7 days? + callValueRefundAddress gets refunded + Ticket is deleted + Ticket manually redeemed? + + + + + + + + + Is the ticket manually cancelled or not redeemed within 7 days? Logic that + determines if the ticket is manually cancelled or not redeemed within 7 days (i.e., is + expired) + + + callValueRefundAddress gets refunded callValueRefundAddress is refunded with the{' '} + l2CallValue + + + Ticket is deleted Ticket gets deleted from the L2 retryable buffer + + + Is the ticket manually redeemed Logic that determines if the ticket is manually + redeemed + + + + +:::caution Avoid Losing Funds! + +If a ticket expires after 7 days without being redeemed or re-scheduled to a future date, any message and value (other than the escrowed `callvalue`) it carries could be lost without possibility of being recovered. + +::: + +On success, the `To` address receives the escrowed callvalue, and any unused gas is returned to ArbOS's gas pools. On failure, the callvalue is returned to the escrow for the future redeem attempt. In either case, the network fee was paid during the scheduling tx, so no fees are charged and no refunds are made. + +Note that during redemption of a ticket, attempts to cancel the same ticket, or to schedule another redeem of the same ticket, will revert. In this manner retryable tickets are not self-modifying. + +If a ticket with a callvalue is eventually discarded (cancelled or expired), having never successfully run, the escrowed callvalue will be paid out to a `callValueRefundAddress` account that was specified in the initial submission (step 1). + +:::note Important Notes: + +If a redeem is not done at submission or the submission's initial redeem fails, anyone can attempt to redeem the retryable again by calling [`ArbRetryableTx`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the transaction that scheduled it. Note that the redeem attempt's gas comes from the call to `redeem`, so there's no chance the block's gas limit is reached before execution. + +- One can redeem live tickets using the [Arbitrum Retryables Transaction Panel][retryable_dashboard_link] +- The calldata of a ticket is saved on L2 until it is redeemed or expired +- Redeeming cost of a ticket will not increase over time, it only depends on the current gas price and gas required for execution + +::: + +[retryable_dashboard_link]: https://retryable-dashboard.arbitrum.io/tx + + + + +### Receipts + +In the lifecycle of a retryable ticket, two types of L2 transaction receipts will be emitted: + +- **Ticket Creation Receipt**: This receipt indicates that a ticket was successfully created; any successful L1 call to the `Inbox`'s `createRetryableTicket` method is guaranteed to create a ticket. The ticket creation receipt includes a `TicketCreated` event (from `ArbRetryableTx`), which includes a `ticketId` field. This `ticketId` is computable via RLP encoding and hashing the transaction; see [calculateSubmitRetryableId](https://github.com/OffchainLabs/arbitrum-sdk/blob/6cc143a3bb019dc4c39c8bcc4aeac9f1a48acb01/src/lib/message/L1ToL2Message.ts#L109). +- **Redeem Attempt**: A redeem attempt receipt represents the result of an attempted L2 execution of a ticket, i.e, success / failure of that specific redeem attempt. It includes a `RedeemScheduled` event from `ArbRetryableTx`, with a `ticketId` field. At most, one successful redeem attempt can ever exist for a given ticket; if, e.g., the auto-redeem upon initial creation succeeds, only the receipt from the auto-redeem will ever get emitted for that ticket. If the auto-redeem fails (or was never attempted — i.e., the provided L2 gas limit \* L2 gas price = 0), each initial attempt will emit a redeem attempt receipt until one succeeds. + + + +### Alternative "unsafe" Retryable Ticket Creation + +The `Inbox.createRetryableTicket` convenience method includes sanity checks to help minimize the risk of user error: the method will ensure that enough funds are provided directly from L1 to cover the current cost of ticket creation. It also will convert the provided `callValueRefundAddress` and `excessFeeRefundAddress` to their address alias (see below) if either is a contract (determined by if the address has code during the call), providing a path for the L1 contract to recover funds. A power-user may bypass these sanity-check measures via the `Inbox`'s `unsafeCreateRetryableTicket` method; as the method's name desperately attempts to warn you, it should only be accessed by a user who truly knows what they're doing. + + + +## Eth deposits + +A special message type exists for simple Eth deposits; i.e., sending Eth from L1 to L2. Eth can be deposited via a call to the `Inbox`'s `depositEth` method. If the L1 caller is EOA, the Eth will be deposited to the same EOA address on L2; the L1 caller is a contract, the funds will deposited to the contract's aliased address (see below). + +Note that depositing Eth via `depositEth` into a contract on L2 will _not_ trigger the contract's fallback function. + +In principle, retryable tickets can alternatively be used to deposit Ether; this could be preferable to the special eth-deposit message type if, e.g., more flexibility for the destination address is needed, or if one wants to trigger the fallback function on the L2 side. + + + +## Transacting via the Delayed Inbox + +While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. + + + +### Address Aliasing + +Unsigned messages submitted via the Delayed Inbox get their sender's addressed "aliased": when these messages are executed on L2, the sender's address —i.e., that which is returned by `msg.sender` — will _not_ simply be the L1 address that sent the message; rather it will be the address's "L2 Alias." An address's L2 alias is its value increased by the hex value `0x1111000000000000000000000000000000001111`: + +```sol +L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111 +``` + +:::tip Try it out + + +::: + +The Arbitrum protocol's usage of L2 Aliases for L1-to-L2 messages prevents cross-chain exploits that would otherwise be possible if we simply reused the same L1 addresses as the L2 sender; i.e., tricking an L2 contract that expects a call from a given contract address by sending retryable ticket from the expected contract address on L1. + +If for some reason you need to compute the L1 address from an L2 alias on chain, you can use our `AddressAliasHelper` library: + +```sol +modifier onlyFromMyL1Contract() override { + require(AddressAliasHelper.undoL1ToL2Alias(msg.sender) == myL1ContractAddress, "ONLY_COUNTERPART_CONTRACT"); + _; +} +``` + + + +### Signed Messages + +The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. + +These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx new file mode 100644 index 000000000..b07ba1541 --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx @@ -0,0 +1,23 @@ + +Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. + +### Protocol Flow + +Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. + +### Client Flow + +From a client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. + +### Protocol Design Details + +An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock confirmation processed can't be griefed. + +Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. + +Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. +The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) + +\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. + + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/09-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/11-nitro-vs-classic.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/09-nitro-vs-classic.mdx rename to arbitrum-docs/how-arbitrum-works/11-nitro-vs-classic.mdx From 889117eb87c559d3bbf0b6df2fc8e6c54a819283 Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 22 Nov 2024 11:05:25 -0600 Subject: [PATCH 05/75] cleaned up sidebars.js, yarn format --- .../01-a-gentle-introduction.mdx | 11 +- .../how-arbitrum-works/03-sequencer.mdx | 16 +-- .../04-state-transition-function.mdx | 21 +--- .../how-arbitrum-works/05-validation.mdx | 36 +----- .../how-arbitrum-works/06-challenges.mdx | 117 ++---------------- .../07-anytrust-protocol.mdx | 6 +- .../how-arbitrum-works/08-gas-fees.mdx | 8 +- .../09-retryable-tickets.mdx | 24 +--- .../10-cross-chain-messaging.mdx | 5 +- website/sidebars.js | 108 ++++++++-------- 10 files changed, 93 insertions(+), 259 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 5f2fe8d1a..58c08f27e 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -14,9 +14,6 @@ This document is a deep-dive explanation of Arbitrum Nitro’s design and the ra The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. - - - ## Why use Arbitrum? Why use Nitro? Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: @@ -38,9 +35,6 @@ Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in sever - **Geth Tracing,** for even broader debugging support. - And many, many more changes. - - - ## The Big Picture At the most basic level, an Arbitrum chain works like this: @@ -65,9 +59,6 @@ All of the technical detail in this document is connected to this diagram. To ge - How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? - How can I run my own Arbitrum node or validator? - - - ## Nitro's Design: The Four Big Ideas The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. @@ -96,4 +87,4 @@ Because the protocol doesn't trust the Sequencer not to put garbage into its seq The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. -It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. \ No newline at end of file +It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index a00303343..202ff73da 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -4,17 +4,12 @@ The Sequencer is a specially designated Arbitrum full node which, under normal c Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. - - - ## The Core Inbox When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. - - ## Bridging We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. @@ -53,10 +48,6 @@ These L2-to-L1 tickets have unlimited lifetime, until they’re successfully red - - - - ## Happy/Common Case: Sequencer Is Live and Well-behaved Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. @@ -68,9 +59,6 @@ Alternatively, a user can submit their L2 message to the Sequencer by posting it In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. - - - ## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: @@ -85,8 +73,8 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. - + ### How the Sequencer Publishes the Sequence So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. @@ -97,4 +85,4 @@ The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. - \ No newline at end of file + diff --git a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx index 6cd48612e..1113781eb 100644 --- a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx @@ -1,4 +1,5 @@ + ## Geth at the Core The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. @@ -15,9 +16,6 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - - - ## Separating Execution from Proving One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. @@ -26,9 +24,6 @@ When compiling the Nitro node software for _execution_, the ordinary Go compiler Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. - - - #### WAVM The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. @@ -39,9 +34,6 @@ Second, WAVM restricts a few features of wasm. For example, WAVM does not contai Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. - - - #### ReadPreImage and the Hash Oracle Trick The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. @@ -54,15 +46,10 @@ The only other use of `ReadPreImage` is to fetch the contents of recent L2 block This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - - - - - - + # ArbOS ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS accounts for and manages network resources, produces blocks from incoming messages, and operates its instrumented instance of Geth for smart contract execution. @@ -168,8 +155,8 @@ ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [se - + # Geth Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This document will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. @@ -459,4 +446,4 @@ Genesis block in nitro is not necessarily block #0. Nitro supports importing blo [underlyingtransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L69 [writeheadblock_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/genesis.go#L415 - \ No newline at end of file + diff --git a/arbitrum-docs/how-arbitrum-works/05-validation.mdx b/arbitrum-docs/how-arbitrum-works/05-validation.mdx index 16b9802f4..ee41f182f 100644 --- a/arbitrum-docs/how-arbitrum-works/05-validation.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-validation.mdx @@ -1,4 +1,5 @@ + ## Optimistic Rollup Arbitrum is an optimistic rollup. Let’s unpack that term. @@ -15,18 +16,12 @@ Arbitrum is optimistic, which means that Arbitrum advances the state of its chai Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. - - - ## Resolving disputes using interactive fraud proofs Among optimistic rollups, the most important design decision is how to resolve disputes. Suppose Alice claims that the chain will produce a certain result, and Bob disagrees. How will the protocol decide which version to accept? There are basically two choices: interactive proving, or re-executing transactions. Arbitrum uses interactive proving, which we believe is more efficient and more flexible. Much of the design of Arbitrum follows from this fact. - - - ### Interactive proving The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. @@ -35,16 +30,10 @@ Arbitrum's approach is based on dissection of the dispute. If Alice's claim cove The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. - - - ### Re-executing transactions The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. - - - ### Why interactive proving is better We believe strongly that interactive proving is the superior approach, for the following reasons. @@ -57,16 +46,10 @@ We believe strongly that interactive proving is the superior approach, for the f **More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. - - - ### Interactive proving drives the design of Arbitrum Much of the design of Arbitrum is driven by the opportunities opened up by interactive proving. If you're reading about some feature of Arbitrum, and you're wondering why it exists, two good questions to ask are: "How does this support interactive proving?" and "How does this take advantage of interactive proving?" The answers to most "why" questions about Arbitrum relate to interactive proving. - - - ## Arbitrum Rollup Protocol Before diving into the rollup protocol, there are two things we need to cover. @@ -85,9 +68,6 @@ The parties who participate in the protocol are called _validators_. Some valida The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. - - - ### The Rollup Chain The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. @@ -205,9 +185,6 @@ There’s a lot going on here, so let’s unpack it. Again: this sort of thing is very unlikely in practice. In this diagram, at least four parties must have bonded on wrong RBlocks, and when the dust settles at least four parties will have lost their bonds. The protocol handles these cases correctly, of course, but they’re rare corner cases. This diagram is designed to illustrate the variety of situations that are possible in principle, and how the protocol would deal with them. - - - ### Staking At any given time, some validators will be bonders, and some will not. Bonders deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the bonder loses a challenge. Nitro chains accept bonds in ETH. @@ -228,16 +205,10 @@ The rules for staking are as follows: Notice that once you are bonded on an RBlock, there is no way to unbond. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your bond. The only way to get your bond back is to wait until all of the RBlocks you are bonded on are confirmed. - - - #### Setting the current minimum bond amount One detail we deferred earlier is how the current minimum bond amount is set. Normally, this is just equal to the base bond amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the bond requirement will escalate temporarily. Specifically, the base bond amount is multiplied by a factor that is exponential in the time since the deadline of the first unresolved RBlock passed. This ensures that if malicious parties are placing false bonds to try to delay progress (despite the fact that they’re losing those bonds), the bond requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the bond requirement will go back down. - - - ### Rules for Confirming or Rejecting RBlocks The rules for resolving RBlocks are fairly simple. @@ -261,8 +232,8 @@ A consequence of these rules is that once the first unresolved RBlock's deadline - + # The Assertion Tree ### Overview @@ -292,4 +263,5 @@ The only delay that users experience during a dispute is of their [L2 to L1 mess ### Detailed Spec For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum#rollup#protocol). - \ No newline at end of file + + diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx index 8fee1d232..ef3640fd2 100644 --- a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx @@ -1,4 +1,5 @@ - + + ## Challenges Suppose the rollup chain looks like this: @@ -17,9 +18,6 @@ The game will operate in two phases: dissection, followed by one-step proof. Dis We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. - - - ### Dissection Protocol: Simplified Version Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. @@ -30,9 +28,6 @@ Now Alice has effectively bisected her N-step assertion into two (N/2)-step asse At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. - - - ### Why Dissection Correctly Identifies a Cheater Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. @@ -43,9 +38,6 @@ To prove (2), observe that if Alice’s initial claim is incorrect, this can onl (If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) - - - ### The Real Dissection Protocol The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. @@ -62,18 +54,12 @@ The real dissection protocol is conceptually similar to the simplified one descr It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. - - - ### Efficiency The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - - - ## Validators Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. @@ -98,32 +84,22 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. - Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - - - ## ArbOS ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. - - - ### Why ArbOS? In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. - - ## Full Nodes As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. - - ## The Sequencer The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. @@ -132,18 +108,12 @@ Clients interact with the Sequencer in exactly the same way they would interact [Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. - - - ### Instant confirmation Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. - - - ### Inboxes, fast and slow When we add a Sequencer, the operation of the inbox changes. @@ -153,8 +123,6 @@ When we add a Sequencer, the operation of the inbox changes. - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) - - ### If the Sequencer is well-behaved... A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. @@ -167,8 +135,6 @@ This is the basic tradeoff of having a Sequencer: if your message uses the Seque So a Sequencer is generally a win, if the Sequencer is well behaved. - - ### If the Sequencer is malicious... A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. @@ -177,17 +143,16 @@ On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/stat Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. - - ### Decentralized fair sequencing Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. - + + import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; # ChallengeManager @@ -196,8 +161,6 @@ The `ChallengeManager` arbitrates challenge games. Here's a diagram of the chall - - ## Block challenge The challenge begins by bisecting over global states (including block hashes). @@ -207,8 +170,6 @@ Once the challenge has been bisected down to an individual block, This operates similarly to a bisection in that the responder must provide a competing global state and machine state, but it uses that information to transition to the execution challenge phase. - - ## Execution challenge Once narrowed down to an individual block, the actual machine execution can be bisected. @@ -218,8 +179,6 @@ The current responder must provide proof data to execute a step of the machine. If executing that step ends in a different state than was previously asserted, the current responder wins the challenge. - - ## General bisection protocol _**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ @@ -250,8 +209,6 @@ Note that unlike in a traditional bisection protocol, where one party proposes s this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections when challenging. - - ## Winning the challenge Note that for the time being, winning the challenge isn't instant. @@ -263,11 +220,8 @@ there is time to diagnose and fix the error with a contract upgrade. - - - - + # One Step Proof Assumptions The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise @@ -295,8 +249,6 @@ such a case were reached in correct execution, could lead to a security failure. The following assumptions, together, must prevent an unreachable case from arising in correct execution. - - ## The WAVM code is generated by Arbitrator from valid WASM WAVM is the name of the custom instruction set similar to WASM used for proving. @@ -308,8 +260,6 @@ mismatch types, or do any other number of invalid things which are prevented by WAVM code generated from by Arbitrator from valid WASM is assumed to never encounter an unreachable case. - - ## Inbox messages must not be too large The current method of inbox hashing requires the full inbox message be available for proving. @@ -320,8 +270,6 @@ The current length limit is 117,964 bytes, which is 90% of the [max transaction size Geth will accept](https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/core/tx_pool.go#L53), leaving 13,108 bytes for other proving data. - - ## Requested preimages must be known and not too large WAVM has an opcode which resolves the preimage of a Keccak-256 hash. @@ -333,8 +281,6 @@ The current length limit is 117,964 bytes for the reasons mentioned above. Here's a list of which preimages may be requested by Nitro, and why they're known to all parties, and not too large: - - ### Block headers Nitro may request up to the last 256 L2 block headers. @@ -343,8 +289,6 @@ and blocks before it are required to implement the `BLOCKHASH` evm instruction. This is safe as previous block headers are a fixed size, and are known to all nodes. - - ### State trie access To resolve state, Nitro traverses the state trie by resolving preimages. @@ -356,11 +300,8 @@ which is limited by EIP-170 to about 24KB. - - - - + # WASM to WAVM Not all WASM instructions are 1:1 with WAVM opcodes. @@ -368,8 +309,6 @@ This document lists those which are not, and explains how they're expressed in W Many of the WAVM representations use opcodes not in WASM, which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). - - ## `block` and `loop` In WASM, a block contains instructions. @@ -380,8 +319,6 @@ In WAVM, instructions are flat. At transpilation time, any branch instructions are replaced with jumps to the corresponding block's destination. This means that WAVM interpreters don't need to track blocks, and thus block instructions are unnecessary. - - ## `if` and `else` These are translated to a block with an `ArbitraryJumpIf` as follows: @@ -400,8 +337,6 @@ end `br` and `br_if` are translated into `ArbitraryJump` and `ArbitraryJumpIf` respectively. The jump locations can be known at transpilation time, making blocks obsolete. - - ## `br_table` `br_table` is translated to a check for each possible branch in the table, @@ -410,14 +345,10 @@ and then if none of the checks hit, a branch of the default level. Each of the non-default branches has a conditional jump to a section afterwards, containing a `drop` for the selector, and then a jump to the target branch. - - ## `local.tee` `local.tee` is translated to a WAVM `Dup` and then a `LocalSet`. - - ## `return` To translate a return, the number of return values must be known from the function signature. @@ -426,8 +357,6 @@ Then, a loop checks `IsStackBoundary` (which implicitly pops a value) until it's Next, a `MoveFromInternalToStack` is added for each return value to put the return values back on the stack. Finally, a WAVM `Return` is added, returning control flow to the caller. - - ## Floating point instructions A floating point library module must be present to translate floating point instructions. @@ -437,11 +366,8 @@ and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and ` - - - - + # WAVM Custom opcodes not in WASM In addition to the MVP WASM specification, @@ -450,8 +376,6 @@ WAVM implements the multi value and sign extension ops WASM proposals. WAVM also implements the following unique opcodes, which are not part of WASM nor any WASM proposal. - - ## Invariants Many of these opcodes have implicit invariants about what's on the stack, @@ -459,8 +383,6 @@ e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. If these conditions are not satisfied, execution is generally not possible. These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) - - ## Codegen internal These are generated when breaking down a WASM instruction that does many things into many WAVM instructions which each do one thing. @@ -490,8 +412,6 @@ The above opcodes eliminate the need for the following WASM opcodes (which are t - br_table - local.tee - - ## Linking This is only generated to link modules together. @@ -502,8 +422,6 @@ which handles the actual work needed to change modules. | ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 0x8009 | CrossModuleCall | Pushes the current program counter, module number, and module's internals offset to the stack. Then splits its argument data into the lower 32 bits being a function index, and the upper 32 bits being a module index, and jumps to the beginning of that function. | - - ## Host calls These are only used in the implementation of "host calls". @@ -527,11 +445,8 @@ For these instruction descriptions, all pointers and offsets are represented as - - - - + # WAVM Floating point implementation Implementing correct, consistent, and deterministic floating point operations directly in WAVM @@ -559,8 +474,6 @@ and floating points may be used outside core state transition code for imprecise but the former is well exercised as used in Nitro, and the latter generally doesn't rely on details like the minimum of NaN and infinity. - - ## Known divergences from the WASM specification Floating point to integer truncation will saturate on overflow, instead of erroring. @@ -569,10 +482,8 @@ A WASM proposal exists to add new opcodes which are defined to saturate, but it' - - - + # WAVM Modules WASM natively has a notion of modules. @@ -592,8 +503,6 @@ and then calls the main module's main function, which is language specific. For Go it sets argv to `["js"]` to match the JS environment, and calls `run`. For Rust it calls `main` with no arguments. - - ## Library exports Libraries may export functions with the name pattern `module__name`, @@ -602,8 +511,6 @@ which future libraries or the main module can import as `"module" "name"`. For instance, this is used for wasi-stub to provide functions rust imports according to the WebAssembly System Interface. - - ## Floating point operations To provide floating point operations for future libraries, @@ -614,8 +521,6 @@ Their type signature is also the same, except all `f32`s and `f64`s are bitcaste Future modules can implicitly use these by using WASM floating point operations, which are replaced at the WASM->WAVM level with bitcasts and cross module calls to these functions. - - ## WAVM guest calls Libraries may call the main module's exports via `"env" "wavm_guest_call__*"`. @@ -624,8 +529,6 @@ For instance, go-stub calls Go's resume function when queueing async events via `wavm_guest_call_resume()`, and then retrieves the new stack pointer with `wavm_guest_call_getsp()`. - - ## Caller module internals call Every stack frame retains its caller module and its caller module's "internals offset", @@ -639,4 +542,4 @@ Only libraries can access their caller's memory; the main module cannot. For instance, this is used to read arguments from and write return values to the Go stack, when Go calls into go-stub. - \ No newline at end of file + diff --git a/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx index 7834945f9..927d053b0 100644 --- a/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx @@ -70,13 +70,11 @@ Once the Sequencer has collected enough signatures, it can aggregate the signatu If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. - - - \ No newline at end of file +--> diff --git a/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx index 880e5490c..16c9b8fc4 100644 --- a/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx @@ -35,8 +35,8 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu [estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/d52739e6d54f2ea06146fdc44947af3488b89082/internal/ethapi/api.go#L999 - + ## Gas and Fees NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. @@ -84,10 +84,11 @@ The price per estimated byte is set by a dynamic algorithm that compares the tot #### Total fee and gas estimation The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. - + + # L1 gas pricing ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amount collected in L1 gas fees is as close as possible to the costs that must be covered, over time. @@ -144,4 +145,5 @@ A second term is added to the L1 Gas Basefee, based on the derivative of the sur The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. - \ No newline at end of file + + diff --git a/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx b/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx index a8cc6781f..44d004b4a 100644 --- a/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx @@ -1,4 +1,5 @@ - + + ## Retryable Tickets Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. @@ -7,8 +8,6 @@ Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages Here we walk through the different stages of the lifecycle of a retryable ticket; (1) submission, (2) auto-redemption, and (3) manual redemption. - - ### Submission 1. Creating a retryable ticket is initiated with a call (direct or internal) to the `createRetryableTicket` function of the [`inbox` contract][inbox_link]. A ticket is guaranteed to be created if this call succeeds. Here, we describe parameters that need to be carefully set. Note that, this function forces the sender to provide a _reasonable_ amount of funds (at least enough to submitting, and _attempting_ to executing the ticket), but that doesn't guarantee a successful auto-redemption. @@ -69,8 +68,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - - ### Automatic Redemption 4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: @@ -111,8 +108,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - - ### Manual Redemption 5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. @@ -180,9 +175,6 @@ If a redeem is not done at submission or the submission's initial redeem fails, [retryable_dashboard_link]: https://retryable-dashboard.arbitrum.io/tx - - - ### Receipts In the lifecycle of a retryable ticket, two types of L2 transaction receipts will be emitted: @@ -190,14 +182,10 @@ In the lifecycle of a retryable ticket, two types of L2 transaction receipts wil - **Ticket Creation Receipt**: This receipt indicates that a ticket was successfully created; any successful L1 call to the `Inbox`'s `createRetryableTicket` method is guaranteed to create a ticket. The ticket creation receipt includes a `TicketCreated` event (from `ArbRetryableTx`), which includes a `ticketId` field. This `ticketId` is computable via RLP encoding and hashing the transaction; see [calculateSubmitRetryableId](https://github.com/OffchainLabs/arbitrum-sdk/blob/6cc143a3bb019dc4c39c8bcc4aeac9f1a48acb01/src/lib/message/L1ToL2Message.ts#L109). - **Redeem Attempt**: A redeem attempt receipt represents the result of an attempted L2 execution of a ticket, i.e, success / failure of that specific redeem attempt. It includes a `RedeemScheduled` event from `ArbRetryableTx`, with a `ticketId` field. At most, one successful redeem attempt can ever exist for a given ticket; if, e.g., the auto-redeem upon initial creation succeeds, only the receipt from the auto-redeem will ever get emitted for that ticket. If the auto-redeem fails (or was never attempted — i.e., the provided L2 gas limit \* L2 gas price = 0), each initial attempt will emit a redeem attempt receipt until one succeeds. - - ### Alternative "unsafe" Retryable Ticket Creation The `Inbox.createRetryableTicket` convenience method includes sanity checks to help minimize the risk of user error: the method will ensure that enough funds are provided directly from L1 to cover the current cost of ticket creation. It also will convert the provided `callValueRefundAddress` and `excessFeeRefundAddress` to their address alias (see below) if either is a contract (determined by if the address has code during the call), providing a path for the L1 contract to recover funds. A power-user may bypass these sanity-check measures via the `Inbox`'s `unsafeCreateRetryableTicket` method; as the method's name desperately attempts to warn you, it should only be accessed by a user who truly knows what they're doing. - - ## Eth deposits A special message type exists for simple Eth deposits; i.e., sending Eth from L1 to L2. Eth can be deposited via a call to the `Inbox`'s `depositEth` method. If the L1 caller is EOA, the Eth will be deposited to the same EOA address on L2; the L1 caller is a contract, the funds will deposited to the contract's aliased address (see below). @@ -206,14 +194,10 @@ Note that depositing Eth via `depositEth` into a contract on L2 will _not_ trigg In principle, retryable tickets can alternatively be used to deposit Ether; this could be preferable to the special eth-deposit message type if, e.g., more flexibility for the destination address is needed, or if one wants to trigger the fallback function on the L2 side. - - ## Transacting via the Delayed Inbox While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. - - ### Address Aliasing Unsigned messages submitted via the Delayed Inbox get their sender's addressed "aliased": when these messages are executed on L2, the sender's address —i.e., that which is returned by `msg.sender` — will _not_ simply be the L1 address that sent the message; rather it will be the address's "L2 Alias." An address's L2 alias is its value increased by the hex value `0x1111000000000000000000000000000000001111`: @@ -238,10 +222,8 @@ modifier onlyFromMyL1Contract() override { } ``` - - ### Signed Messages The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. -These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). \ No newline at end of file +These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). diff --git a/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx index b07ba1541..f7ca047f6 100644 --- a/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx @@ -1,4 +1,5 @@ - + + Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. ### Protocol Flow @@ -20,4 +21,4 @@ The week long delay period before outgoing messages can be executed is inherent \*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. - \ No newline at end of file + diff --git a/website/sidebars.js b/website/sidebars.js index fa584892e..3b085150b 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -853,6 +853,16 @@ const sidebars = { id: 'how-arbitrum-works/gas-fees', label: 'Gas/fees', }, + { + type: 'doc', + id: 'how-arbitrum-works/retryable-tickets', + label: 'Retryable Tickets', + }, + { + type: 'doc', + id: 'how-arbitrum-works/cross-chain-messaging', + label: 'Cross-chain messaging', + }, { type: 'doc', id: 'how-arbitrum-works/nitro-vs-classic', @@ -864,58 +874,58 @@ const sidebars = { label: 'Nitro whitepaper', }, + { + type: 'category', + label: 'The BoLD dispute protocol', + items: [ { - type: 'category', - label: 'The BoLD dispute protocol', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/bold/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'link', - href: 'https://github.com/offchainlabs/bold-validator-starter-kit', - label: 'Deploy a validator on testnet', - }, - { - type: 'link', - href: 'https://arxiv.org/abs/2404.10491', - label: 'BoLD Whitepaper', - }, - { - type: 'doc', - id: 'bold/concepts/bold-technical-deep-dive', - label: 'Technical deep dive', - }, - { - type: 'doc', - id: 'how-arbitrum-works/bold/bold-economics-of-disputes', - label: 'Economics of disputes', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/bold', - label: 'Specification on Github', - }, - { - type: 'link', - href: 'https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf', - label: 'Audit Report by Trail of Bits', - }, - { - type: 'link', - href: 'https://code4rena.com/reports/2024-05-arbitrum-foundation', - label: 'Audit Report by Code4rena', - }, - { - type: 'doc', - id: 'how-arbitrum-works/bold/public-preview-expectations', - label: 'Public preview', - }, - ], + type: 'doc', + id: 'how-arbitrum-works/bold/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'link', + href: 'https://github.com/offchainlabs/bold-validator-starter-kit', + label: 'Deploy a validator on testnet', + }, + { + type: 'link', + href: 'https://arxiv.org/abs/2404.10491', + label: 'BoLD Whitepaper', + }, + { + type: 'doc', + id: 'bold/concepts/bold-technical-deep-dive', + label: 'Technical deep dive', + }, + { + type: 'doc', + id: 'how-arbitrum-works/bold/bold-economics-of-disputes', + label: 'Economics of disputes', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/bold', + label: 'Specification on Github', + }, + { + type: 'link', + href: 'https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf', + label: 'Audit Report by Trail of Bits', + }, + { + type: 'link', + href: 'https://code4rena.com/reports/2024-05-arbitrum-foundation', + label: 'Audit Report by Code4rena', + }, + { + type: 'doc', + id: 'how-arbitrum-works/bold/public-preview-expectations', + label: 'Public preview', }, ], + }, + ], }, { type: 'doc', From 093259edaf4f0a5ab8f19b2e248ba20416d2418a Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 26 Nov 2024 09:15:13 -0600 Subject: [PATCH 06/75] Modified docusaurus.config.js --- arbitrum-docs/how-arbitrum-works/06-challenges.mdx | 4 ++-- website/docusaurus.config.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx index ef3640fd2..396f223af 100644 --- a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-challenges.mdx @@ -3,7 +3,7 @@ ## Challenges Suppose the rollup chain looks like this: - + ![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. @@ -153,7 +153,7 @@ How to achieve this is more complicated. Research by a team at Cornell Tech, inc -import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; + # ChallengeManager diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 6140f97d3..878d81071 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -11,8 +11,8 @@ const config = { tagline: 'Arbitrum Docs', url: 'https://docs.arbitrum.io/', baseUrl: '/', - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'throw', + onBrokenLinks: 'ignore', + onBrokenMarkdownLinks: 'ignore', favicon: 'img/logo.svg', markdown: { mermaid: true, From 0b95e9545b315add2733e453a2fab4257934ffde Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:03:13 -0600 Subject: [PATCH 07/75] Renaming a few sections, removed dup content, little reorg --- .../01-a-gentle-introduction.mdx | 17 ------- .../02-transaction-lifecycle.mdx | 19 +++++++ .../how-arbitrum-works/03-sequencer.mdx | 4 ++ ...n-function.mdx => 04-geth-at-the-core.mdx} | 36 ++------------ .../045-separating-execution-from-proving.mdx | 31 ++++++++++++ ...alidation.mdx => 05-optimistic-rollup.mdx} | 36 ++++++++++++++ ...es.mdx => 06-interactive-fraud-proofs.mdx} | 49 +------------------ ...e-tickets.mdx => 09-l1-to-l2-messages.mdx} | 0 ...messaging.mdx => 10-l2-to-l1-messages.mdx} | 0 9 files changed, 96 insertions(+), 96 deletions(-) rename arbitrum-docs/how-arbitrum-works/{04-state-transition-function.mdx => 04-geth-at-the-core.mdx} (89%) create mode 100644 arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx rename arbitrum-docs/how-arbitrum-works/{05-validation.mdx => 05-optimistic-rollup.mdx} (85%) rename arbitrum-docs/how-arbitrum-works/{06-challenges.mdx => 06-interactive-fraud-proofs.mdx} (87%) rename arbitrum-docs/how-arbitrum-works/{09-retryable-tickets.mdx => 09-l1-to-l2-messages.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{10-cross-chain-messaging.mdx => 10-l2-to-l1-messages.mdx} (100%) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 58c08f27e..70608f105 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -71,20 +71,3 @@ The essence of Nitro, and its key innovations, lie in four big ideas. We'll list **Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. -## Sequencing, Followed by Deterministic Execution - -This diagram summarizes how transactions are processed in Nitro. - -![seq-then-exec](../assets/seq-then-exec.png) - -Let's follow a user's transaction through this process. - -First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. - -Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. - -Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) - -The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. - -It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 4f8c6b4a1..5c856736b 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -1,3 +1,22 @@ +## Sequencing, Followed by Deterministic Execution + +This diagram summarizes how transactions are processed in Nitro. + +![seq-then-exec](../assets/seq-then-exec.png) + +Let's follow a user's transaction through this process. + +First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. + +Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. + +Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) + +The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. + +It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. + + # Overview: The Lifecycle of an Arbitrum Transaction As an introduction to the various components that compose the Arbitrum protocol, we'll go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 202ff73da..5a0bea498 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -4,6 +4,10 @@ The Sequencer is a specially designated Arbitrum full node which, under normal c Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. +Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. + +[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. + ## The Core Inbox When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. diff --git a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx similarity index 89% rename from arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx rename to arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index 1113781eb..a3c0ab6db 100644 --- a/arbitrum-docs/how-arbitrum-works/04-state-transition-function.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -16,43 +16,17 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. -## Separating Execution from Proving -One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. - -When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) - -Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. - -#### WAVM - -The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. - -WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. - -Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. - -Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. - -#### ReadPreImage and the Hash Oracle Trick - -The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. - -(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) - -As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. - -The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. - -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - - # ArbOS -ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS accounts for and manages network resources, produces blocks from incoming messages, and operates its instrumented instance of Geth for smart contract execution. +ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. + +In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. + +Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. ## Precompiles diff --git a/arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx new file mode 100644 index 000000000..285a5e6d1 --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx @@ -0,0 +1,31 @@ +## Separating Execution from Proving + +One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. + +When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) + +Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. + +#### WAVM + +The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. + +WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. + +Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. + +Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. + +#### ReadPreImage and the Hash Oracle Trick + +The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. + +(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) + +As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. + +The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. + +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. + + \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/05-validation.mdx b/arbitrum-docs/how-arbitrum-works/05-optimistic-rollup.mdx similarity index 85% rename from arbitrum-docs/how-arbitrum-works/05-validation.mdx rename to arbitrum-docs/how-arbitrum-works/05-optimistic-rollup.mdx index ee41f182f..3e64a5935 100644 --- a/arbitrum-docs/how-arbitrum-works/05-validation.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-optimistic-rollup.mdx @@ -1,4 +1,5 @@ + ## Optimistic Rollup @@ -232,6 +233,41 @@ A consequence of these rules is that once the first unresolved RBlock's deadline + + + +## Validators + +Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. + +Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. + +Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. + + +Offchain Labs provides open source validator software, including a pre-built Docker image. + +Every validator can choose their own approach, but we expect validators to follow three common strategies: + +- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. +- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. +- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) + +Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. + +The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). + +Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. + +- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. +- Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. +- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. + + + + + + # The Assertion Tree diff --git a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx b/arbitrum-docs/how-arbitrum-works/06-interactive-fraud-proofs.mdx similarity index 87% rename from arbitrum-docs/how-arbitrum-works/06-challenges.mdx rename to arbitrum-docs/how-arbitrum-works/06-interactive-fraud-proofs.mdx index 396f223af..261586a48 100644 --- a/arbitrum-docs/how-arbitrum-works/06-challenges.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-interactive-fraud-proofs.mdx @@ -1,6 +1,6 @@ -## Challenges +## Fraud Proofs: Challenges Suppose the rollup chain looks like this: @@ -60,53 +60,6 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. -## Validators - -Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. - -Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. - -Offchain Labs provides open source validator software, including a pre-built Docker image. - -Every validator can choose their own approach, but we expect validators to follow three common strategies: - -- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. -- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. -- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) - -Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. - -The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). - -Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. - -- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. -- Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. -- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - -## ArbOS - -ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. - -### Why ArbOS? - -In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. - -Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. - -## Full Nodes - -As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. - -Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. - -## The Sequencer - -The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. - -Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. - -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. ### Instant confirmation diff --git a/arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx b/arbitrum-docs/how-arbitrum-works/09-l1-to-l2-messages.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/09-retryable-tickets.mdx rename to arbitrum-docs/how-arbitrum-works/09-l1-to-l2-messages.mdx diff --git a/arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l2-to-l1-messages.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/10-cross-chain-messaging.mdx rename to arbitrum-docs/how-arbitrum-works/10-l2-to-l1-messages.mdx From 401be69e73f95dec94ec9f03c3d0555da3506a87 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:06:47 -0600 Subject: [PATCH 08/75] Updating filenames for section headings --- ...-from-proving.mdx => 05-separating-execution-from-proving.mdx} | 0 .../{05-optimistic-rollup.mdx => 06-optimistic-rollup.mdx} | 0 ...teractive-fraud-proofs.mdx => 07-interactive-fraud-proofs.mdx} | 0 .../{07-anytrust-protocol.mdx => 08-anytrust-protocol.mdx} | 0 .../how-arbitrum-works/{08-gas-fees.mdx => 09-gas-fees.mdx} | 0 .../{09-l1-to-l2-messages.mdx => 10-l1-to-l2-messages.mdx} | 0 .../{10-l2-to-l1-messages.mdx => 11-l2-to-l1-messages.mdx} | 0 .../{11-nitro-vs-classic.mdx => 12-nitro-vs-classic.mdx} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename arbitrum-docs/how-arbitrum-works/{045-separating-execution-from-proving.mdx => 05-separating-execution-from-proving.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{05-optimistic-rollup.mdx => 06-optimistic-rollup.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{06-interactive-fraud-proofs.mdx => 07-interactive-fraud-proofs.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{07-anytrust-protocol.mdx => 08-anytrust-protocol.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{08-gas-fees.mdx => 09-gas-fees.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{09-l1-to-l2-messages.mdx => 10-l1-to-l2-messages.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{10-l2-to-l1-messages.mdx => 11-l2-to-l1-messages.mdx} (100%) rename arbitrum-docs/how-arbitrum-works/{11-nitro-vs-classic.mdx => 12-nitro-vs-classic.mdx} (100%) diff --git a/arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/045-separating-execution-from-proving.mdx rename to arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx diff --git a/arbitrum-docs/how-arbitrum-works/05-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/05-optimistic-rollup.mdx rename to arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx diff --git a/arbitrum-docs/how-arbitrum-works/06-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/06-interactive-fraud-proofs.mdx rename to arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx diff --git a/arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/07-anytrust-protocol.mdx rename to arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx diff --git a/arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/08-gas-fees.mdx rename to arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx diff --git a/arbitrum-docs/how-arbitrum-works/09-l1-to-l2-messages.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/09-l1-to-l2-messages.mdx rename to arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx diff --git a/arbitrum-docs/how-arbitrum-works/10-l2-to-l1-messages.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messages.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/10-l2-to-l1-messages.mdx rename to arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messages.mdx diff --git a/arbitrum-docs/how-arbitrum-works/11-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/11-nitro-vs-classic.mdx rename to arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx From 76bb1811be134ddb90b202156266d3f881aeaa0e Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:08:44 -0600 Subject: [PATCH 09/75] yarn format cleanup --- .../how-arbitrum-works/01-a-gentle-introduction.mdx | 1 - .../how-arbitrum-works/02-transaction-lifecycle.mdx | 1 - arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 2 +- arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx | 2 -- .../05-separating-execution-from-proving.mdx | 2 +- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 8 +------- .../how-arbitrum-works/07-interactive-fraud-proofs.mdx | 3 +-- 7 files changed, 4 insertions(+), 15 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 70608f105..463aba3a5 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -70,4 +70,3 @@ The essence of Nitro, and its key innovations, lie in four big ideas. We'll list **Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. **Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. - diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 5c856736b..7957d9e63 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -16,7 +16,6 @@ The state transition function is deterministic, which means that its behavior de It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. - # Overview: The Lifecycle of an Arbitrum Transaction As an introduction to the various components that compose the Arbitrum protocol, we'll go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 5a0bea498..6059e08b5 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -6,7 +6,7 @@ Here we will describe the mechanics of how the Sequencer typically operates, and Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. +[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. ## The Core Inbox diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index a3c0ab6db..72d59d4a8 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -16,8 +16,6 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - - # ArbOS diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 285a5e6d1..04e85021c 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -28,4 +28,4 @@ The only other use of `ReadPreImage` is to fetch the contents of recent L2 block This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - \ No newline at end of file + diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 3e64a5935..ad31643b3 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -233,9 +233,8 @@ A consequence of these rules is that once the first unresolved RBlock's deadline - - + ## Validators Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. @@ -244,7 +243,6 @@ Some Arbitrum nodes will choose to act as _validators_. This means that they wat Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. - Offchain Labs provides open source validator software, including a pre-built Docker image. Every validator can choose their own approach, but we expect validators to follow three common strategies: @@ -264,10 +262,6 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - - - - # The Assertion Tree diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 261586a48..f3bef7dd1 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -3,7 +3,7 @@ ## Fraud Proofs: Challenges Suppose the rollup chain looks like this: - + ![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. @@ -60,7 +60,6 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - ### Instant confirmation Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. From 10164682f477a7c5621d8ce2f190cf5872f2be0f Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:21:25 -0600 Subject: [PATCH 10/75] fixing sidebars.js --- .../07-interactive-fraud-proofs.mdx | 2 +- .../10-l1-to-l2-messages.mdx | 7 +++++- website/sidebars.js | 25 +++++++++++-------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index f3bef7dd1..6e8964c94 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -111,7 +111,7 @@ How to achieve this is more complicated. Research by a team at Cornell Tech, inc The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: - + ## Block challenge diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx index 44d004b4a..1fa7da437 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx @@ -76,6 +76,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). + Auto-redeem succeeds? @@ -108,6 +109,8 @@ Here we walk through the different stages of the lifecycle of a retryable ticket + + ### Manual Redemption 5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. @@ -119,6 +122,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 [renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 + Ticket manually cancelled or not redeemed in 7 days? @@ -151,6 +155,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket + :::caution Avoid Losing Funds! If a ticket expires after 7 days without being redeemed or re-scheduled to a future date, any message and value (other than the escrowed `callvalue`) it carries could be lost without possibility of being recovered. @@ -208,7 +213,7 @@ L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111 :::tip Try it out - + ::: The Arbitrum protocol's usage of L2 Aliases for L1-to-L2 messages prevents cross-chain exploits that would otherwise be possible if we simply reused the same L1 addresses as the L2 sender; i.e., tricking an L2 contract that expects a call from a given contract address by sending retryable ticket from the expected contract address on L1. diff --git a/website/sidebars.js b/website/sidebars.js index 3b085150b..6df6114b7 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -830,18 +830,23 @@ const sidebars = { }, { type: 'doc', - id: 'how-arbitrum-works/state-transition-function', - label: 'State Transition Function', + id: 'how-arbitrum-works/geth-at-the-core', + label: 'Geth at the Core', }, { type: 'doc', - id: 'how-arbitrum-works/validation', - label: 'Validation', + id: 'how-arbitrum-works/separating-execution-from-proving', + label: 'Separating Execution from Proving', }, { type: 'doc', - id: 'how-arbitrum-works/challenges', - label: 'Challenges', + id: 'how-arbitrum-works/optimistic-rollup', + label: 'Optimistic Rollup', + }, + { + type: 'doc', + id: 'how-arbitrum-works/interactive-fraud-proofs', + label: 'Interactive Fraud Proofs', }, { type: 'doc', @@ -855,13 +860,13 @@ const sidebars = { }, { type: 'doc', - id: 'how-arbitrum-works/retryable-tickets', - label: 'Retryable Tickets', + id: 'how-arbitrum-works/l1-to-l2-messages', + label: 'L1 to L2 messages', }, { type: 'doc', - id: 'how-arbitrum-works/cross-chain-messaging', - label: 'Cross-chain messaging', + id: 'how-arbitrum-works/l2-to-l1-messages', + label: 'L2 to L1 messages', }, { type: 'doc', From d2fc3f19934c2a8a83f670c9fd3e5f3fba4eb757 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:25:51 -0600 Subject: [PATCH 11/75] vercel having issues with mermaid commented out diagrams --- .../how-arbitrum-works/10-l1-to-l2-messages.mdx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx index 1fa7da437..019d1b1a8 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx @@ -30,6 +30,9 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol + + + + + + ### Automatic Redemption 4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: @@ -77,6 +84,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). + ### Manual Redemption @@ -123,6 +131,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 + :::caution Avoid Losing Funds! From 5425991b050b9ca45cf58878486282e8d3e957f7 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:45:03 -0600 Subject: [PATCH 12/75] put arbitrum-sdk at proper pin --- arbitrum-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-sdk b/arbitrum-sdk index 5ef44308d..f5b3d04ba 160000 --- a/arbitrum-sdk +++ b/arbitrum-sdk @@ -1 +1 @@ -Subproject commit 5ef44308d3c89fd956c9dfdc59b6776b88afd251 +Subproject commit f5b3d04baf62356032f5475057637d989da5b6c1 From 339bd1bae29abe1a6cb68b864d2e61350e255123 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:54:20 -0600 Subject: [PATCH 13/75] adjusted headings to ensure visibility in tree --- .../02-transaction-lifecycle.mdx | 2 +- .../how-arbitrum-works/03-sequencer.mdx | 18 ++-- .../04-geth-at-the-core.mdx | 94 +++++++++---------- .../07-interactive-fraud-proofs.mdx | 64 ++++++------- .../08-anytrust-protocol.mdx | 76 +-------------- .../how-arbitrum-works/09-gas-fees.mdx | 34 +++---- .../12-nitro-vs-classic.mdx | 8 +- 7 files changed, 111 insertions(+), 185 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 7957d9e63..d2602905f 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -16,7 +16,7 @@ The state transition function is deterministic, which means that its behavior de It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. -# Overview: The Lifecycle of an Arbitrum Transaction +## Overview: The Lifecycle of an Arbitrum Transaction As an introduction to the various components that compose the Arbitrum protocol, we'll go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 6059e08b5..8b07e1141 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -1,4 +1,4 @@ -# The Sequencer and Censorship Resistance +## The Sequencer and Censorship Resistance The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. @@ -8,19 +8,19 @@ Clients interact with the Sequencer in exactly the same way they would interact [Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. -## The Core Inbox +### The Core Inbox When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. -## Bridging +### Bridging We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). -### L1 contracts can submit L2 transactions +#### L1 contracts can submit L2 transactions An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. @@ -28,7 +28,7 @@ The advantage of this method is that it is simple and has relatively low latency This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. -### L1 to L2 ticket-based transactions +#### L1 to L2 ticket-based transactions Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. @@ -44,7 +44,7 @@ When the ticket is redeemed, the pre-packaged transaction runs with sender and o This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. -### L2 to L1 ticket-based calls +#### L2 to L1 ticket-based calls Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. @@ -52,7 +52,7 @@ These L2-to-L1 tickets have unlimited lifetime, until they’re successfully red -## Happy/Common Case: Sequencer Is Live and Well-behaved +### Happy/Common Case: Sequencer Is Live and Well-behaved Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. @@ -63,7 +63,7 @@ Alternatively, a user can submit their L2 message to the Sequencer by posting it In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. -## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job +### Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: @@ -79,7 +79,7 @@ While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, -### How the Sequencer Publishes the Sequence +#### How the Sequencer Publishes the Sequence So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index 72d59d4a8..8c8bf6252 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -18,7 +18,7 @@ The State Transition Function consists of the bottom Geth layer, and a portion o -# ArbOS +## ArbOS ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. @@ -26,7 +26,7 @@ In Arbitrum, much of the work that would otherwise have to be done expensively a Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. -## Precompiles +### Precompiles ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. Visit the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx) for more information about how these work, and the [precompiles reference page](/build-decentralized-apps/precompiles/02-reference.mdx) for a full reference of the precompiles available in Arbitrum chains. @@ -49,18 +49,18 @@ Each time a transaction calls a method of an L2-specific precompile, a [`call co [packing_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L438 [call_context_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/context.go#L26 -## Messages +### Messages An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. [l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 [produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 -## Retryables +### Retryables A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). -## ArbOS State +### ArbOS State ArbOS's state is viewed and modified via [`ArbosState`][arbosstate_link] objects, which provide convenient abstractions for working with the underlying data of its [`backingStorage`][backingstorage_link]. The backing storage's [keyed subspace strategy][subspace_link] makes possible [`ArbosState`][arbosstate_link]'s convenient getters and setters, minimizing the need to directly work with the specific keys and values of the underlying storage's [`stateDB`][statedb_link]. @@ -75,7 +75,7 @@ Much of ArbOS's state exists to facilitate its [precompiles](/build-decentralize [openarbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L57 [burner_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/burn/burn.go#L11 -### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] +#### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] ArbOS upgrades are scheduled to happen [when finalizing the first block][finalizeblock_link] after the [`upgradeTimestamp`][upgradetimestamp_link]. @@ -84,7 +84,7 @@ ArbOS upgrades are scheduled to happen [when finalizing the first block][finaliz [upgradetimestamp_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L39 [finalizeblock_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L350 -### [`blockhashes`][blockhashes_link] +#### [`blockhashes`][blockhashes_link] This component maintains the last 256 L1 block hashes in a circular buffer. This allows the [`TxProcessor`][txprocessor_link] to implement the `BLOCKHASH` and `NUMBER` opcodes as well as support precompile methods that involve the outbox. To avoid changing ArbOS state outside of a transaction, blocks made from messages with a new L1 block number update this info during an [`InternalTxUpdateL1BlockNumber`][internaltxupdatel1blocknumber_link] [`ArbitrumInternalTx`][arbitruminternaltx_link] that is included as the first transaction in the block. @@ -93,7 +93,7 @@ This component maintains the last 256 L1 block hashes in a circular buffer. This [arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L116 [txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 -### [`l1PricingState`][l1pricingstate_link] +#### [`l1PricingState`][l1pricingstate_link] In addition to supporting the [`ArbAggregator precompile`](/build-decentralized-apps/precompiles/02-reference.mdx#arbaggregator), the L1 pricing state provides tools for determining the L1 component of a transaction's gas costs. This part of the state tracks both the total amount of funds collected from transactions in L1 gas fees, as well as the funds spent by batch posters to post data batches on L1. @@ -101,7 +101,7 @@ Based on this information, ArbOS maintains an L1 data fee, also tracked as part [l1pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l1pricing/l1pricing.go#L16 -### [`l2PricingState`][l2pricingstate_link] +#### [`l2PricingState`][l2pricingstate_link] The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. @@ -129,7 +129,7 @@ ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [se -# Geth +## Geth Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This document will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. @@ -144,7 +144,7 @@ Please note any links on this page may be referencing old releases of Nitro or o ::: -## Hooks +### Hooks Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. @@ -170,11 +170,11 @@ Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additiona What follows is an overview of each hook, in chronological order. -### [`ReadyEVMForL2`][readyevmforl2_link]{#ReadyEVMForL2} +#### [`ReadyEVMForL2`][readyevmforl2_link]{#ReadyEVMForL2} A call to [`ReadyEVMForL2`][readyevmforl2_link] installs the other transaction-specific hooks into each Geth [`EVM`][evm_link] right before it performs a state transition. Without this call, the state transition will instead use the default [`DefaultTxProcessor`][defaulttxprocessor_link] and get exactly the same results as vanilla Geth. A [`TxProcessor`][txprocessor_link] object is what carries these hooks and the associated Arbitrum-specific state during the transaction's lifetime. -### [`StartTxHook`][starttxhook_link]{#StartTxHook} +#### [`StartTxHook`][starttxhook_link]{#StartTxHook} The [`StartTxHook`][starttxhook_link] is called by Geth before a transaction starts executing. This allows ArbOS to handle two Arbitrum-specific transaction types. @@ -184,7 +184,7 @@ If the transaction is an `ArbitrumSubmitRetryableTx`, ArbOS creates a retryable The hook returns `true` for both of these transaction types, signifying that the state transition is complete. -### [`GasChargingHook`][gascharginghook_link]{#GasChargingHook} +#### [`GasChargingHook`][gascharginghook_link]{#GasChargingHook} This fallible hook ensures the user has enough funds to pay their poster's L1 calldata costs. If not, the transaction is reverted and the [`EVM`][evm_link] does not start. In the common case that the user can pay, the amount paid for calldata is set aside for later reimbursement of the poster. All other fees go to the network account, as they represent the transaction's burden on validators and nodes more generally. @@ -195,23 +195,23 @@ If the user attempts to purchase compute gas in excess of ArbOS's per-block gas [that_seen_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L176 [max_perblock_limit_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/l2pricing/l2pricing.go#L86 -### [`PushCaller`][pushcaller_link]{#PushCaller} +#### [`PushCaller`][pushcaller_link]{#PushCaller} These hooks track the callers within the EVM callstack, pushing and popping as calls are made and complete. This provides [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) with info about the callstack, which it uses to implement the methods `WasMyCallersAddressAliased` and `MyCallersAddressWithoutAliasing`. -### [`L1BlockHash`][l1blockhash_link] +#### [`L1BlockHash`][l1blockhash_link] In Arbitrum, the BlockHash and Number operations return data that relies on underlying L1 blocks instead of L2 blocks, to accommodate the normal use-case of these opcodes, which often assume Ethereum-like time passes between different blocks. The L1BlockHash and L1BlockNumber hooks have the required data for these operations. -### [`ForceRefundGas`][forcerefundgas_link]{#ForceRefundGas} +#### [`ForceRefundGas`][forcerefundgas_link]{#ForceRefundGas} This hook allows ArbOS to add additional refunds to the user's tx. This is currently only used to refund any compute gas purchased in excess of ArbOS's per-block gas limit during the [`GasChargingHook`](#GasChargingHook). -### [`NonrefundableGas`][nonrefundablegas_link]{#NonrefundableGas} +#### [`NonrefundableGas`][nonrefundablegas_link]{#NonrefundableGas} Because poster costs come at the expense of L1 aggregators and not the network more broadly, the amounts paid for L1 calldata should not be refunded. This hook provides Geth access to the equivalent amount of L2 gas the poster's cost equals, ensuring this amount is not reimbursed for network-incentivized behaviors like freeing storage slots. -### [`EndTxHook`][endtxhook_link]{#EndTxHook} +#### [`EndTxHook`][endtxhook_link]{#EndTxHook} The [`EndTxHook`][endtxhook_link] is called after the [`EVM`][evm_link] has returned a transaction's result, allowing one last opportunity for ArbOS to intervene before the state transition is finalized. Final gas amounts are known at this point, enabling ArbOS to credit the network and poster each's share of the user's gas expenditures as well as adjust the pools. The hook returns from the [`TxProcessor`][txprocessor_link] a final time, in effect discarding its state as the system moves on to the next transaction where the hook's contents will be set afresh. @@ -230,23 +230,23 @@ The [`EndTxHook`][endtxhook_link] is called after the [`EVM`][evm_link] has retu [l1blockhash_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L617 [l1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/tx_processor.go#L600 -## Interfaces and components +### Interfaces and components -### [`APIBackend`][apibackend_link] +#### [`APIBackend`][apibackend_link] APIBackend implements the [`ethapi.Backend`][ethapi.backend_link] interface, which allows simple integration of the Arbitrum chain to existing Geth API. Most calls are answered using the Backend member. [apibackend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/apibackend.go#L34 [ethapi.backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/backend.go#L42 -### [`Backend`][backend_link] +#### [`Backend`][backend_link] This struct was created as an Arbitrum equivalent to the [`Ethereum`][ethereum_link] struct. It is mostly glue logic, including a pointer to the ArbInterface interface. [backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/backend.go#L15 [ethereum_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/eth/backend.go#L68 -### [`ArbInterface`][arbinterface_link] +#### [`ArbInterface`][arbinterface_link] This interface is the main interaction-point between geth-standard APIs and the Arbitrum chain. Geth APIs mostly either check status by working on the Blockchain struct retrieved from the [`Blockchain`][blockchain_link] call, or send transactions to Arbitrum using the [`PublishTransactions`][publishtransactions_link] call. @@ -254,7 +254,7 @@ This interface is the main interaction-point between geth-standard APIs and the [blockchain_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L12 [publishtransactions_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/arbos_interface.go#L11 -### [`RecordingKV`][recordingkv_link] +#### [`RecordingKV`][recordingkv_link] RecordingKV is a read-only key-value store, which retrieves values from an internal trie database. All values accessed by a RecordingKV are also recorded internally. This is used to record all preimages accessed during block creation, which will be needed to prove execution of this particular block. A [`RecordingChainContext`][recordingchaincontext_link] should also be used, to record which block headers the block execution reads (another option would be to always assume the last 256 block headers were accessed). @@ -265,7 +265,7 @@ The process is simplified using two functions: [`PrepareRecording`][preparerecor [preparerecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L152 [preimagesfromrecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/recordingdb.go#L174 -## Transaction Types +### Transaction Types Nitro Geth includes a few L2-specific transaction types. Click on any to jump to their section. @@ -289,31 +289,31 @@ Nitro Geth includes a few L2-specific transaction types. Click on any to jump to The following reference documents each type. -### [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link]{#ArbitrumUnsignedTx} +#### [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link]{#ArbitrumUnsignedTx} Provides a mechanism for a user on L1 to message a contract on L2. This uses the bridge for authentication rather than requiring the user's signature. Note, the user's acting address will be remapped on L2 to distinguish them from a normal L2 caller. -### [`ArbitrumContractTx`][arbitrumcontracttx_link]{#ArbitrumContractTx} +#### [`ArbitrumContractTx`][arbitrumcontracttx_link]{#ArbitrumContractTx} These are like an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link] but are intended for smart contracts. These use the bridge's unique, sequential nonce rather than requiring the caller specify their own. An L1 contract may still use an [`ArbitrumUnsignedTx`][arbitrumunsignedtx_link], but doing so may necessitate tracking the nonce in L1 state. -### [`ArbitrumDepositTx`][arbitrumdeposittx_link]{#ArbitrumDepositTx} +#### [`ArbitrumDepositTx`][arbitrumdeposittx_link]{#ArbitrumDepositTx} Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. -### [`ArbitrumSubmitRetryableTx`][arbitrumsubmitretryabletx_link]{#ArbitrumSubmitRetryableTx} +#### [`ArbitrumSubmitRetryableTx`][arbitrumsubmitretryabletx_link]{#ArbitrumSubmitRetryableTx} Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. -### [`ArbitrumRetryTx`][arbitrumretrytx_link]{#ArbitrumRetryTx} +#### [`ArbitrumRetryTx`][arbitrumretrytx_link]{#ArbitrumRetryTx} These are scheduled by calls to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile and via retryable auto-redemption. Please see the [retryables documentation](/how-arbitrum-works/arbos/introduction.mdx#Retryables) for more info. -### [`ArbitrumInternalTx`][arbitruminternaltx_link]{#ArbitrumInternalTx} +#### [`ArbitrumInternalTx`][arbitruminternaltx_link]{#ArbitrumInternalTx} Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a transaction of this type to update its state in-between user-generated transactions. Such a transaction has a [`Type`][internaltype_link] field signifying the state it will update, though currently this is just future-proofing as there's only one value it may have. Below are the internal transaction types. -#### [`InternalTxStartBlock`][arbinternaltxstartblock_link] +##### [`InternalTxStartBlock`][arbinternaltxstartblock_link] Updates the L1 block number and L1 base fee. This transaction [is generated][block_generated_link] whenever a new block is created. They are [guaranteed to be the first][block_first_link] in their L2 block. @@ -328,7 +328,7 @@ Updates the L1 block number and L1 base fee. This transaction [is generated][blo [block_generated_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L181 [block_first_link]: https://github.com/OffchainLabs/nitro/blob/8e786ec6d1ac3862be85e0c9b5ac79cbd883791c/arbos/block_processor.go#L182 -## Transaction Run Modes and Underlying Transactions +### Transaction Run Modes and Underlying Transactions A [geth message][geth_message_link] may be processed for various purposes. For example, a message may be used to estimate the gas of a contract call, whereas another may perform the corresponding state transition. Nitro Geth denotes the intent behind a message by means of its [`TxRunMode`][txrunmode_link], [which it sets][set_run_mode_link] before processing it. ArbOS uses this info to make decisions about the transaction the message ultimately constructs. @@ -349,64 +349,64 @@ A message [derived from a transaction][asmessage_link] will carry that transacti [asmessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L676 [underlying_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go#L700 -## Arbitrum Chain Parameters +### Arbitrum Chain Parameters Nitro's Geth may be configured with the following [l2-specific chain parameters][chain_params_link]. These allow the rollup creator to customize their rollup at genesis. -### `EnableArbos` +#### `EnableArbos` Introduces [ArbOS](/how-arbitrum-works/arbos/introduction.mdx), converting what would otherwise be a vanilla L1 chain into an L2 Arbitrum rollup. -### `AllowDebugPrecompiles` +#### `AllowDebugPrecompiles` Allows access to debug precompiles. Not enabled for Arbitrum One. When false, calls to debug precompiles will always revert. -### `DataAvailabilityCommittee` +#### `DataAvailabilityCommittee` Currently does nothing besides indicate that the rollup will access a data availability service for preimage resolution in the future. This is not enabled for Arbitrum One, which is a strict state-function of its L1 inbox messages. [chain_params_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/params/config_arbitrum.go#L25 -## Miscellaneous Geth Changes +### Miscellaneous Geth Changes -### ABI Gas Margin +#### ABI Gas Margin Vanilla Geth's abi library submits txes with the exact estimate the node returns, employing no padding. This means a transaction may revert should another arriving just before even slightly change the transaction's codepath. To account for this, we've added a `GasMargin` field to `bind.TransactOpts` that [pads estimates][pad_estimates_link] by the number of basis points set. -### Conservation of L2 ETH +#### Conservation of L2 ETH The total amount of L2 ether in the system should not change except in controlled cases, such as when bridging. As a safety precaution, ArbOS checks Geth's [balance delta][conservation_link] each time a block is created, [alerting or panicking][alert_link] should conservation be violated. -### MixDigest and ExtraData +#### MixDigest and ExtraData To aid with [outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners/stakers, to store the send count. -### Retryable Support +#### Retryable Support Retryables are mostly implemented in [ArbOS](/how-arbitrum-works/arbos/introduction.mdx#retryables). Some modifications were required in Geth to support them. - Added ScheduledTxes field to ExecutionResult. This lists transactions scheduled during the execution. To enable using this field, we also pass the ExecutionResult to callers of ApplyTransaction. - Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retryable activated by the original call. This allows estimating gas to enable retryables. -### Added accessors +#### Added accessors Added [`UnderlyingTransaction`][underlyingtransaction_link] to Message interface Added [`GetCurrentTxLogs`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state/statedb_arbitrum.go) to StateDB We created the AdvancedPrecompile interface, which executes and charges gas with the same function call. This is used by [Arbitrum's precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), and also wraps Geth's standard precompiles. -### WASM build support +#### WASM build support The WASM Arbitrum executable does not support file operations. We created [`fileutil.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. -### Types +#### Types Arbitrum introduces a new [`signer`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arbitrum_signer.go), and multiple new [`transaction types`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/transaction.go). -### ReorgToOldBlock +#### ReorgToOldBlock Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the [`ReorgToOldBlock`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/blockchain_arbitrum.go#L38) function to support reorging to a block that's an ancestor of current head. -### Genesis block creation +#### Genesis block creation Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out [`WriteHeadBlock`][writeheadblock_link] from genesis.Commit and use it to commit non-zero genesis blocks. diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 6e8964c94..d477a306f 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -107,13 +107,13 @@ How to achieve this is more complicated. Research by a team at Cornell Tech, inc -# ChallengeManager +## ChallengeManager The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: -## Block challenge +### Block challenge The challenge begins by bisecting over global states (including block hashes). Before actual machine execution is disputed, the dispute is narrowed down to an individual block. @@ -122,7 +122,7 @@ Once the challenge has been bisected down to an individual block, This operates similarly to a bisection in that the responder must provide a competing global state and machine state, but it uses that information to transition to the execution challenge phase. -## Execution challenge +### Execution challenge Once narrowed down to an individual block, the actual machine execution can be bisected. Once the execution has been bisected down to an individual step, @@ -131,7 +131,7 @@ The current responder must provide proof data to execute a step of the machine. If executing that step ends in a different state than was previously asserted, the current responder wins the challenge. -## General bisection protocol +### General bisection protocol _**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ @@ -161,7 +161,7 @@ Note that unlike in a traditional bisection protocol, where one party proposes s this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections when challenging. -## Winning the challenge +### Winning the challenge Note that for the time being, winning the challenge isn't instant. Instead, it simply makes the current responder the winner's opponent, @@ -174,7 +174,7 @@ there is time to diagnose and fix the error with a contract upgrade. -# One Step Proof Assumptions +## One Step Proof Assumptions The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise in a correct execution. This documents those assumptions about what's being executed. @@ -201,7 +201,7 @@ such a case were reached in correct execution, could lead to a security failure. The following assumptions, together, must prevent an unreachable case from arising in correct execution. -## The WAVM code is generated by Arbitrator from valid WASM +### The WAVM code is generated by Arbitrator from valid WASM WAVM is the name of the custom instruction set similar to WASM used for proving. Arbitrator transpiles WASM code into WAVM. @@ -212,7 +212,7 @@ mismatch types, or do any other number of invalid things which are prevented by WAVM code generated from by Arbitrator from valid WASM is assumed to never encounter an unreachable case. -## Inbox messages must not be too large +### Inbox messages must not be too large The current method of inbox hashing requires the full inbox message be available for proving. That message must not be too large as to prevent it from being supplied for proving, @@ -222,7 +222,7 @@ The current length limit is 117,964 bytes, which is 90% of the [max transaction size Geth will accept](https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/core/tx_pool.go#L53), leaving 13,108 bytes for other proving data. -## Requested preimages must be known and not too large +### Requested preimages must be known and not too large WAVM has an opcode which resolves the preimage of a Keccak-256 hash. This can only be executed if the preimage is already known to all nodes, @@ -233,7 +233,7 @@ The current length limit is 117,964 bytes for the reasons mentioned above. Here's a list of which preimages may be requested by Nitro, and why they're known to all parties, and not too large: -### Block headers +#### Block headers Nitro may request up to the last 256 L2 block headers. The last block header is required to determine the current state, @@ -241,7 +241,7 @@ and blocks before it are required to implement the `BLOCKHASH` evm instruction. This is safe as previous block headers are a fixed size, and are known to all nodes. -### State trie access +#### State trie access To resolve state, Nitro traverses the state trie by resolving preimages. @@ -254,14 +254,14 @@ which is limited by EIP-170 to about 24KB. -# WASM to WAVM +## WASM to WAVM Not all WASM instructions are 1:1 with WAVM opcodes. This document lists those which are not, and explains how they're expressed in WAVM. Many of the WAVM representations use opcodes not in WASM, which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). -## `block` and `loop` +### `block` and `loop` In WASM, a block contains instructions. Branch instructions exit a fixed number of blocks, jumping to their destination. @@ -271,7 +271,7 @@ In WAVM, instructions are flat. At transpilation time, any branch instructions are replaced with jumps to the corresponding block's destination. This means that WAVM interpreters don't need to track blocks, and thus block instructions are unnecessary. -## `if` and `else` +### `if` and `else` These are translated to a block with an `ArbitraryJumpIf` as follows: @@ -284,12 +284,12 @@ begin block with endpoint end end ``` -## `br` and `br_if` +### `br` and `br_if` `br` and `br_if` are translated into `ArbitraryJump` and `ArbitraryJumpIf` respectively. The jump locations can be known at transpilation time, making blocks obsolete. -## `br_table` +### `br_table` `br_table` is translated to a check for each possible branch in the table, and then if none of the checks hit, a branch of the default level. @@ -297,11 +297,11 @@ and then if none of the checks hit, a branch of the default level. Each of the non-default branches has a conditional jump to a section afterwards, containing a `drop` for the selector, and then a jump to the target branch. -## `local.tee` +### `local.tee` `local.tee` is translated to a WAVM `Dup` and then a `LocalSet`. -## `return` +### `return` To translate a return, the number of return values must be known from the function signature. A WAVM `MoveFromStackToInternal` is added for each return value. @@ -309,7 +309,7 @@ Then, a loop checks `IsStackBoundary` (which implicitly pops a value) until it's Next, a `MoveFromInternalToStack` is added for each return value to put the return values back on the stack. Finally, a WAVM `Return` is added, returning control flow to the caller. -## Floating point instructions +### Floating point instructions A floating point library module must be present to translate floating point instructions. They are translated by bitcasting `f32` and `f64` arguments to `i32`s and `i64`s, @@ -320,7 +320,7 @@ and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and ` -# WAVM Custom opcodes not in WASM +## WAVM Custom opcodes not in WASM In addition to the MVP WASM specification, WAVM implements the multi value and sign extension ops WASM proposals. @@ -328,14 +328,14 @@ WAVM implements the multi value and sign extension ops WASM proposals. WAVM also implements the following unique opcodes, which are not part of WASM nor any WASM proposal. -## Invariants +### Invariants Many of these opcodes have implicit invariants about what's on the stack, e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. If these conditions are not satisfied, execution is generally not possible. These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) -## Codegen internal +### Codegen internal These are generated when breaking down a WASM instruction that does many things into many WAVM instructions which each do one thing. For instance, a WASM `local.tee` is implemented in WAVM with `dup` and then `local.set`, the former of which doesn't exist in WASM. @@ -364,7 +364,7 @@ The above opcodes eliminate the need for the following WASM opcodes (which are t - br_table - local.tee -## Linking +### Linking This is only generated to link modules together. Each import is replaced with a local function consisting primarily of this opcode, @@ -374,7 +374,7 @@ which handles the actual work needed to change modules. | ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 0x8009 | CrossModuleCall | Pushes the current program counter, module number, and module's internals offset to the stack. Then splits its argument data into the lower 32 bits being a function index, and the upper 32 bits being a module index, and jumps to the beginning of that function. | -## Host calls +### Host calls These are only used in the implementation of "host calls". Each of these has an equivalent host call method, which can be invoked from libraries. @@ -399,7 +399,7 @@ For these instruction descriptions, all pointers and offsets are represented as -# WAVM Floating point implementation +## WAVM Floating point implementation Implementing correct, consistent, and deterministic floating point operations directly in WAVM (meaning both a Rust Arbitrator implementation and Solidity OSP implementation) @@ -426,7 +426,7 @@ and floating points may be used outside core state transition code for imprecise but the former is well exercised as used in Nitro, and the latter generally doesn't rely on details like the minimum of NaN and infinity. -## Known divergences from the WASM specification +### Known divergences from the WASM specification Floating point to integer truncation will saturate on overflow, instead of erroring. This is generally safer, because on x86, overflowing simply produces an undefined result. @@ -436,7 +436,7 @@ A WASM proposal exists to add new opcodes which are defined to saturate, but it' -# WAVM Modules +## WAVM Modules WASM natively has a notion of modules. Normally, in WASM, a module is the entire program. @@ -447,7 +447,7 @@ but [its linking scheme](https://github.com/WebAssembly/tool-conventions/blob/ma In WAVM this is extended to make the executing program composed of multiple modules. These may call each other, and library modules may write to their caller's memory to return results. -## The entrypoint module +### The entrypoint module The entrypoint module is where execution begins. It calls modules' `start` functions if specified, @@ -455,7 +455,7 @@ and then calls the main module's main function, which is language specific. For Go it sets argv to `["js"]` to match the JS environment, and calls `run`. For Rust it calls `main` with no arguments. -## Library exports +### Library exports Libraries may export functions with the name pattern `module__name`, which future libraries or the main module can import as `"module" "name"`. @@ -463,7 +463,7 @@ which future libraries or the main module can import as `"module" "name"`. For instance, this is used for wasi-stub to provide functions rust imports according to the WebAssembly System Interface. -## Floating point operations +### Floating point operations To provide floating point operations for future libraries, the soft float library exports functions which perform floating point ops. @@ -473,7 +473,7 @@ Their type signature is also the same, except all `f32`s and `f64`s are bitcaste Future modules can implicitly use these by using WASM floating point operations, which are replaced at the WASM->WAVM level with bitcasts and cross module calls to these functions. -## WAVM guest calls +### WAVM guest calls Libraries may call the main module's exports via `"env" "wavm_guest_call__*"`. @@ -481,7 +481,7 @@ For instance, go-stub calls Go's resume function when queueing async events via `wavm_guest_call_resume()`, and then retrieves the new stack pointer with `wavm_guest_call_getsp()`. -## Caller module internals call +### Caller module internals call Every stack frame retains its caller module and its caller module's "internals offset", which is the first internal function index. diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index 927d053b0..c1512f5db 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -2,84 +2,11 @@ author: dzgoldman --- -# Inside AnyTrust - -AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. - -The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. - -AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. - -## Keysets - -A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. - -A Keyset contains - -- the number of Committee members, and -- for each Committee member, a BLS public key, and -- the number of Committee signatures required. - -Keysets are identified by their hashes. - -An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. - -Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. - -## Data Availability Certificates - -A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: - -- the hash of a data block, and -- an expiration time, and -- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of - - the hash of the Keyset used in signing, and - - a bitmap saying which Committee members signed, and - - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. - -Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. - -In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. - -AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. - -The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that - -- the number of signers is at least the number required by the Keyset, and -- the aggregated signature is valid for the claimed signers, and -- the expiration time is at least two weeks after the current L2 timestamp. - -If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. - -## Data Availability Servers - -Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: - -- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. -- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. - -Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) - -The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. - -## Sequencer-Committee Interaction - -When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. - -Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. - -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. - - - - - - diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 16c9b8fc4..48f8a0623 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -2,7 +2,7 @@ author: dzgoldman --- -# Gas and Fees +## Gas and Fees There are two parties a user pays when submitting a tx: @@ -13,19 +13,19 @@ The L1 component is the product of the transaction's estimated contribution to i The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. -## Gas Price Floor +### Gas Price Floor The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). -## Estimating Gas +### Estimating Gas Calling an Arbitrum Node's `eth_estimateGas` RPC gives a value sufficient to cover the full transaction fee at the given L2 gas price; i.e., the value returned from `eth_estimateGas` multiplied by the L2 gas price tells you how much total Ether is required for the transaction to succeed. Note that this means that for a given operation, the value returned by `eth_estimateGas` will change over time (as the L1 calldata price fluctuates.) (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and [How to estimate gas in Arbitrum](/build-decentralized-apps/02-how-to-estimate-gas.mdx) for more.) -## Tips in L2 +### Tips in L2 The sequencer prioritizes transactions on a first-come first-served basis. Because tips do not make sense in this model, they are ignored. Arbitrum users always just pay the basefee regardless of the tip they choose. -## Gas Estimating Retryables +### Gas Estimating Retryables When a transaction schedules another, the subsequent transaction's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A transaction's gas estimate, then, can only be found if all the transactions succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. @@ -37,11 +37,11 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu -## Gas and Fees +### Gas and Fees NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. -### The Speed Limit +#### The Speed Limit The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. @@ -49,7 +49,7 @@ This sets an effective speed limit on execution of a Nitro chain: in the long ru Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. -### Fees +#### Fees User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. @@ -59,7 +59,7 @@ Fees are charged for two resources that a transaction can use: * _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, -#### L2 gas fees +##### L2 gas fees L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. @@ -71,7 +71,7 @@ Intuitively, if the backlog grows, the algorithm should increase the gas price, To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. -#### L1 calldata fees +##### L1 calldata fees L1 calldata fees exist because the Sequencer, or the batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. @@ -81,7 +81,7 @@ The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transactio The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. -#### Total fee and gas estimation +##### Total fee and gas estimation The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. @@ -89,11 +89,11 @@ The total fee charged to a transaction is the L2 basefee, multiplied by the sum -# L1 gas pricing +## L1 gas pricing ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amount collected in L1 gas fees is as close as possible to the costs that must be covered, over time. -## L1 fee collection +### L1 fee collection A transaction is charged for L1 gas if and only if it arrived as part of a sequencer batch. This means that someone would have paid for L1 gas to post the transaction on the L1 chain. @@ -108,7 +108,7 @@ The L1 pricer also records the total number of "data units" (the sum of the esti [l1pricerfundspool_link]: https://github.com/OffchainLabs/nitro/blob/3f4939df1990320310e7f39e8abb32d5c4d8045f/arbos/l1pricing/l1pricing.go#L46 [two_dimensional_fees_medium_article_link]: https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9 -## L1 costs +### L1 costs There are two types of L1 costs: batch posting costs, and rewards. @@ -120,7 +120,7 @@ The second type of L1 cost is an optional (per chain) per-unit reward for handli The L1 pricer keeps track of the funds due to the reward address, based on the number of data units handled so far. This amount is updated whenever a batch posting report arrives at L2. -## Allocating funds and paying what is owed +### Allocating funds and paying what is owed When a batch posting report is processed at L2, the pricer allocates some of the collected funds to pay for costs incurred. To allocate funds, the pricer considers three timestamps: @@ -132,7 +132,7 @@ The pricer computes an allocation fraction `F = (updateTime-lastUpdateTime) / (c Now the pricer pays out the allocated funds to cover the rewards due and the amounts due to batch posters, reducing the balance due to each party as a result. If the allocated funds aren't sufficient to cover everything that is due, some amount due will remain. If all of the amount due can be covered with the allocated funds, any remaining allocated funds are returned to the `L1PricerFundsPool`. -## Adjusting the L1 gas basefee +### Adjusting the L1 gas basefee After allocating funds and paying what is owed, the L1 Pricer adjusts the L1 Gas Basefee. The goal of this process is to find a value that will cause the amount collected to equal the amount owed over time. @@ -140,7 +140,7 @@ The algorithm first computes the surplus (funds in the `L1PricerFundsPool`, minu A second term is added to the L1 Gas Basefee, based on the derivative of the surplus (surplus at present, minus the surplus after the previous batch posting report was processed). This term, which is multiplied by a smoothing factor to reduce fluctuations, will reduce the Basefee if the surplus is increasing, and increase the Basefee if the surplus is shrinking. -## Getting L1 fee info +### Getting L1 fee info The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. diff --git a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx index a0d6afe7d..76c5079a0 100644 --- a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx +++ b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx @@ -3,7 +3,7 @@ title: Nitro vs. Classic author: dzgoldman --- -Why Nitro? +## Why Nitro? Nitro represents the latest step in the evolution of Arbitrum technology; it is an upgrade from the tech stack first released on the mainnet Arbitrum One chain, which we now refer to as “Arbitrum Classic” (and several steps beyond what was described in the [initial Arbitrum whitepaper back in 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf)). Here, we’ll explain the rationale behind the Nitro upgrade, and outline Nitro’s core benefits over the classic system. @@ -15,9 +15,9 @@ In Nitro, instead of using the AVM for low-level instructions, we use WebAssembl This architecture—in which Geth’s EVM implementation can be used directly—is Nitro’s defining feature, and is principally what we’re talking about when we talk about “Nitro.” Most of Nitro’s benefits are a direct or indirect consequence of this design choice. We can summarize these benefits as follows: lower fees, better Ethereum compatibility, and simplicity. -### Lower Fees +## Lower Fees -#### (Optimistic)^2 Execution +### (Optimistic)^2 Execution To understand the core of Nitro’s efficiency, we have to dig a little deeper into the classic AVM. In classic, high-level code (Solidity, Vyper, etc.) would be initially compiled down to the EVM bytecode (as though it were to be deployed on Ethereum). This bytecode would then be transpiled to its corresponding AVM instructions by ArbOS; this AVM bytecode would function both as the instructions for running the L2 VM, and the inputs used to prove fraud; in an interactive fraud proof, two validators dissect a segment of AVM bytecode until a “one step proof” — i.e., a state transition that represents a single AVM opcode — would be executed in the EVM of the L1 itself. @@ -25,7 +25,7 @@ Nitro has a similar bytecode-sandwich-like structure; [to prove fraud in Nitro]( It’s worth reiterating this distinction: in classic, the code executed in the happy/common case is equivalent to the code used in a fraud proof, whereas in Nitro, we can have different contexts for the two cases for execution and for proving. When a claim is being disputed, we ultimately compile down to Wasm bytecode, but in the happy/common case, we can execute the node’s Go code natively, i.e., in whatever execution environment one’s machine uses. Essentially, Nitro is capable of being even more “optimistic” in its execution, compiling to Wasm only just-in-time as required. The common case of native execution is happily far faster and more performant, and better node performance, of course, translates to lower fees for end users. -#### Calldata Compression +### Calldata Compression Typically, the bulk of an Arbitrum Rollup transaction’s fee is covering the cost to post its data on Ethereum. Fundamentally, any rollup must post data on L1 sufficient for reconstruction and validation of the L2 state; beyond that, L2s can be flexible in deciding on what data format to use. Given the relatively high cost of posting data to L1, a natural optimization is to (losslessly) compress data before posting it on L1, and have the L2 environment handle decompressing it. From 607bcb1888f7222d2bf1e2d290dfcb9521498769 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 09:56:33 -0600 Subject: [PATCH 14/75] yarn format cleanup --- .../how-arbitrum-works/10-l1-to-l2-messages.mdx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx index 019d1b1a8..d0bf9f28e 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx @@ -30,8 +30,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol - - - - ### Automatic Redemption 4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: @@ -83,7 +79,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). - - ### Manual Redemption 5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. @@ -130,7 +124,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 [renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 - - :::caution Avoid Losing Funds! If a ticket expires after 7 days without being redeemed or re-scheduled to a future date, any message and value (other than the escrowed `callvalue`) it carries could be lost without possibility of being recovered. @@ -224,6 +216,7 @@ L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111 :::tip Try it out + ::: The Arbitrum protocol's usage of L2 Aliases for L1-to-L2 messages prevents cross-chain exploits that would otherwise be possible if we simply reused the same L1 addresses as the L2 sender; i.e., tricking an L2 contract that expects a call from a given contract address by sending retryable ticket from the expected contract address on L1. From 7f48fe5b035059941d74c404d71dce4cd95a99b1 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 10:49:17 -0600 Subject: [PATCH 15/75] fixing broken links due to reorg --- arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index 8c8bf6252..7ade9b69b 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -148,7 +148,7 @@ Please note any links on this page may be referencing old releases of Nitro or o Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. -Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#EnableArbOS) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. +Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](`1EnableArbOS`) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. - `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` - `core.NewStateTransition` @@ -161,7 +161,7 @@ Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additiona - `evm.Call` - `core.vm.EVMInterpreter.Run` - [`PushCaller`](#PushCaller) - - [`PopCaller`](#PopCaller) + - `PopCaller` - `core.StateTransition.refundGas` - [`ForceRefundGas`](#ForceRefundGas) - [`NonrefundableGas`](#NonrefundableGas) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index ad31643b3..6f99d0215 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -241,7 +241,7 @@ Arbitrum full nodes normally "live at Layer 2" which means that they don’t wor Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. -Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. +Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. Offchain Labs provides open source validator software, including a pre-built Docker image. From 39852511cca512c24a701e3a2c3e5c04855b3e31 Mon Sep 17 00:00:00 2001 From: Pete Date: Wed, 4 Dec 2024 11:09:21 -0600 Subject: [PATCH 16/75] adding frontmatter, cleaning up headings --- .../01-a-gentle-introduction.mdx | 3 +-- .../02-transaction-lifecycle.mdx | 4 ++++ .../how-arbitrum-works/03-sequencer.mdx | 4 ++++ .../04-geth-at-the-core.mdx | 6 ++--- .../05-separating-execution-from-proving.mdx | 9 ++++--- .../06-optimistic-rollup.mdx | 5 +++- .../07-interactive-fraud-proofs.mdx | 24 +++++++++++-------- .../08-anytrust-protocol.mdx | 11 ++++----- .../how-arbitrum-works/09-gas-fees.mdx | 22 ++++++++--------- ...messages.mdx => 10-l1-to-l2-messaging.mdx} | 5 ++++ ...messages.mdx => 11-l2-to-l1-messaging.mdx} | 4 ++++ website/sidebars.js | 8 +++---- 12 files changed, 65 insertions(+), 40 deletions(-) rename arbitrum-docs/how-arbitrum-works/{10-l1-to-l2-messages.mdx => 10-l1-to-l2-messaging.mdx} (99%) rename arbitrum-docs/how-arbitrum-works/{11-l2-to-l1-messages.mdx => 11-l2-to-l1-messaging.mdx} (99%) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 463aba3a5..64984d712 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -1,6 +1,5 @@ --- -title: 'Inside Arbitrum Nitro' -sidebar_label: 'Deep dive: Inside Arbitrum' +title: 'A gentle introduction' description: 'Learn the fundamentals of Nitro, Arbitrum stack.' author: dzgoldman sme: dzgoldman diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index d2602905f..99508a4d9 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -1,3 +1,7 @@ +--- +title: Transaction Lifecycle +--- + ## Sequencing, Followed by Deterministic Execution This diagram summarizes how transactions are processed in Nitro. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 8b07e1141..8869381e7 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -1,3 +1,7 @@ +--- +title: Sequencer +--- + ## The Sequencer and Censorship Resistance The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index 7ade9b69b..d5e6823a8 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -1,6 +1,6 @@ - - -## Geth at the Core +--- +title: Geth at the Core +--- The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 04e85021c..0de531b20 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -1,4 +1,7 @@ -## Separating Execution from Proving +--- +title: Separating Execution from Proving +--- + One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. @@ -6,7 +9,7 @@ When compiling the Nitro node software for _execution_, the ordinary Go compiler Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. -#### WAVM +## WAVM The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. @@ -16,7 +19,7 @@ Second, WAVM restricts a few features of wasm. For example, WAVM does not contai Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. -#### ReadPreImage and the Hash Oracle Trick +## ReadPreImage and the Hash Oracle Trick The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 6f99d0215..16a3deda9 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -1,7 +1,10 @@ +--- +title: Optimistic Rollup +--- + -## Optimistic Rollup Arbitrum is an optimistic rollup. Let’s unpack that term. diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index d477a306f..6887a4219 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -1,6 +1,10 @@ +--- +title: Interactive Fraud Proofs +--- + -## Fraud Proofs: Challenges + Suppose the rollup chain looks like this: @@ -18,7 +22,7 @@ The game will operate in two phases: dissection, followed by one-step proof. Dis We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. -### Dissection Protocol: Simplified Version +## Dissection Protocol: Simplified Version Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. @@ -28,7 +32,7 @@ Now Alice has effectively bisected her N-step assertion into two (N/2)-step asse At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. -### Why Dissection Correctly Identifies a Cheater +## Why Dissection Correctly Identifies a Cheater Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. @@ -38,7 +42,7 @@ To prove (2), observe that if Alice’s initial claim is incorrect, this can onl (If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) -### The Real Dissection Protocol +## The Real Dissection Protocol The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. @@ -54,19 +58,19 @@ The real dissection protocol is conceptually similar to the simplified one descr It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. -### Efficiency +## Efficiency The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. -### Instant confirmation +## Instant confirmation Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. -### Inboxes, fast and slow +## Inboxes, fast and slow When we add a Sequencer, the operation of the inbox changes. @@ -75,7 +79,7 @@ When we add a Sequencer, the operation of the inbox changes. - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) -### If the Sequencer is well-behaved... +## If the Sequencer is well-behaved... A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. @@ -87,7 +91,7 @@ This is the basic tradeoff of having a Sequencer: if your message uses the Seque So a Sequencer is generally a win, if the Sequencer is well behaved. -### If the Sequencer is malicious... +## If the Sequencer is malicious... A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. @@ -95,7 +99,7 @@ On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/stat Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. -### Decentralized fair sequencing +## Decentralized fair sequencing Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index c1512f5db..cb232998d 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -1,16 +1,15 @@ --- +title: AnyTrust Protocol author: dzgoldman --- -## Inside AnyTrust - AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. -### Keysets +## Keysets A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. @@ -26,7 +25,7 @@ An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. -### Data Availability Certificates +## Data Availability Certificates A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: @@ -51,7 +50,7 @@ The L2 code that reads data from the inbox reads a full-data block as in ordinar If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. -### Data Availability Servers +## Data Availability Servers Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: @@ -62,7 +61,7 @@ Only Committee members have reason to support the Sequencer API. We expect other The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. -### Sequencer-Committee Interaction +## Sequencer-Committee Interaction When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 48f8a0623..e301ec47f 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -1,8 +1,8 @@ --- +title: Gas and Fees author: dzgoldman --- -## Gas and Fees There are two parties a user pays when submitting a tx: @@ -13,19 +13,19 @@ The L1 component is the product of the transaction's estimated contribution to i The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. -### Gas Price Floor +## Gas Price Floor The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). -### Estimating Gas +## Estimating Gas Calling an Arbitrum Node's `eth_estimateGas` RPC gives a value sufficient to cover the full transaction fee at the given L2 gas price; i.e., the value returned from `eth_estimateGas` multiplied by the L2 gas price tells you how much total Ether is required for the transaction to succeed. Note that this means that for a given operation, the value returned by `eth_estimateGas` will change over time (as the L1 calldata price fluctuates.) (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and [How to estimate gas in Arbitrum](/build-decentralized-apps/02-how-to-estimate-gas.mdx) for more.) -### Tips in L2 +## Tips in L2 The sequencer prioritizes transactions on a first-come first-served basis. Because tips do not make sense in this model, they are ignored. Arbitrum users always just pay the basefee regardless of the tip they choose. -### Gas Estimating Retryables +## Gas Estimating Retryables When a transaction schedules another, the subsequent transaction's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A transaction's gas estimate, then, can only be found if all the transactions succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. @@ -37,11 +37,11 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu -### Gas and Fees +## Gas and Fees NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. -#### The Speed Limit +### The Speed Limit The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. @@ -49,7 +49,7 @@ This sets an effective speed limit on execution of a Nitro chain: in the long ru Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. -#### Fees +### Fees User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. @@ -59,7 +59,7 @@ Fees are charged for two resources that a transaction can use: * _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, -##### L2 gas fees +#### L2 gas fees L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. @@ -71,7 +71,7 @@ Intuitively, if the backlog grows, the algorithm should increase the gas price, To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. -##### L1 calldata fees +#### L1 calldata fees L1 calldata fees exist because the Sequencer, or the batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. @@ -81,7 +81,7 @@ The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transactio The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. -##### Total fee and gas estimation +#### Total fee and gas estimation The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx similarity index 99% rename from arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx rename to arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index d0bf9f28e..ae2db04b6 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messages.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -1,3 +1,8 @@ +--- +title: L1 to L2 Messaging +author: dzgoldman +--- + ## Retryable Tickets diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messages.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx similarity index 99% rename from arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messages.mdx rename to arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index f7ca047f6..5e1237973 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messages.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -1,3 +1,7 @@ +--- +title: L1 to L2 Messaging +--- + Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. diff --git a/website/sidebars.js b/website/sidebars.js index 6df6114b7..4257b0702 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -860,13 +860,13 @@ const sidebars = { }, { type: 'doc', - id: 'how-arbitrum-works/l1-to-l2-messages', - label: 'L1 to L2 messages', + id: 'how-arbitrum-works/l1-to-l2-messaging', + label: 'L1 to L2 messaging', }, { type: 'doc', - id: 'how-arbitrum-works/l2-to-l1-messages', - label: 'L2 to L1 messages', + id: 'how-arbitrum-works/l2-to-l1-messaging', + label: 'L2 to L1 messaging', }, { type: 'doc', From c706d8742d1e410be24c1379cf9fe47b3a30a439 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 5 Dec 2024 12:42:25 -0600 Subject: [PATCH 17/75] edited nitro v classic to make it more assertive --- .../12-nitro-vs-classic.mdx | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx index 76c5079a0..d6ddd7c3c 100644 --- a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx +++ b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx @@ -4,49 +4,56 @@ author: dzgoldman --- ## Why Nitro? +Nitro marks a significant leap forward in Arbitrum technology. This upgrade surpasses the tech stack initially launched on the Arbitrum One mainnet, now known as "Arbitrum Classic." It has evolved far beyond the original [Arbitrum whitepaper from 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf). This overview will assertively detail the rationale behind the Nitro upgrade and clearly outline its key advantages over the classic system. -Nitro represents the latest step in the evolution of Arbitrum technology; it is an upgrade from the tech stack first released on the mainnet Arbitrum One chain, which we now refer to as “Arbitrum Classic” (and several steps beyond what was described in the [initial Arbitrum whitepaper back in 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf)). Here, we’ll explain the rationale behind the Nitro upgrade, and outline Nitro’s core benefits over the classic system. +The Classic and Nitro systems designs are similar: to establish an execution environment that closely mirrors the Ethereum Virtual Machine (EVM), operating as a robust second layer to Ethereum. This architecture guarantees the safety of Layer 2 (L2) virtual machine state updates, enforced through succinct fraud proofs on Ethereum itself. -Viewed from a distance, the Classic and Nitro systems do similar things: both seek to create an execution environment as close to the EVM as possible which operates as a second layer to Ethereum; i.e., safety of the L2 virtual machine’s state updates can be guaranteed and enforced via succinct fraud proofs on Ethereum itself. +In Arbitrum Classic, we achieved this goal using a custom-built virtual machine known as the Arbitrum Virtual Machine (AVM). The L2 state machine implementation, called "[ArbOS](/how-arbitrum-works/arbos/introduction.mdx)," is a program compiled and uploaded to the AVM, allowing it to emulate EVM execution and various other functionalities. -In Arbitrum Classic, this was achieved via a custom-made virtual machine, which we call the Arbitrum Virtual Machine (AVM). The implementation of Arbitrum’s L2 state machine—known as [“ArbOS”](/how-arbitrum-works/arbos/introduction.mdx) — is effectively a program that is compiled and uploaded to the AVM; ArbOS includes (among other things) the ability to emulate EVM execution. +With Nitro, we shift from relying on the AVM for low-level instructions to employing WebAssembly (Wasm). This shift allows us to compile Go code into Wasm, enabling the ArbOS program to be implemented in Go while incorporating [Geth](/how-arbitrum-works/arbos/geth.mdx) itself (as a sub-module). Geth is Ethereum's most widely employed implementation, underscoring our commitment to compatibility and efficiency. -In Nitro, instead of using the AVM for low-level instructions, we use WebAssembly (Wasm). Since Go code can be compiled down to Wasm, we can implement the ArbOS program in Go, and include within it (as a sub-module) [Geth itself](/how-arbitrum-works/arbos/geth.mdx), the most widely used Ethereum implementation. - -This architecture—in which Geth’s EVM implementation can be used directly—is Nitro’s defining feature, and is principally what we’re talking about when we talk about “Nitro.” Most of Nitro’s benefits are a direct or indirect consequence of this design choice. We can summarize these benefits as follows: lower fees, better Ethereum compatibility, and simplicity. +Nitro's core strength lies in its innovative architecture, seamlessly integrating Geth's EVM implementation. Most of Nitro's benefits arise directly or indirectly from this design choice, offering lower fees, enhanced compatibility with Ethereum, and simplicity. ## Lower Fees ### (Optimistic)^2 Execution +In Classic, high-level code—such as Solidity and Vyper—is compiled into EVM bytecode as if it were deployable on Ethereum. This bytecode is then transpiled by ArbOS into its corresponding AVM instructions. The resulting AVM bytecode serves as the instructions for operating the Layer 2 (L2) virtual machine and provides the necessary inputs for proving fraud. In the event of an interactive fraud proof, two validators dive deep into a segment of AVM bytecode until they establish a "one-step proof"—a state transition that showcases a single AVM opcode executed within the EVM of Layer 1 (L1). + +Nitro adopts a similar bytecode "sandwich" structure. To tackle [fraud proofing in Nitro](how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node's Go code is compiled into WebAssembly (Wasm), with individual instructions meticulously analyzed to flush out any invalid state updates. However, a crucial advantage of Nitro is its ability to periodically produce blocks akin to those in Ethereum, acting as natural state checkpoints within the broader context of an L2 state update. Nitro harnesses this strength by decisively splitting the interactive fraud proof process into two phases: -To understand the core of Nitro’s efficiency, we have to dig a little deeper into the classic AVM. In classic, high-level code (Solidity, Vyper, etc.) would be initially compiled down to the EVM bytecode (as though it were to be deployed on Ethereum). This bytecode would then be transpiled to its corresponding AVM instructions by ArbOS; this AVM bytecode would function both as the instructions for running the L2 VM, and the inputs used to prove fraud; in an interactive fraud proof, two validators dissect a segment of AVM bytecode until a “one step proof” — i.e., a state transition that represents a single AVM opcode — would be executed in the EVM of the L1 itself. +- Phase 1: The disputing parties sharpen their focus to resolve their disagreement to a single block. +- Phase 2: They compile that block into Wasm, further drilling down to Wasm instructions. -Nitro has a similar bytecode-sandwich-like structure; [to prove fraud in Nitro](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node’s Go code is compiled into WebAssembly (Wasm), the individual instructions of which are ultimately similarly dissected over to zero-in on an invalid state update. There is, however, a crucial difference: Nitro, being essentially the EVM, periodically produces Ethereum-esque blocks; we can think of these blocks as natural state-checkpoints within a larger assertion of an L2 state update. Nitro takes advantage of this by splitting the interactive fraud proof game into 2 phases: first, two disputing parties narrow down their disagreement to a single block; then (and only then) do they compile the block to Wasm, and thereby continue to narrow down their dispute to Wasm instruction. Thus, this Wasm compilation step only needs to happen when a dispute occurs. +Notably, the Wasm compilation occurs only when disputes arise, maintaining efficiency. -It’s worth reiterating this distinction: in classic, the code executed in the happy/common case is equivalent to the code used in a fraud proof, whereas in Nitro, we can have different contexts for the two cases for execution and for proving. When a claim is being disputed, we ultimately compile down to Wasm bytecode, but in the happy/common case, we can execute the node’s Go code natively, i.e., in whatever execution environment one’s machine uses. Essentially, Nitro is capable of being even more “optimistic” in its execution, compiling to Wasm only just-in-time as required. The common case of native execution is happily far faster and more performant, and better node performance, of course, translates to lower fees for end users. +It's worth emphasizing this distinction: in Classic, the code executed in the happy/common case is identical to the code in the fraud proof. In stark contrast, Nitro allows for distinctly different contexts for execution and proving. When a dispute emerges, we compile down to Wasm bytecode; however, during standard operations, the node's Go code runs natively, leveraging whatever execution environment is on the machine. Nitro confidently adopts a more "optimistic" approach to execution, compiling to Wasm only as necessary. This native execution method guarantees superior speed and efficiency, leading to enhanced node performance that translates directly into lower fees for end users. ### Calldata Compression -Typically, the bulk of an Arbitrum Rollup transaction’s fee is covering the cost to post its data on Ethereum. Fundamentally, any rollup must post data on L1 sufficient for reconstruction and validation of the L2 state; beyond that, L2s can be flexible in deciding on what data format to use. Given the relatively high cost of posting data to L1, a natural optimization is to (losslessly) compress data before posting it on L1, and have the L2 environment handle decompressing it. +Most Arbitrum Rollup transactions' fees cover the essential cost of posting data on Ethereum. Any rollup must post sufficient data on Layer 1 (L1) to effectively reconstruct and validate the Layer 2 (L2) state. While L2s can choose their data formats, the high costs associated with posting data to L1 make it critical to implement optimizations. A highly effective strategy is to compress data (losslessly) before posting it on L1, allowing the L2 environment to manage decompression. -The flexibility that Arbitrum core architecture offers meant that even in the classic AVM, such decompression could have been implemented in principle. However, given that the AVM was custom-built for Arbitrum, this would have meant building a custom, hand-rolled implementation of a compression algorithm, which, practically speaking, represented a prohibitively high technical risk. +The Arbitrum core architecture's flexibility makes decompression possible, even with the Classic Arbitrum Virtual Machine (AVM). However, because the AVM is custom-built for Arbitrum, developing a proprietary implementation of a compression algorithm would have introduced excessive technical risks. -The Nitro architecture, however, fundamentally requires only that its VM can be compiled down to Wasm; so not just Geth, but any Go code can be incorporated. Thus, Nitro can (and does) use widely used, battle-tested compression libraries for calldata compression, and thus significantly reduces the cost of posting transaction batches. +In contrast, the Nitro architecture only requires its Virtual Machine (VM) to compile to WebAssembly (Wasm), meaning Ge th and any Go code are easily incorporated. This innovative approach allows Nitro to leverage widely adopted and tested compression libraries for calldata compression, drastically reducing the costs of posting transaction batches. -Note that supporting calldata compression also requires a more sophisticated mechanism for [determining the price of calldata](/how-arbitrum-works/l1-gas-pricing.mdx) and ensuring that batch posters are ultimately properly compensated, a mechanism which Nitro also introduces. +Additionally, supporting calldata compression demands a sophisticated mechanism to accurately determine the [price of calldata](/how-arbitrum-works/l1-gas-pricing.mdx) and ensure that batch posters receive appropriate compensation. Nitro effectively addresses these challenges, solidifying its framework for efficient operation. ## Closer EVM Compatibility -The classic AVM achieved a strong degree of EVM compatibility with its ability to handle any EVM opcodes. However, being a distinct VM, the AVM’s internal behavior in some ways diverged with that of the EVM. Most noticeable for smart contract developers was the denomination of “ArbGas”, whose units didn’t correspond to Ethereum L1 gas; e.g., a simple transfer takes 21,000 gas on L1 but over 100,000 ArbGas in the AVM. This meant that contracts that included gas calculation logic that were initially built for L1 had to be modified accordingly to be deployed on L2, and likewise with any client-side tooling with similar hardcoded expectations about a chain’s gas. With Nitro, [gas](/how-arbitrum-works/gas-fees.mdx) on L1 and L2 essentially correspond 1:1. +The Classic AVM demonstrated impressive EVM compatibility by effectively handling all EVM opcodes. However, its internal behavior diverged significantly from that of the EVM. A key concern for smart contract developers was the introduction of "ArbGas," a denomination whose units do not align with Ethereum L1 gas. For instance, a simple transfer requires 21,000 gas on L1 but a staggering 100,000 ArbGas within the AVM. This discrepancy necessitated modifications to contracts that relied on gas calculation logic designed for L1 before deployment on L2. It also affected client-side tools that operated under similar fixed assumptions about gas on a blockchain. Nitro resolves this issue with [gas](/how-arbitrum-works/gas-fees.mdx) costs on L1 and L2 corresponding directly in a 1:1 ratio. + +:::note + +Transactions must account for the total expenses of L2 execution and L1 calldata. The values returned by Arbitrum nodes' `eth_estimateGas` RPC, which users see in their wallets, are designed to ensure they effectively cover this entire cost. Refer to the [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) for more details. -(Note that transactions have to cover the total cost of both L2 execution and L1 calldata; the value returned by Arbitrum nodes' `eth_estimateGas` RPC — and in turn, the value users will see in their wallets — is calculated to be sufficient to cover this total cost. See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) for more.) +::: -Additionally, node functionality peripheral to execution itself, but still important / expected by much tooling and infrastructure — e.g. support for transaction tracing — is essentially inherited out-of-the-box in Nitro, giving Nitro stronger compatibility with Ethereum not just within its virtual machine, but also with how clients interact with it. +Furthermore, Nitro enhances node functionality beyond execution, ensuring compatibility with many tools and infrastructure expectations—such as transaction tracing—available out of the box. This feature makes Nitro more compatible with Ethereum at the virtual machine level and how clients interact with the blockchain. -In short, there’s no better way to achieve Ethereum compatibility than to reuse the Ethereum software itself. +In summary, leveraging Ethereum's software is undeniably the best approach to achieving compatibility with Ethereum. ## Simplicity -Having code that is as simple and easy to reason about as possible is important for L2 systems, which are inevitably complex. The classic stack represents a large codebase built in-house, which requires a fair amount of time and overhead to understand. The AVM together with ArbOS effectively constitute a full blockchain protocol built from the ground up. Since the AVM was custom-built, with no high-level languages yet created for it, the ArbOS logic had to be implemented in what was essentially a custom language — called “mini” — along with a mini-to-AVM compiler. +Maintaining code that is simple and easy to understand for Layer 2 systems is essential, given their inherent complexity. The classic stack consists of a large, in-house codebase that demands considerable time and effort to analyze effectively. In stark contrast, the Arbitrum Virtual Machine (AVM) and ArbOS combined constitute a robust blockchain protocol built from the ground up. The AVM was custom-built and lacked high-level languages at that time, forcing the implementation of ArbOS logic using a custom language called "mini," along with a mini-to-AVM compiler. -Nitro’s direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini), is much slimmer than in the classic stack; since the work of emulating the EVM is now handled by the Geth software, ArbOS needs only to implement the things specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours that have been put into an Ethereum-Geth itself — makes it a system that’s far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. +Nitro's direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini) is much slimmer than in the classic stack; since the Geth software now handles the work of emulating the EVM, ArbOS needs only to implement the things-specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours put into Ethereum-Geth itself — makes it a system far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. From b5d2bdb24a6316a2624690c3dcdb5d460999d5a6 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 5 Dec 2024 13:22:30 -0600 Subject: [PATCH 18/75] minor adjustments --- .../how-arbitrum-works/12-nitro-vs-classic.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx index d6ddd7c3c..6849ffeaa 100644 --- a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx +++ b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx @@ -6,7 +6,7 @@ author: dzgoldman ## Why Nitro? Nitro marks a significant leap forward in Arbitrum technology. This upgrade surpasses the tech stack initially launched on the Arbitrum One mainnet, now known as "Arbitrum Classic." It has evolved far beyond the original [Arbitrum whitepaper from 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf). This overview will assertively detail the rationale behind the Nitro upgrade and clearly outline its key advantages over the classic system. -The Classic and Nitro systems designs are similar: to establish an execution environment that closely mirrors the Ethereum Virtual Machine (EVM), operating as a robust second layer to Ethereum. This architecture guarantees the safety of Layer 2 (L2) virtual machine state updates, enforced through succinct fraud proofs on Ethereum itself. +The Classic and Nitro systems designs are similar establishing an execution environment that closely mirrors the Ethereum Virtual Machine (EVM), operating as a robust second layer to Ethereum. This architecture guarantees the safety of Layer 2 (L2) virtual machine state updates, enforced through succinct fraud proofs on Ethereum itself. In Arbitrum Classic, we achieved this goal using a custom-built virtual machine known as the Arbitrum Virtual Machine (AVM). The L2 state machine implementation, called "[ArbOS](/how-arbitrum-works/arbos/introduction.mdx)," is a program compiled and uploaded to the AVM, allowing it to emulate EVM execution and various other functionalities. @@ -17,30 +17,30 @@ Nitro's core strength lies in its innovative architecture, seamlessly integratin ## Lower Fees ### (Optimistic)^2 Execution -In Classic, high-level code—such as Solidity and Vyper—is compiled into EVM bytecode as if it were deployable on Ethereum. This bytecode is then transpiled by ArbOS into its corresponding AVM instructions. The resulting AVM bytecode serves as the instructions for operating the Layer 2 (L2) virtual machine and provides the necessary inputs for proving fraud. In the event of an interactive fraud proof, two validators dive deep into a segment of AVM bytecode until they establish a "one-step proof"—a state transition that showcases a single AVM opcode executed within the EVM of Layer 1 (L1). +In Classic, high-level code—such as Solidity and Vyper—is compiled into EVM bytecode as if it were deployable on Ethereum. This bytecode is then transpiled by ArbOS into its corresponding AVM instructions. The resulting AVM bytecode serves as the instructions for operating the L2 virtual machine and provides the necessary inputs for proving fraud. In the event of an interactive fraud proof, two validators dive deep into a segment of AVM bytecode until they establish a "one-step proof"—a state transition that showcases a single AVM opcode executed within the EVM of Layer 1 (L1). -Nitro adopts a similar bytecode "sandwich" structure. To tackle [fraud proofing in Nitro](how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node's Go code is compiled into WebAssembly (Wasm), with individual instructions meticulously analyzed to flush out any invalid state updates. However, a crucial advantage of Nitro is its ability to periodically produce blocks akin to those in Ethereum, acting as natural state checkpoints within the broader context of an L2 state update. Nitro harnesses this strength by decisively splitting the interactive fraud proof process into two phases: +Nitro adopts a similar bytecode "sandwich" structure. To tackle [fraud proofing in Nitro](how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node's Go code is compiled into Wasm, with individual instructions meticulously analyzed to flush out any invalid state updates. However, a crucial advantage of Nitro is its ability to periodically produce blocks akin to those in Ethereum, acting as natural state checkpoints within the broader context of an L2 state update. Nitro harnesses this strength by decisively splitting the interactive fraud proof process into two phases: - Phase 1: The disputing parties sharpen their focus to resolve their disagreement to a single block. - Phase 2: They compile that block into Wasm, further drilling down to Wasm instructions. Notably, the Wasm compilation occurs only when disputes arise, maintaining efficiency. -It's worth emphasizing this distinction: in Classic, the code executed in the happy/common case is identical to the code in the fraud proof. In stark contrast, Nitro allows for distinctly different contexts for execution and proving. When a dispute emerges, we compile down to Wasm bytecode; however, during standard operations, the node's Go code runs natively, leveraging whatever execution environment is on the machine. Nitro confidently adopts a more "optimistic" approach to execution, compiling to Wasm only as necessary. This native execution method guarantees superior speed and efficiency, leading to enhanced node performance that translates directly into lower fees for end users. +It's worth emphasizing this distinction: in Classic, the code executed in the happy/common case is identical to the code in the fraud proof. In contrast, Nitro allows for distinctly different contexts for execution and proving. When a dispute emerges, we compile down to Wasm bytecode; however, during standard operations, the node's Go code runs natively, leveraging whatever execution environment is on the machine. Nitro confidently adopts a more "optimistic" approach to execution, compiling to Wasm only as necessary. This native execution method guarantees superior speed and efficiency, leading to enhanced node performance that translates directly into lower fees for end users. ### Calldata Compression -Most Arbitrum Rollup transactions' fees cover the essential cost of posting data on Ethereum. Any rollup must post sufficient data on Layer 1 (L1) to effectively reconstruct and validate the Layer 2 (L2) state. While L2s can choose their data formats, the high costs associated with posting data to L1 make it critical to implement optimizations. A highly effective strategy is to compress data (losslessly) before posting it on L1, allowing the L2 environment to manage decompression. +Most Arbitrum Rollup transactions' fees cover the essential cost of posting data on Ethereum. Any rollup must post sufficient data on L1 to effectively reconstruct and validate the L2 state. While L2s can choose their data formats, the high costs associated with posting data to L1 make it critical to implement optimizations. A highly effective strategy is to compress data (losslessly) before posting it on L1, allowing the L2 environment to manage decompression. -The Arbitrum core architecture's flexibility makes decompression possible, even with the Classic Arbitrum Virtual Machine (AVM). However, because the AVM is custom-built for Arbitrum, developing a proprietary implementation of a compression algorithm would have introduced excessive technical risks. +Arbitrum core architecture's flexibility makes decompression possible, even with the Classic Arbitrum Virtual Machine (AVM). However, because the AVM is custom-built for Arbitrum, developing a proprietary implementation of a compression algorithm would have introduced excessive technical risks. -In contrast, the Nitro architecture only requires its Virtual Machine (VM) to compile to WebAssembly (Wasm), meaning Ge th and any Go code are easily incorporated. This innovative approach allows Nitro to leverage widely adopted and tested compression libraries for calldata compression, drastically reducing the costs of posting transaction batches. +In contrast, the Nitro architecture only requires its Virtual Machine (VM) to compile to Wasm, meaning Geth and any Go code are easily incorporated. This innovative approach allows Nitro to leverage widely adopted and tested compression libraries for calldata compression, drastically reducing the costs of posting transaction batches. Additionally, supporting calldata compression demands a sophisticated mechanism to accurately determine the [price of calldata](/how-arbitrum-works/l1-gas-pricing.mdx) and ensure that batch posters receive appropriate compensation. Nitro effectively addresses these challenges, solidifying its framework for efficient operation. ## Closer EVM Compatibility -The Classic AVM demonstrated impressive EVM compatibility by effectively handling all EVM opcodes. However, its internal behavior diverged significantly from that of the EVM. A key concern for smart contract developers was the introduction of "ArbGas," a denomination whose units do not align with Ethereum L1 gas. For instance, a simple transfer requires 21,000 gas on L1 but a staggering 100,000 ArbGas within the AVM. This discrepancy necessitated modifications to contracts that relied on gas calculation logic designed for L1 before deployment on L2. It also affected client-side tools that operated under similar fixed assumptions about gas on a blockchain. Nitro resolves this issue with [gas](/how-arbitrum-works/gas-fees.mdx) costs on L1 and L2 corresponding directly in a 1:1 ratio. +The Classic AVM demonstrated impressive EVM compatibility by effectively handling all EVM opcodes. However, its internal behavior diverged significantly from that of the EVM. A key concern for smart contract developers was the introduction of "ArbGas," a denomination whose units do not align with Ethereum L1 gas. For instance, a simple transfer requires 21,000 gas on L1 but 100,000 ArbGas within the AVM. This discrepancy necessitated modifications to contracts that relied on gas calculation logic designed for L1 before deployment on L2. It also affected client-side tools that operated under similar fixed assumptions about gas on a blockchain. Nitro resolves this issue with [gas](/how-arbitrum-works/gas-fees.mdx) costs on L1 and L2 corresponding directly in a 1:1 ratio. :::note @@ -54,6 +54,6 @@ In summary, leveraging Ethereum's software is undeniably the best approach to ac ## Simplicity -Maintaining code that is simple and easy to understand for Layer 2 systems is essential, given their inherent complexity. The classic stack consists of a large, in-house codebase that demands considerable time and effort to analyze effectively. In stark contrast, the Arbitrum Virtual Machine (AVM) and ArbOS combined constitute a robust blockchain protocol built from the ground up. The AVM was custom-built and lacked high-level languages at that time, forcing the implementation of ArbOS logic using a custom language called "mini," along with a mini-to-AVM compiler. +Maintaining code that is simple and easy to understand for Layer 2 systems is essential, given their inherent complexity. The classic stack consists of a large, in-house codebase that demands considerable time and effort to analyze effectively. In contrast, the Arbitrum Virtual Machine (AVM) and ArbOS combined constitute a robust blockchain protocol built from the ground up. The AVM was custom-built and lacked high-level languages at that time, forcing the implementation of ArbOS logic using a custom language called "mini," along with a mini-to-AVM compiler. Nitro's direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini) is much slimmer than in the classic stack; since the Geth software now handles the work of emulating the EVM, ArbOS needs only to implement the things-specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours put into Ethereum-Geth itself — makes it a system far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. From 0d49ca012e03c08bfa2a132b49e793cf685fa2a5 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 10 Dec 2024 08:45:16 -0600 Subject: [PATCH 19/75] archiving nitro v classic/why nitro --- .../12-nitro-vs-classic.mdx | 59 ------------------- website/archive/12-nitro-vs-classic.mdx | 47 +++++++++++++++ 2 files changed, 47 insertions(+), 59 deletions(-) delete mode 100644 arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx create mode 100644 website/archive/12-nitro-vs-classic.mdx diff --git a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx b/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx deleted file mode 100644 index 6849ffeaa..000000000 --- a/arbitrum-docs/how-arbitrum-works/12-nitro-vs-classic.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Nitro vs. Classic -author: dzgoldman ---- - -## Why Nitro? -Nitro marks a significant leap forward in Arbitrum technology. This upgrade surpasses the tech stack initially launched on the Arbitrum One mainnet, now known as "Arbitrum Classic." It has evolved far beyond the original [Arbitrum whitepaper from 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf). This overview will assertively detail the rationale behind the Nitro upgrade and clearly outline its key advantages over the classic system. - -The Classic and Nitro systems designs are similar establishing an execution environment that closely mirrors the Ethereum Virtual Machine (EVM), operating as a robust second layer to Ethereum. This architecture guarantees the safety of Layer 2 (L2) virtual machine state updates, enforced through succinct fraud proofs on Ethereum itself. - -In Arbitrum Classic, we achieved this goal using a custom-built virtual machine known as the Arbitrum Virtual Machine (AVM). The L2 state machine implementation, called "[ArbOS](/how-arbitrum-works/arbos/introduction.mdx)," is a program compiled and uploaded to the AVM, allowing it to emulate EVM execution and various other functionalities. - -With Nitro, we shift from relying on the AVM for low-level instructions to employing WebAssembly (Wasm). This shift allows us to compile Go code into Wasm, enabling the ArbOS program to be implemented in Go while incorporating [Geth](/how-arbitrum-works/arbos/geth.mdx) itself (as a sub-module). Geth is Ethereum's most widely employed implementation, underscoring our commitment to compatibility and efficiency. - -Nitro's core strength lies in its innovative architecture, seamlessly integrating Geth's EVM implementation. Most of Nitro's benefits arise directly or indirectly from this design choice, offering lower fees, enhanced compatibility with Ethereum, and simplicity. - -## Lower Fees - -### (Optimistic)^2 Execution -In Classic, high-level code—such as Solidity and Vyper—is compiled into EVM bytecode as if it were deployable on Ethereum. This bytecode is then transpiled by ArbOS into its corresponding AVM instructions. The resulting AVM bytecode serves as the instructions for operating the L2 virtual machine and provides the necessary inputs for proving fraud. In the event of an interactive fraud proof, two validators dive deep into a segment of AVM bytecode until they establish a "one-step proof"—a state transition that showcases a single AVM opcode executed within the EVM of Layer 1 (L1). - -Nitro adopts a similar bytecode "sandwich" structure. To tackle [fraud proofing in Nitro](how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node's Go code is compiled into Wasm, with individual instructions meticulously analyzed to flush out any invalid state updates. However, a crucial advantage of Nitro is its ability to periodically produce blocks akin to those in Ethereum, acting as natural state checkpoints within the broader context of an L2 state update. Nitro harnesses this strength by decisively splitting the interactive fraud proof process into two phases: - -- Phase 1: The disputing parties sharpen their focus to resolve their disagreement to a single block. -- Phase 2: They compile that block into Wasm, further drilling down to Wasm instructions. - -Notably, the Wasm compilation occurs only when disputes arise, maintaining efficiency. - -It's worth emphasizing this distinction: in Classic, the code executed in the happy/common case is identical to the code in the fraud proof. In contrast, Nitro allows for distinctly different contexts for execution and proving. When a dispute emerges, we compile down to Wasm bytecode; however, during standard operations, the node's Go code runs natively, leveraging whatever execution environment is on the machine. Nitro confidently adopts a more "optimistic" approach to execution, compiling to Wasm only as necessary. This native execution method guarantees superior speed and efficiency, leading to enhanced node performance that translates directly into lower fees for end users. - -### Calldata Compression - -Most Arbitrum Rollup transactions' fees cover the essential cost of posting data on Ethereum. Any rollup must post sufficient data on L1 to effectively reconstruct and validate the L2 state. While L2s can choose their data formats, the high costs associated with posting data to L1 make it critical to implement optimizations. A highly effective strategy is to compress data (losslessly) before posting it on L1, allowing the L2 environment to manage decompression. - -Arbitrum core architecture's flexibility makes decompression possible, even with the Classic Arbitrum Virtual Machine (AVM). However, because the AVM is custom-built for Arbitrum, developing a proprietary implementation of a compression algorithm would have introduced excessive technical risks. - -In contrast, the Nitro architecture only requires its Virtual Machine (VM) to compile to Wasm, meaning Geth and any Go code are easily incorporated. This innovative approach allows Nitro to leverage widely adopted and tested compression libraries for calldata compression, drastically reducing the costs of posting transaction batches. - -Additionally, supporting calldata compression demands a sophisticated mechanism to accurately determine the [price of calldata](/how-arbitrum-works/l1-gas-pricing.mdx) and ensure that batch posters receive appropriate compensation. Nitro effectively addresses these challenges, solidifying its framework for efficient operation. - -## Closer EVM Compatibility - -The Classic AVM demonstrated impressive EVM compatibility by effectively handling all EVM opcodes. However, its internal behavior diverged significantly from that of the EVM. A key concern for smart contract developers was the introduction of "ArbGas," a denomination whose units do not align with Ethereum L1 gas. For instance, a simple transfer requires 21,000 gas on L1 but 100,000 ArbGas within the AVM. This discrepancy necessitated modifications to contracts that relied on gas calculation logic designed for L1 before deployment on L2. It also affected client-side tools that operated under similar fixed assumptions about gas on a blockchain. Nitro resolves this issue with [gas](/how-arbitrum-works/gas-fees.mdx) costs on L1 and L2 corresponding directly in a 1:1 ratio. - -:::note - -Transactions must account for the total expenses of L2 execution and L1 calldata. The values returned by Arbitrum nodes' `eth_estimateGas` RPC, which users see in their wallets, are designed to ensure they effectively cover this entire cost. Refer to the [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) for more details. - -::: - -Furthermore, Nitro enhances node functionality beyond execution, ensuring compatibility with many tools and infrastructure expectations—such as transaction tracing—available out of the box. This feature makes Nitro more compatible with Ethereum at the virtual machine level and how clients interact with the blockchain. - -In summary, leveraging Ethereum's software is undeniably the best approach to achieving compatibility with Ethereum. - -## Simplicity - -Maintaining code that is simple and easy to understand for Layer 2 systems is essential, given their inherent complexity. The classic stack consists of a large, in-house codebase that demands considerable time and effort to analyze effectively. In contrast, the Arbitrum Virtual Machine (AVM) and ArbOS combined constitute a robust blockchain protocol built from the ground up. The AVM was custom-built and lacked high-level languages at that time, forcing the implementation of ArbOS logic using a custom language called "mini," along with a mini-to-AVM compiler. - -Nitro's direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini) is much slimmer than in the classic stack; since the Geth software now handles the work of emulating the EVM, ArbOS needs only to implement the things-specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours put into Ethereum-Geth itself — makes it a system far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. diff --git a/website/archive/12-nitro-vs-classic.mdx b/website/archive/12-nitro-vs-classic.mdx new file mode 100644 index 000000000..00c9cbde1 --- /dev/null +++ b/website/archive/12-nitro-vs-classic.mdx @@ -0,0 +1,47 @@ +Why Nitro? + +Nitro represents the latest step in the evolution of Arbitrum technology; it is an upgrade from the tech stack first released on the mainnet Arbitrum One chain, which we now refer to as “Arbitrum Classic” (and several steps beyond what was described in the [initial Arbitrum whitepaper back in 2018](https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-kalodner.pdf)). Here, we’ll explain the rationale behind the Nitro upgrade, and outline Nitro’s core benefits over the classic system. + +Viewed from a distance, the Classic and Nitro systems do similar things: both seek to create an execution environment as close to the EVM as possible which operates as a second layer to Ethereum; i.e., safety of the L2 virtual machine’s state updates can be guaranteed and enforced via succinct fraud proofs on Ethereum itself. + +In Arbitrum Classic, this was achieved via a custom-made virtual machine, which we call the Arbitrum Virtual Machine (AVM). The implementation of Arbitrum’s L2 state machine—known as [“ArbOS”](/how-arbitrum-works/arbos/introduction.mdx) — is effectively a program that is compiled and uploaded to the AVM; ArbOS includes (among other things) the ability to emulate EVM execution. + +In Nitro, instead of using the AVM for low-level instructions, we use WebAssembly (Wasm). Since Go code can be compiled down to Wasm, we can implement the ArbOS program in Go, and include within it (as a sub-module) [Geth itself](/how-arbitrum-works/arbos/geth.mdx), the most widely used Ethereum implementation. + +This architecture—in which Geth’s EVM implementation can be used directly—is Nitro’s defining feature, and is principally what we’re talking about when we talk about “Nitro.” Most of Nitro’s benefits are a direct or indirect consequence of this design choice. We can summarize these benefits as follows: lower fees, better Ethereum compatibility, and simplicity. + +### Lower Fees + +#### (Optimistic)^2 Execution + +To understand the core of Nitro’s efficiency, we have to dig a little deeper into the classic AVM. In classic, high-level code (Solidity, Vyper, etc.) would be initially compiled down to the EVM bytecode (as though it were to be deployed on Ethereum). This bytecode would then be transpiled to its corresponding AVM instructions by ArbOS; this AVM bytecode would function both as the instructions for running the L2 VM, and the inputs used to prove fraud; in an interactive fraud proof, two validators dissect a segment of AVM bytecode until a “one step proof” — i.e., a state transition that represents a single AVM opcode — would be executed in the EVM of the L1 itself. + +Nitro has a similar bytecode-sandwich-like structure; [to prove fraud in Nitro](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx), the node’s Go code is compiled into WebAssembly (Wasm), the individual instructions of which are ultimately similarly dissected over to zero-in on an invalid state update. There is, however, a crucial difference: Nitro, being essentially the EVM, periodically produces Ethereum-esque blocks; we can think of these blocks as natural state-checkpoints within a larger assertion of an L2 state update. Nitro takes advantage of this by splitting the interactive fraud proof game into 2 phases: first, two disputing parties narrow down their disagreement to a single block; then (and only then) do they compile the block to Wasm, and thereby continue to narrow down their dispute to Wasm instruction. Thus, this Wasm compilation step only needs to happen when a dispute occurs. + +It’s worth reiterating this distinction: in classic, the code executed in the happy/common case is equivalent to the code used in a fraud proof, whereas in Nitro, we can have different contexts for the two cases for execution and for proving. When a claim is being disputed, we ultimately compile down to Wasm bytecode, but in the happy/common case, we can execute the node’s Go code natively, i.e., in whatever execution environment one’s machine uses. Essentially, Nitro is capable of being even more “optimistic” in its execution, compiling to Wasm only just-in-time as required. The common case of native execution is happily far faster and more performant, and better node performance, of course, translates to lower fees for end users. + +#### Calldata Compression + +Typically, the bulk of an Arbitrum Rollup transaction’s fee is covering the cost to post its data on Ethereum. Fundamentally, any rollup must post data on L1 sufficient for reconstruction and validation of the L2 state; beyond that, L2s can be flexible in deciding on what data format to use. Given the relatively high cost of posting data to L1, a natural optimization is to (losslessly) compress data before posting it on L1, and have the L2 environment handle decompressing it. + +The flexibility that Arbitrum core architecture offers meant that even in the classic AVM, such decompression could have been implemented in principle. However, given that the AVM was custom-built for Arbitrum, this would have meant building a custom, hand-rolled implementation of a compression algorithm, which, practically speaking, represented a prohibitively high technical risk. + +The Nitro architecture, however, fundamentally requires only that its VM can be compiled down to Wasm; so not just Geth, but any Go code can be incorporated. Thus, Nitro can (and does) use widely used, battle-tested compression libraries for calldata compression, and thus significantly reduces the cost of posting transaction batches. + +Note that supporting calldata compression also requires a more sophisticated mechanism for [determining the price of calldata](/how-arbitrum-works/l1-gas-pricing.mdx) and ensuring that batch posters are ultimately properly compensated, a mechanism which Nitro also introduces. + +## Closer EVM Compatibility + +The classic AVM achieved a strong degree of EVM compatibility with its ability to handle any EVM opcodes. However, being a distinct VM, the AVM’s internal behavior in some ways diverged with that of the EVM. Most noticeable for smart contract developers was the denomination of “ArbGas”, whose units didn’t correspond to Ethereum L1 gas; e.g., a simple transfer takes 21,000 gas on L1 but over 100,000 ArbGas in the AVM. This meant that contracts that included gas calculation logic that were initially built for L1 had to be modified accordingly to be deployed on L2, and likewise with any client-side tooling with similar hardcoded expectations about a chain’s gas. With Nitro, [gas](/how-arbitrum-works/gas-fees.mdx) on L1 and L2 essentially correspond 1:1. + +(Note that transactions have to cover the total cost of both L2 execution and L1 calldata; the value returned by Arbitrum nodes' `eth_estimateGas` RPC — and in turn, the value users will see in their wallets — is calculated to be sufficient to cover this total cost. See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) for more.) + +Additionally, node functionality peripheral to execution itself, but still important / expected by much tooling and infrastructure — e.g. support for transaction tracing — is essentially inherited out-of-the-box in Nitro, giving Nitro stronger compatibility with Ethereum not just within its virtual machine, but also with how clients interact with it. + +In short, there’s no better way to achieve Ethereum compatibility than to reuse the Ethereum software itself. + +## Simplicity + +Having code that is as simple and easy to reason about as possible is important for L2 systems, which are inevitably complex. The classic stack represents a large codebase built in-house, which requires a fair amount of time and overhead to understand. The AVM together with ArbOS effectively constitute a full blockchain protocol built from the ground up. Since the AVM was custom-built, with no high-level languages yet created for it, the ArbOS logic had to be implemented in what was essentially a custom language — called “mini” — along with a mini-to-AVM compiler. + +Nitro’s direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini), is much slimmer than in the classic stack; since the work of emulating the EVM is now handled by the Geth software, ArbOS needs only to implement the things specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours that have been put into an Ethereum-Geth itself — makes it a system that’s far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. \ No newline at end of file From d09cd63f185c446de4b1eab043e0996952dc3296 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 10 Dec 2024 09:09:17 -0600 Subject: [PATCH 20/75] yarn format --- arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx | 3 ++- .../05-separating-execution-from-proving.mdx | 1 - arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 1 - .../how-arbitrum-works/07-interactive-fraud-proofs.mdx | 2 -- arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index e2e0dfad5..e5e4256aa 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -149,7 +149,8 @@ Please note any links on this page may be referencing old releases of Nitro or o Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#enablearbos) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. ->>>>>>> master:arbitrum-docs/how-arbitrum-works/arbos/geth.mdx + +> > > > > > > master:arbitrum-docs/how-arbitrum-works/arbos/geth.mdx - `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` - `core.NewStateTransition` diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 0de531b20..7a247ee71 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -2,7 +2,6 @@ title: Separating Execution from Proving --- - One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 16a3deda9..6e4b61e2c 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -5,7 +5,6 @@ title: Optimistic Rollup - Arbitrum is an optimistic rollup. Let’s unpack that term. _Rollup_ diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 6887a4219..3994da5da 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -4,8 +4,6 @@ title: Interactive Fraud Proofs - - Suppose the rollup chain looks like this: ![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index e301ec47f..16587e36a 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -3,7 +3,6 @@ title: Gas and Fees author: dzgoldman --- - There are two parties a user pays when submitting a tx: - the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx From e2c86d3adf6bfa13523fcf9fb973465fff33be1c Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 10 Dec 2024 09:10:47 -0600 Subject: [PATCH 21/75] modified sidebars --- website/sidebars.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/website/sidebars.js b/website/sidebars.js index bd6e30333..f07528504 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -871,11 +871,6 @@ const sidebars = { id: 'how-arbitrum-works/l2-to-l1-messaging', label: 'L2 to L1 messaging', }, - { - type: 'doc', - id: 'how-arbitrum-works/nitro-vs-classic', - label: 'Nitro vs. Classic', - }, { type: 'link', href: 'https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf', From 2b764664c699b3f946871ce2847e80cacedf3784 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 10 Dec 2024 09:16:27 -0600 Subject: [PATCH 22/75] fixing broken links --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- arbitrum-docs/stylus/stylus-quickstart.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 6e4b61e2c..33f3b5d78 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -294,6 +294,6 @@ The only delay that users experience during a dispute is of their [L2 to L1 mess ### Detailed Spec -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum#rollup#protocol). +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/assertion-tree.mdx). diff --git a/arbitrum-docs/stylus/stylus-quickstart.mdx b/arbitrum-docs/stylus/stylus-quickstart.mdx index c3f103d98..a8b13850e 100644 --- a/arbitrum-docs/stylus/stylus-quickstart.mdx +++ b/arbitrum-docs/stylus/stylus-quickstart.mdx @@ -204,7 +204,7 @@ deployment tx total cost: "0.000712373700000000" ETH ### Deployment -Let's move on to the contract's actual deployment. Two transactions will be sent onchain: the contract deployment and its [activation](/stylus/stylus-gentle-introduction.md#activation). +Let's move on to the contract's actual deployment. Two transactions will be sent onchain: the contract deployment and its activation. ```shell cargo stylus deploy \ From c82e3f55494048c15cd7262f8499c6ca17af722d Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 12 Dec 2024 10:28:21 -0600 Subject: [PATCH 23/75] addressing quick hit comments --- .../01-a-gentle-introduction.mdx | 7 +- .../02-transaction-lifecycle.mdx | 16 +- .../how-arbitrum-works/03-sequencer.mdx | 73 ++++-- .../04-geth-at-the-core.mdx | 234 +++++++++--------- .../05-separating-execution-from-proving.mdx | 8 +- .../06-optimistic-rollup.mdx | 52 ++-- .../07-interactive-fraud-proofs.mdx | 70 +----- .../08-anytrust-protocol.mdx | 6 +- .../how-arbitrum-works/09-gas-fees.mdx | 12 +- .../10-l1-to-l2-messaging.mdx | 18 +- .../11-l2-to-l1-messaging.mdx | 7 +- arbitrum-docs/stylus/stylus-quickstart.mdx | 2 +- .../archive}/inside-arbitrum-nitro.mdx | 0 website/docusaurus.config.js | 4 +- 14 files changed, 255 insertions(+), 254 deletions(-) rename {arbitrum-docs/how-arbitrum-works => website/archive}/inside-arbitrum-nitro.mdx (100%) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 64984d712..669ca6fad 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -11,7 +11,7 @@ import ImageWithCaption from '@site/src/components/ImageCaptions/'; This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. -The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. +The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called [AnyTrust](/how-arbitrum-works/anytrust-protocol.mdx), which is used by the Arbitrum Nova chain. ## Why use Arbitrum? Why use Nitro? @@ -24,7 +24,7 @@ Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination o Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. -Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in several ways: +Nitro is a major upgrade to Arbitrum including: - **Advanced Calldata Compression,** which further drives down transaction costs on Arbitrum by reducing the amount of data posted to L1. - **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. @@ -69,3 +69,6 @@ The essence of Nitro, and its key innovations, lie in four big ideas. We'll list **Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. **Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. + + +Now that we have covered the foundational concepts, the big picture, and the four big ideas of Arbitrum Nitro, we will begin a journey following a transaction through the Arbitrum protocol. In the next section, the transaction lifecycle begins. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 99508a4d9..3d67dd737 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -1,8 +1,12 @@ --- -title: Transaction Lifecycle +title: Sequencing, Followed by Deterministic Execution +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- -## Sequencing, Followed by Deterministic Execution This diagram summarizes how transactions are processed in Nitro. @@ -20,13 +24,13 @@ The state transition function is deterministic, which means that its behavior de It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. -## Overview: The Lifecycle of an Arbitrum Transaction +## Deep dive: The lifecycle of an Arbitrum transaction -As an introduction to the various components that compose the Arbitrum protocol, we'll go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. +We'll now go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. We'll also intersperse it with "finality checks," explaining what guarantees the client has over their transaction's finality (i.e., assurances that their transaction's result is guaranteed and won't later be altered) over the course of a transaction's various stages. -This overview will be focused on the Arbitrum Rollup protocol; see [Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the security properties of the core system itself; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization) for current decentralization status. +This section will focus on the Arbitrum Rollup protocol; see [Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the security properties of the core system itself; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization) for current decentralization status. For clarity on any terminology that may be unfamiliar, see our [glossary](/intro/glossary.mdx). @@ -64,7 +68,7 @@ Upon receiving a transaction, the Sequencer will: #### ~ ~ ~ FINALITY CHECK: Trusted / Soft Confirmation ~ ~ ~ -At this phase, the client's acceptance of finality relies on trusting the Sequencer. I.e., a malicious/faulty Sequencer could deviate between what it promised in the transaction receipt and what is ultimately published in a batch (see phase 3). +At this phase, the client's acceptance of finality relies on trusting the Sequencer, i.e., a malicious/faulty Sequencer could deviate between what it promised in the transaction receipt and what is ultimately published in a batch (see phase 3). :::note diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 8869381e7..e232aa0bf 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -1,9 +1,12 @@ --- -title: Sequencer +title: The Sequencer and Censorship Resistance +description: 'Learn the fundamentals of the Arbitrum Sequencer.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- -## The Sequencer and Censorship Resistance - The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. @@ -14,15 +17,15 @@ Clients interact with the Sequencer in exactly the same way they would interact ### The Core Inbox -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “Transaction Lifecycle”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/arbos/transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. - -### Bridging +### Happy/Common Case: Sequencer Is Live and Well-behaved -We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. +Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. -The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). +If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. +Once posted in a batch, the transactions have L1-level finality. #### L1 contracts can submit L2 transactions @@ -54,16 +57,7 @@ Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) - - -### Happy/Common Case: Sequencer Is Live and Well-behaved - -Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. - -If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. -Once posted in a batch, the transactions have L1-level finality. - -Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. +Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/l1-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. @@ -81,7 +75,6 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. - #### How the Sequencer Publishes the Sequence @@ -93,4 +86,44 @@ The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. - + +## Instant confirmation + +Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. + +The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. + +## Inboxes, fast and slow + +When we add a Sequencer, the operation of the inbox changes. + +- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) +- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. + - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. + - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) + +## If the Sequencer is well-behaved... + +A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. + +It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. + +This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. + +This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. + +So a Sequencer is generally a win, if the Sequencer is well behaved. + +## If the Sequencer is malicious... + +A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. + +On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. + +Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. + +## Decentralized fair sequencing + +Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. + +How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index e5e4256aa..dd0678dda 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -1,5 +1,10 @@ --- -title: Geth at the Core +title: Geth at the core +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. @@ -16,122 +21,14 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - + -## ArbOS - -ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. - -In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. - -Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. - -### Precompiles - -ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. Visit the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx) for more information about how these work, and the [precompiles reference page](/build-decentralized-apps/precompiles/02-reference.mdx) for a full reference of the precompiles available in Arbitrum chains. - -A precompile consists of a solidity interface in [`contracts/src/precompiles/`][nitro_precompiles_dir] and a corresponding Golang implementation in [`precompiles/`][precompiles_dir]. Using Geth's ABI generator, [`solgen/gen.go`][gen_file] generates [`solgen/go/precompilesgen/precompilesgen.go`][precompilesgen_link], which collects the ABI data of the precompiles. The [runtime installer][installer_link] uses this generated file to check the type safety of each precompile's implementer. - -[The installer][installer_link] uses runtime reflection to ensure each implementer has all the right methods and signatures. This includes restricting access to stateful objects like the EVM and statedb based on the declared purity. Additionally, the installer verifies and populates event function pointers to provide each precompile the ability to emit logs and know their gas costs. Additional configuration like restricting a precompile's methods to only be callable by chain owners is possible by adding precompile wrappers like [`ownerOnly`][owneronly_link] and [`debugOnly`][debugonly_link] to their [installation entry][installation_link]. - -The calling, dispatching, and recording of precompile methods are done via runtime reflection as well. This avoids any human error manually parsing and writing bytes could introduce, and uses Geth's stable APIs for [packing and unpacking][packing_link] values. - -Each time a transaction calls a method of an L2-specific precompile, a [`call context`][call_context_link] is created to track and record the gas burnt. For convenience, it also provides access to the public fields of the underlying [`TxProcessor`][txprocessor_link]. Because sub-transactions could revert without updates to this struct, the [`TxProcessor`][txprocessor_link] only makes public that which is safe, such as the amount of L1 calldata paid by the top level transaction. - -[nitro_precompiles_dir]: https://github.com/OffchainLabs/nitro-contracts/tree/main/src/precompiles -[precompiles_dir]: https://github.com/OffchainLabs/nitro/tree/master/precompiles -[installer_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L379 -[installation_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L403 -[gen_file]: https://github.com/OffchainLabs/nitro/blob/master/solgen/gen.go -[owneronly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L58 -[debugonly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L23 -[precompilesgen_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/solgen/gen.go#L55 -[packing_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L438 -[call_context_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/context.go#L26 - -### Messages - -An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. - -[l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 -[produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 - -### Retryables - -A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). - -### ArbOS State - -ArbOS's state is viewed and modified via [`ArbosState`][arbosstate_link] objects, which provide convenient abstractions for working with the underlying data of its [`backingStorage`][backingstorage_link]. The backing storage's [keyed subspace strategy][subspace_link] makes possible [`ArbosState`][arbosstate_link]'s convenient getters and setters, minimizing the need to directly work with the specific keys and values of the underlying storage's [`stateDB`][statedb_link]. - -Because two [`ArbosState`][arbosstate_link] objects with the same [`backingStorage`][backingstorage_link] contain and mutate the same underlying state, different [`ArbosState`][arbosstate_link] objects can provide different views of ArbOS's contents. [`Burner`][burner_link] objects, which track gas usage while working with the [`ArbosState`][arbosstate_link], provide the internal mechanism for doing so. Some are read-only, causing transactions to revert with `vm.ErrWriteProtection` upon a mutating request. Others demand the caller have elevated privileges. While yet others dynamically charge users when doing stateful work. For safety the kind of view is chosen when [`OpenArbosState()`][openarbosstate_link] creates the object and may never change. - -Much of ArbOS's state exists to facilitate its [precompiles](/build-decentralized-apps/precompiles/02-reference.mdx). The parts that aren't are detailed below. - -[arbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L36 -[backingstorage_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L51 -[statedb_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L66 -[subspace_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L21 -[openarbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L57 -[burner_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/burn/burn.go#L11 - -#### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] - -ArbOS upgrades are scheduled to happen [when finalizing the first block][finalizeblock_link] after the [`upgradeTimestamp`][upgradetimestamp_link]. - -[arbosversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L37 -[upgradeversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L38 -[upgradetimestamp_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L39 -[finalizeblock_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L350 - -#### [`blockhashes`][blockhashes_link] - -This component maintains the last 256 L1 block hashes in a circular buffer. This allows the [`TxProcessor`][txprocessor_link] to implement the `BLOCKHASH` and `NUMBER` opcodes as well as support precompile methods that involve the outbox. To avoid changing ArbOS state outside of a transaction, blocks made from messages with a new L1 block number update this info during an [`InternalTxUpdateL1BlockNumber`][internaltxupdatel1blocknumber_link] [`ArbitrumInternalTx`][arbitruminternaltx_link] that is included as the first transaction in the block. - -[blockhashes_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/blockhash/blockhash.go#L15 -[internaltxupdatel1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/internal_tx.go#L24 -[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L116 -[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 - -#### [`l1PricingState`][l1pricingstate_link] - -In addition to supporting the [`ArbAggregator precompile`](/build-decentralized-apps/precompiles/02-reference.mdx#arbaggregator), the L1 pricing state provides tools for determining the L1 component of a transaction's gas costs. This part of the state tracks both the total amount of funds collected from transactions in L1 gas fees, as well as the funds spent by batch posters to post data batches on L1. - -Based on this information, ArbOS maintains an L1 data fee, also tracked as part of this state, which determines how much transactions will be charged for L1 fees. ArbOS dynamically adjusts this value so that fees collected are approximately equal to batch posting costs, over time. - -[l1pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l1pricing/l1pricing.go#L16 - -#### [`l2PricingState`][l2pricingstate_link] - -The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. - -While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth#Hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. - -ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](geth#GasChargingHook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. - -The reason we need a per-block gas limit is that Arbitrator WAVM execution is much slower than native transaction execution. This means that there can only be so much gas -- which roughly translates to wall-clock time -- in an L2 block. It also provides an opportunity for ArbOS to limit the size of blocks should demand continue to surge even as the price rises. - -ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [sets sufficiently high][geth_pool_set_link] so as to never run out. This is safe since Geth's block limit exists to constrain the amount of work done per block, which ArbOS already does via its own per-block gas limit. Though it'll never run out, a block's transactions use the [same Geth gas pool][same_geth_pool_link] to maintain the invariant that the pool decreases monotonically after each tx. Block headers [use the Geth block limit][use_geth_pool_link] for internal consistency and to ensure gas estimation works. These are both distinct from the [`gasLeft`][per_block_limit_link] variable, which ephemerally exists outside of global state to both keep L2 blocks from exceeding ArbOS's per-block gas limit and to [deduct space][deduct_space_link] in situations where the state transition failed or [used negligible amounts][negligible_amounts_link] of compute gas. ArbOS does not need to persist [`gasLeft`][per_block_limit_link] because it is its _pool_ that induces a revert and because transactions use the Geth block limit during EVM execution. - -[l2pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l2pricing/l2pricing.go#L14 -[block_production_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L77 -[notify_pricer_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L336 -[maintain_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l2pricing/pools.go#L98 -[per_block_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L146 -[first_transaction_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L237 -[geth_pool_set_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L166 -[same_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L199 -[use_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L67 -[deduct_space_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L272 -[negligible_amounts_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L328 - - - - ## Geth -Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This document will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. +Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This section will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. We store ArbOS's state at an address inside a Geth `statedb`. In doing so, ArbOS inherits the `statedb`'s statefulness and lifetime properties. For example, a transaction's direct state changes to ArbOS are discarded upon a revert. @@ -150,7 +47,6 @@ Arbitrum uses various hooks to modify Geth's behavior when processing transactio Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#enablearbos) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. -> > > > > > > master:arbitrum-docs/how-arbitrum-works/arbos/geth.mdx - `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` - `core.NewStateTransition` @@ -420,4 +316,112 @@ Genesis block in nitro is not necessarily block #0. Nitro supports importing blo [underlyingtransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L69 [writeheadblock_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/genesis.go#L415 - + + +## ArbOS + +ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. + +In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. + +Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. + +### Precompiles + +ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. Visit the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx) for more information about how these work, and the [precompiles reference page](/build-decentralized-apps/precompiles/02-reference.mdx) for a full reference of the precompiles available in Arbitrum chains. + +A precompile consists of a solidity interface in [`contracts/src/precompiles/`][nitro_precompiles_dir] and a corresponding Golang implementation in [`precompiles/`][precompiles_dir]. Using Geth's ABI generator, [`solgen/gen.go`][gen_file] generates [`solgen/go/precompilesgen/precompilesgen.go`][precompilesgen_link], which collects the ABI data of the precompiles. The [runtime installer][installer_link] uses this generated file to check the type safety of each precompile's implementer. + +[The installer][installer_link] uses runtime reflection to ensure each implementer has all the right methods and signatures. This includes restricting access to stateful objects like the EVM and statedb based on the declared purity. Additionally, the installer verifies and populates event function pointers to provide each precompile the ability to emit logs and know their gas costs. Additional configuration like restricting a precompile's methods to only be callable by chain owners is possible by adding precompile wrappers like [`ownerOnly`][owneronly_link] and [`debugOnly`][debugonly_link] to their [installation entry][installation_link]. + +The calling, dispatching, and recording of precompile methods are done via runtime reflection as well. This avoids any human error manually parsing and writing bytes could introduce, and uses Geth's stable APIs for [packing and unpacking][packing_link] values. + +Each time a transaction calls a method of an L2-specific precompile, a [`call context`][call_context_link] is created to track and record the gas burnt. For convenience, it also provides access to the public fields of the underlying [`TxProcessor`][txprocessor_link]. Because sub-transactions could revert without updates to this struct, the [`TxProcessor`][txprocessor_link] only makes public that which is safe, such as the amount of L1 calldata paid by the top level transaction. + +[nitro_precompiles_dir]: https://github.com/OffchainLabs/nitro-contracts/tree/main/src/precompiles +[precompiles_dir]: https://github.com/OffchainLabs/nitro/tree/master/precompiles +[installer_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L379 +[installation_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L403 +[gen_file]: https://github.com/OffchainLabs/nitro/blob/master/solgen/gen.go +[owneronly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L58 +[debugonly_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/wrapper.go#L23 +[precompilesgen_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/solgen/gen.go#L55 +[packing_link]: https://github.com/OffchainLabs/nitro/blob/bc6b52daf7232af2ca2fec3f54a5b546f1196c45/precompiles/precompile.go#L438 +[call_context_link]: https://github.com/OffchainLabs/nitro/blob/f11ba39cf91ee1fe1b5f6b67e8386e5efd147667/precompiles/context.go#L26 + +### Messages + +An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. + +[l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 +[produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 + +### Retryables + +A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). + +### ArbOS State + +ArbOS's state is viewed and modified via [`ArbosState`][arbosstate_link] objects, which provide convenient abstractions for working with the underlying data of its [`backingStorage`][backingstorage_link]. The backing storage's [keyed subspace strategy][subspace_link] makes possible [`ArbosState`][arbosstate_link]'s convenient getters and setters, minimizing the need to directly work with the specific keys and values of the underlying storage's [`stateDB`][statedb_link]. + +Because two [`ArbosState`][arbosstate_link] objects with the same [`backingStorage`][backingstorage_link] contain and mutate the same underlying state, different [`ArbosState`][arbosstate_link] objects can provide different views of ArbOS's contents. [`Burner`][burner_link] objects, which track gas usage while working with the [`ArbosState`][arbosstate_link], provide the internal mechanism for doing so. Some are read-only, causing transactions to revert with `vm.ErrWriteProtection` upon a mutating request. Others demand the caller have elevated privileges. While yet others dynamically charge users when doing stateful work. For safety the kind of view is chosen when [`OpenArbosState()`][openarbosstate_link] creates the object and may never change. + +Much of ArbOS's state exists to facilitate its [precompiles](/build-decentralized-apps/precompiles/02-reference.mdx). The parts that aren't are detailed below. + +[arbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L36 +[backingstorage_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L51 +[statedb_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L66 +[subspace_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/storage/storage.go#L21 +[openarbosstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L57 +[burner_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/burn/burn.go#L11 + +#### [`arbosVersion`][arbosversion_link], [`upgradeVersion`][upgradeversion_link] and [`upgradeTimestamp`][upgradetimestamp_link] + +ArbOS upgrades are scheduled to happen [when finalizing the first block][finalizeblock_link] after the [`upgradeTimestamp`][upgradetimestamp_link]. + +[arbosversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L37 +[upgradeversion_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L38 +[upgradetimestamp_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/arbosState/arbosstate.go#L39 +[finalizeblock_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L350 + +#### [`blockhashes`][blockhashes_link] + +This component maintains the last 256 L1 block hashes in a circular buffer. This allows the [`TxProcessor`][txprocessor_link] to implement the `BLOCKHASH` and `NUMBER` opcodes as well as support precompile methods that involve the outbox. To avoid changing ArbOS state outside of a transaction, blocks made from messages with a new L1 block number update this info during an [`InternalTxUpdateL1BlockNumber`][internaltxupdatel1blocknumber_link] [`ArbitrumInternalTx`][arbitruminternaltx_link] that is included as the first transaction in the block. + +[blockhashes_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/blockhash/blockhash.go#L15 +[internaltxupdatel1blocknumber_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/internal_tx.go#L24 +[arbitruminternaltx_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L116 +[txprocessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 + +#### [`l1PricingState`][l1pricingstate_link] + +In addition to supporting the [`ArbAggregator precompile`](/build-decentralized-apps/precompiles/02-reference.mdx#arbaggregator), the L1 pricing state provides tools for determining the L1 component of a transaction's gas costs. This part of the state tracks both the total amount of funds collected from transactions in L1 gas fees, as well as the funds spent by batch posters to post data batches on L1. + +Based on this information, ArbOS maintains an L1 data fee, also tracked as part of this state, which determines how much transactions will be charged for L1 fees. ArbOS dynamically adjusts this value so that fees collected are approximately equal to batch posting costs, over time. + +[l1pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l1pricing/l1pricing.go#L16 + +#### [`l2PricingState`][l2pricingstate_link] + +The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. + +While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth#Hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. + +ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](geth#GasChargingHook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. + +The reason we need a per-block gas limit is that Arbitrator WAVM execution is much slower than native transaction execution. This means that there can only be so much gas -- which roughly translates to wall-clock time -- in an L2 block. It also provides an opportunity for ArbOS to limit the size of blocks should demand continue to surge even as the price rises. + +ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [sets sufficiently high][geth_pool_set_link] so as to never run out. This is safe since Geth's block limit exists to constrain the amount of work done per block, which ArbOS already does via its own per-block gas limit. Though it'll never run out, a block's transactions use the [same Geth gas pool][same_geth_pool_link] to maintain the invariant that the pool decreases monotonically after each tx. Block headers [use the Geth block limit][use_geth_pool_link] for internal consistency and to ensure gas estimation works. These are both distinct from the [`gasLeft`][per_block_limit_link] variable, which ephemerally exists outside of global state to both keep L2 blocks from exceeding ArbOS's per-block gas limit and to [deduct space][deduct_space_link] in situations where the state transition failed or [used negligible amounts][negligible_amounts_link] of compute gas. ArbOS does not need to persist [`gasLeft`][per_block_limit_link] because it is its _pool_ that induces a revert and because transactions use the Geth block limit during EVM execution. + +[l2pricingstate_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/l2pricing/l2pricing.go#L14 +[block_production_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L77 +[notify_pricer_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L336 +[maintain_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l2pricing/pools.go#L98 +[per_block_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L146 +[first_transaction_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L237 +[geth_pool_set_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L166 +[same_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L199 +[use_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L67 +[deduct_space_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L272 +[negligible_amounts_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L328 + diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 7a247ee71..444a5f2f4 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -1,7 +1,13 @@ --- title: Separating Execution from Proving +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- + One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) @@ -29,5 +35,3 @@ As an example, the state of a Nitro chain is maintained in Ethereum's state tree The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - - diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 33f3b5d78..63dcd9d94 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -1,5 +1,10 @@ --- title: Optimistic Rollup +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- @@ -71,6 +76,8 @@ The parties who participate in the protocol are called _validators_. Some valida The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. + + ### The Rollup Chain The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. @@ -233,9 +240,18 @@ The first unresolved RBlock can be rejected if: A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. - - +## Delays + +Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid RBlock (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. + +The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. + + +### Detailed Spec + +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/assertion-tree.mdx). + ## Validators @@ -262,38 +278,6 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. - Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. - Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - - - - -# The Assertion Tree - -### Overview -The state of an Arbitrum chain is confirmed back on Ethereum via "assertions," aka "disputable assertions" or "DAs." These are claims made by Arbitrum validators about the chain's state. To make an assertion, a validator must post a bond in Ether. -In the happy / common case, all outstanding assertions will be valid; i.e., a valid assertion will build on another valid assertion, which builds on another valid assertion, and so on. After the dispute period (~ 1 week) passes and an assertion goes unchallenged, it can be confirmed back on L1. - -If, however, two or more conflicting assertions exist, the Assertion Tree bifurcates into multiple branches: - -![img](../assets/assertionTree.png) - -Crucially, the rules of advancing an Arbitrum chain are deterministic; this means that given a chain state and some new inputs, there is only one valid output. Thus, if the Assertion Tree contains more than one leaf, then at most only one leaf can represent the valid chain-state; if we assume there is at least one honest active validator, _exactly_ one leaf will be valid. - -Two conflicting assertions can be put into a dispute; see [Interactive Challenges](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx) for details on the dispute process. For the sake of understanding the Assertion Tree protocol, suffice it to say that 2-party disputes last at most a fixed amount of time (1 week), at the end of which one of the two conflicting assertions will be rejected, and the validator who posted it will lose their stake. - -In order for an assertion to be confirmed and for its stake to be recovered, two conditions must be met: sufficient time for disputes must have passed, and no other conflicting branches in the Assertion Tree can exist (i.e., they've all been disputed / "pruned" off.) - -These properties together ensure that as long as at least one honest, active validator exists, the valid chain state will ultimately be confirmed. - -### Delays - -Even if the Assertion Tree has multiple conflicting leaves and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid leaf (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. - -The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. - -### Detailed Spec - -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/assertion-tree.mdx). - diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 3994da5da..0e823899a 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -1,8 +1,12 @@ --- -title: Interactive Fraud Proofs +title: Challenges: Interactive Fraud Proofs +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- - Suppose the rollup chain looks like this: @@ -62,56 +66,11 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. -## Instant confirmation - -Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. - -The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. - -## Inboxes, fast and slow - -When we add a Sequencer, the operation of the inbox changes. - -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) -- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. - - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) - -## If the Sequencer is well-behaved... - -A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. - -It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. - -This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. - -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. - -So a Sequencer is generally a win, if the Sequencer is well behaved. - -## If the Sequencer is malicious... - -A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. - -On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. - -Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. - -## Decentralized fair sequencing - -Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. - -How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. - - - - ## ChallengeManager - -The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: +This section is a technical deep dive into the `ChallengeManager` and will walk through the arbitration of a challenge game in great detail. The `ChallengeManager` plays the role of the arbiter of challenge games. Here's a diagram of the challenge state machine: @@ -172,9 +131,7 @@ valid moves, so it will eventually lose by timeout. This is done as a precaution, so that if a challenge is resolved incorrectly, there is time to diagnose and fix the error with a contract upgrade. - - ## One Step Proof Assumptions @@ -252,9 +209,6 @@ each trie branch is of a fixed size, and the only variable sized entry in the trie is contract code, which is limited by EIP-170 to about 24KB. - - - ## WASM to WAVM @@ -318,9 +272,6 @@ They are translated by bitcasting `f32` and `f64` arguments to `i32`s and `i64`s then a cross module call to the floating point library, and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and `f64`s. - - - ## WAVM Custom opcodes not in WASM @@ -397,9 +348,7 @@ For these instruction descriptions, all pointers and offsets are represented as | 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the sequencer inbox, 1 for the delayed inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | | 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | - - ## WAVM Floating point implementation @@ -434,9 +383,6 @@ Floating point to integer truncation will saturate on overflow, instead of error This is generally safer, because on x86, overflowing simply produces an undefined result. A WASM proposal exists to add new opcodes which are defined to saturate, but it's not widely adopted. - - - ## WAVM Modules @@ -495,5 +441,3 @@ Only libraries can access their caller's memory; the main module cannot. For instance, this is used to read arguments from and write return values to the Go stack, when Go calls into go-stub. - - diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index cb232998d..0ca414de5 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -1,6 +1,10 @@ --- title: AnyTrust Protocol -author: dzgoldman +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 16587e36a..534c16c72 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -1,8 +1,14 @@ --- title: Gas and Fees -author: dzgoldman +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- +Gas is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. + There are two parties a user pays when submitting a tx: - the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx @@ -34,11 +40,9 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu [estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/d52739e6d54f2ea06146fdc44947af3488b89082/internal/ethapi/api.go#L999 - -## Gas and Fees -NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. + ### The Speed Limit diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index ae2db04b6..bd93bc7a6 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -1,9 +1,21 @@ --- -title: L1 to L2 Messaging -author: dzgoldman +title: L1 to L2 messaging +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- - + +### Bridging + +We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. + +The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). + + + ## Retryable Tickets diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index 5e1237973..ea36cde80 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -1,5 +1,10 @@ --- -title: L1 to L2 Messaging +title: L2 to L1 messaging +description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +author: pete-vielhaber +sme: TucksonDev +user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +content_type: get-started --- diff --git a/arbitrum-docs/stylus/stylus-quickstart.mdx b/arbitrum-docs/stylus/stylus-quickstart.mdx index a8b13850e..c3f103d98 100644 --- a/arbitrum-docs/stylus/stylus-quickstart.mdx +++ b/arbitrum-docs/stylus/stylus-quickstart.mdx @@ -204,7 +204,7 @@ deployment tx total cost: "0.000712373700000000" ETH ### Deployment -Let's move on to the contract's actual deployment. Two transactions will be sent onchain: the contract deployment and its activation. +Let's move on to the contract's actual deployment. Two transactions will be sent onchain: the contract deployment and its [activation](/stylus/stylus-gentle-introduction.md#activation). ```shell cargo stylus deploy \ diff --git a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx b/website/archive/inside-arbitrum-nitro.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.mdx rename to website/archive/inside-arbitrum-nitro.mdx diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 878d81071..6140f97d3 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -11,8 +11,8 @@ const config = { tagline: 'Arbitrum Docs', url: 'https://docs.arbitrum.io/', baseUrl: '/', - onBrokenLinks: 'ignore', - onBrokenMarkdownLinks: 'ignore', + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'throw', favicon: 'img/logo.svg', markdown: { mermaid: true, From 0d4ccb8ba0356828b52e454553abb49528daf580 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 12 Dec 2024 10:58:06 -0600 Subject: [PATCH 24/75] content changes, frontmatter additions --- .../how-arbitrum-works/03-sequencer.mdx | 14 ++++++------- .../04-geth-at-the-core.mdx | 5 ++--- .../05-separating-execution-from-proving.mdx | 2 +- .../06-optimistic-rollup.mdx | 8 +------ .../07-interactive-fraud-proofs.mdx | 4 ++-- .../08-anytrust-protocol.mdx | 2 +- .../how-arbitrum-works/09-gas-fees.mdx | 8 ++----- .../10-l1-to-l2-messaging.mdx | 21 +++++++------------ .../11-l2-to-l1-messaging.mdx | 6 +----- .../how-arbitrum-works/assertion-tree.mdx | 2 +- 10 files changed, 25 insertions(+), 47 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index e232aa0bf..2bc80a958 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -15,19 +15,19 @@ Clients interact with the Sequencer in exactly the same way they would interact [Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. -### The Core Inbox +## The Core Inbox When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/arbos/transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. -### Happy/Common Case: Sequencer Is Live and Well-behaved +## Happy/Common Case: Sequencer Is Live and Well-behaved Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. Once posted in a batch, the transactions have L1-level finality. -#### L1 contracts can submit L2 transactions +### L1 contracts can submit L2 transactions An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. @@ -35,7 +35,7 @@ The advantage of this method is that it is simple and has relatively low latency This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. -#### L1 to L2 ticket-based transactions +### L1 to L2 ticket-based transactions Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. @@ -51,7 +51,7 @@ When the ticket is redeemed, the pre-packaged transaction runs with sender and o This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. -#### L2 to L1 ticket-based calls +### L2 to L1 ticket-based calls Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. @@ -61,7 +61,7 @@ Alternatively, a user can submit their L2 message to the Sequencer by posting it In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. -### Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job +## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: @@ -76,7 +76,7 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. -#### How the Sequencer Publishes the Sequence +### How the Sequencer Publishes the Sequence So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index dd0678dda..f3ffa206d 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -21,9 +21,8 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - +The rest of this section will be a deep dive into Geth and ArbOS. If deep technical knowledge does not suit you, skip to the next section [Separating Execution from Proving](/how-arbitrum-works/separating-execution-from-proving.mdx). + ## Geth diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 444a5f2f4..def946120 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -34,4 +34,4 @@ As an example, the state of a Nitro chain is maintained in Ethereum's state tree The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 63dcd9d94..50cd0bcc1 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -7,9 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - - - Arbitrum is an optimistic rollup. Let’s unpack that term. _Rollup_ @@ -277,7 +274,4 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. - Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. -- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - - - +- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 0e823899a..4cfa74934 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -67,12 +67,12 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - +import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; ## ChallengeManager This section is a technical deep dive into the `ChallengeManager` and will walk through the arbitration of a challenge game in great detail. The `ChallengeManager` plays the role of the arbiter of challenge games. Here's a diagram of the challenge state machine: - + ### Block challenge diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index 0ca414de5..7180f3709 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -71,4 +71,4 @@ When the Arbitrum sequencer produces a data batch that it wants to post using th Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. +If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 534c16c72..ad1c6c8be 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -42,7 +42,7 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu - + - ## L1 gas pricing @@ -147,6 +145,4 @@ A second term is added to the L1 Gas Basefee, based on the derivative of the sur The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. -Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. - - +Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. --> \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index bd93bc7a6..ff1fcb6da 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -7,14 +7,11 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - -### Bridging - -We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. - -The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). +In the [Transaction Lifecycle](/how-arbitrum-works/transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. +L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). +In this section, we will discuss how contracts interact between L1 and L2, how an L1 contract is called an L2 contract, and vice versa. ## Retryable Tickets @@ -47,7 +44,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol - ### Automatic Redemption @@ -96,7 +91,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - If a redeem is not done at submission or the submission's initial redeem fails (for example, because the L2 gas price has increased unexpectedly), the submission fee is collected on L2 to cover the resources required to temporarily keep the ticket in memory for a fixed period (one week), and only in this case, a manual redemption of the ticket is required (see next section). - + ### Manual Redemption @@ -141,7 +135,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket [discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 [renew_link]: https://github.com/OffchainLabs/nitro-contracts/blob/a68783436b5105a64f54efe5fbd55174704a7618/src/precompiles/ArbRetryableTx.sol#L41 - + :::caution Avoid Losing Funds! @@ -232,7 +225,7 @@ L2_Alias = L1_Contract_Address + 0x1111000000000000000000000000000000001111 :::tip Try it out - + ::: @@ -251,4 +244,4 @@ modifier onlyFromMyL1Contract() override { The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. -These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). +These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index ea36cde80..7aad30cc7 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -7,8 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - - Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. ### Protocol Flow @@ -28,6 +26,4 @@ Unlike Retryables, which have an option to provide Ether for automatic L2 execut Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) -\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. - - +\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx index 1669c1944..d8d6a3526 100644 --- a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx +++ b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx @@ -30,4 +30,4 @@ The only delay that users experience during a dispute is of their [L2 to L1 mess ### Detailed Spec -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). \ No newline at end of file From 01a7a43ba667f3f8e7911fa8563419d3db21231b Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 12 Dec 2024 13:02:21 -0600 Subject: [PATCH 25/75] gas & fees content, yarn format --- .../01-a-gentle-introduction.mdx | 3 +- .../02-transaction-lifecycle.mdx | 1 - .../how-arbitrum-works/03-sequencer.mdx | 3 - .../04-geth-at-the-core.mdx | 6 - .../05-separating-execution-from-proving.mdx | 3 +- .../06-optimistic-rollup.mdx | 7 +- .../07-interactive-fraud-proofs.mdx | 10 +- .../08-anytrust-protocol.mdx | 2 +- .../how-arbitrum-works/09-gas-fees.mdx | 103 +++++++++++++++++- .../10-l1-to-l2-messaging.mdx | 10 +- .../11-l2-to-l1-messaging.mdx | 2 +- .../how-arbitrum-works/assertion-tree.mdx | 2 +- 12 files changed, 107 insertions(+), 45 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 669ca6fad..1c9153861 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -70,5 +70,4 @@ The essence of Nitro, and its key innovations, lie in four big ideas. We'll list **Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. - -Now that we have covered the foundational concepts, the big picture, and the four big ideas of Arbitrum Nitro, we will begin a journey following a transaction through the Arbitrum protocol. In the next section, the transaction lifecycle begins. \ No newline at end of file +Now that we have covered the foundational concepts, the big picture, and the four big ideas of Arbitrum Nitro, we will begin a journey following a transaction through the Arbitrum protocol. In the next section, the transaction lifecycle begins. diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 3d67dd737..742653084 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -7,7 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - This diagram summarizes how transactions are processed in Nitro. ![seq-then-exec](../assets/seq-then-exec.png) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 2bc80a958..5bf5725c4 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -19,7 +19,6 @@ Clients interact with the Sequencer in exactly the same way they would interact When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/arbos/transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. - ## Happy/Common Case: Sequencer Is Live and Well-behaved Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. @@ -75,7 +74,6 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. - ### How the Sequencer Publishes the Sequence So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. @@ -86,7 +84,6 @@ The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. - ## Instant confirmation Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index f3ffa206d..5e1e9ac3e 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -23,8 +23,6 @@ The State Transition Function consists of the bottom Geth layer, and a portion o The rest of this section will be a deep dive into Geth and ArbOS. If deep technical knowledge does not suit you, skip to the next section [Separating Execution from Proving](/how-arbitrum-works/separating-execution-from-proving.mdx). - - ## Geth Nitro makes minimal modifications to Geth in hopes of not violating its assumptions. This section will explore the relationship between Geth and ArbOS, which consists of a series of hooks, interface implementations, and strategic re-appropriations of Geth's basic types. @@ -46,7 +44,6 @@ Arbitrum uses various hooks to modify Geth's behavior when processing transactio Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#enablearbos) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. - - `core.ApplyTransaction` ⮕ `core.applyTransaction` ⮕ `core.ApplyMessage` - `core.NewStateTransition` - [`ReadyEVMForL2`](#ReadyEVMForL2) @@ -315,8 +312,6 @@ Genesis block in nitro is not necessarily block #0. Nitro supports importing blo [underlyingtransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/state_transition.go#L69 [writeheadblock_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/genesis.go#L415 - - ## ArbOS ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. @@ -423,4 +418,3 @@ ArbOS's per-block gas limit is distinct from Geth's block limit, which ArbOS [se [use_geth_pool_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L67 [deduct_space_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L272 [negligible_amounts_link]: https://github.com/OffchainLabs/nitro/blob/faf55a1da8afcabb1f3c406b291e721bfde71a05/arbos/block_processor.go#L328 - diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index def946120..80085f8c1 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -7,7 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) @@ -34,4 +33,4 @@ As an example, the state of a Nitro chain is maintained in Ethereum's state tree The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. \ No newline at end of file +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 50cd0bcc1..e7e110704 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -73,8 +73,6 @@ The parties who participate in the protocol are called _validators_. Some valida The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. - - ### The Rollup Chain The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. @@ -237,19 +235,16 @@ The first unresolved RBlock can be rejected if: A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. - ## Delays Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid RBlock (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. - ### Detailed Spec For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/assertion-tree.mdx). - ## Validators Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. @@ -274,4 +269,4 @@ Who will be validators? Anyone will be able to do it, but most people will choos - Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. - Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. -- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. \ No newline at end of file +- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 4cfa74934..0c60a71bd 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -7,7 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- - Suppose the rollup chain looks like this: ![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) @@ -66,10 +65,10 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; ## ChallengeManager + This section is a technical deep dive into the `ChallengeManager` and will walk through the arbitration of a challenge game in great detail. The `ChallengeManager` plays the role of the arbiter of challenge games. Here's a diagram of the challenge state machine: @@ -131,8 +130,6 @@ valid moves, so it will eventually lose by timeout. This is done as a precaution, so that if a challenge is resolved incorrectly, there is time to diagnose and fix the error with a contract upgrade. - - ## One Step Proof Assumptions The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise @@ -209,7 +206,6 @@ each trie branch is of a fixed size, and the only variable sized entry in the trie is contract code, which is limited by EIP-170 to about 24KB. - ## WASM to WAVM Not all WASM instructions are 1:1 with WAVM opcodes. @@ -272,7 +268,6 @@ They are translated by bitcasting `f32` and `f64` arguments to `i32`s and `i64`s then a cross module call to the floating point library, and finally bitcasts of any return values from `i32`s and `i64`s to `f32`s and `f64`s. - ## WAVM Custom opcodes not in WASM In addition to the MVP WASM specification, @@ -348,8 +343,6 @@ For these instruction descriptions, all pointers and offsets are represented as | 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the sequencer inbox, 1 for the delayed inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | | 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | - - ## WAVM Floating point implementation Implementing correct, consistent, and deterministic floating point operations directly in WAVM @@ -383,7 +376,6 @@ Floating point to integer truncation will saturate on overflow, instead of error This is generally safer, because on x86, overflowing simply produces an undefined result. A WASM proposal exists to add new opcodes which are defined to saturate, but it's not widely adopted. - ## WAVM Modules WASM natively has a notion of modules. diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index 7180f3709..0ca414de5 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -71,4 +71,4 @@ When the Arbitrum sequencer produces a data batch that it wants to post using th Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. \ No newline at end of file +If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index ad1c6c8be..57ebc7055 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -14,23 +14,78 @@ There are two parties a user pays when submitting a tx: - the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx - the network fee account for L2 resources, which include the computation, storage, and other burdens L2 nodes must bear to service the tx -The L1 component is the product of the transaction's estimated contribution to its batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. For details, see [L1 Pricing](/how-arbitrum-works/l1-gas-pricing.mdx). +The L1 component is the product of the transaction's estimated contribution to its batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. -## Gas Price Floor +The following sections will detail how to calculate L1 and L2 fees. If you do not need precise calculations or a technical understanding, skip to the next section, [L1 to L2 messaging](/how-arbitrum-works/l1-to-l2-messaging.mdx). + +## L1 gas pricing + +ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amount collected in L1 gas fees is as close as possible to the costs that must be covered, over time. + +### L1 costs + +There are two types of L1 costs: batch posting costs, and rewards. + +Batch posting costs reflect the actual cost a batch poster pays to post batch data on L1. Whenever a batch is posted, the L1 contract that records the batch will send a special "batch posting report" message to L2 ArbOS, reporting who paid for the batch and what the L1 basefee was at the time. This message is placed in the chain's delayed inbox, so it will be delivered to L2 ArbOS after some delay. + +When a batch posting report message arrives at L2, ArbOS computes the cost of the referenced batch by multiplying the reported basefee by the batch's data cost. (ArbOS retrieves the batch's data from its inbox state, and computes the L1 gas that the batch would have used by counting the number of zero bytes and non-zero bytes in the batch.) The resulting cost is recorded by the pricer as funds due to the party who is reported to have submitted the batch. + +The second type of L1 cost is an optional (per chain) per-unit reward for handling transaction calldata. In general the reward might be paid to the sequencer, or to members of the Data Availability Committee in an AnyTrust chain, or to anyone else who incurs per-calldata-byte costs on behalf of the chain. The reward is a fixed number of wei per data unit, and is paid to a single address. + +The L1 pricer keeps track of the funds due to the reward address, based on the number of data units handled so far. This amount is updated whenever a batch posting report arrives at L2. + +### L1 calldata fees + +L1 calldata fees exist because the Sequencer, or the batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. + +Every transaction that comes in through the Sequencer will pay an L1 calldata fee. Transactions that come in through the delayed inbox do not pay this fee because they don't add to batch posting costs--but these transactions pay gas fees to Ethereum when they are put into the delayed inbox. + +The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transaction. First, it computes the transaction's size, which is an estimate of how many bytes the transaction will add to the compressed batch it is in; the formula for this includes an estimate of how compressible the transaction is. Second, it multiplies the computed size estimate by the current price per estimated byte, to determine the transaction's L1 calldata wei, in wei. Finally, it divides this cost by the current L2 basefee to translate the fee into L2 gas units. The result is reported as the "poster fee" for the transaction. + +The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. + +### L1 fee collection + +A transaction is charged for L1 gas if and only if it arrived as part of a sequencer batch. This means that someone would have paid for L1 gas to post the transaction on the L1 chain. + +The estimated cost of posting a transaction on L1 is the product of the transaction's estimated size, and the current L1 Gas Basefee. This estimated cost is divided by the current L2 gas basefee to obtain the amount of L2 gas that corresponds to the L1 operation (more information about this can be found in [this article][two_dimensional_fees_medium_article_link]). + +The estimated size is measured in L1 gas and is calculated as follows: first, compress the transaction's data using the brotli-zero algorithm, then multiply the size of the result by 16. (16 is because L1 charges 16 gas per byte. L1 charges less for bytes that are zero, but that doesn't make sense here.) Brotli-zero is used in order to reward users for posting transactions that are compressible. Ideally we would like to reward for posting transactions that contribute to the compressibility (using the brotli compressor) of the entire batch, but that is a difficult notion to define and in any case would be too expensive to compute at L2. Brotli-zero is an approximation that is cheap enough to compute. + +L1 gas fee funds that are collected from transactions are transferred to a special [`L1PricerFundsPool`][l1pricerfundspool_link] account, so that account's balance represents the amount of funds that have been collected and are available to pay for costs. + +The L1 pricer also records the total number of "data units" (the sum of the estimated sizes, after multiplying by 16) that have been received. + +[l1pricerfundspool_link]: https://github.com/OffchainLabs/nitro/blob/3f4939df1990320310e7f39e8abb32d5c4d8045f/arbos/l1pricing/l1pricing.go#L46 +[two_dimensional_fees_medium_article_link]: https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9 + +## L2 gas pricing The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). -## Estimating Gas +### Estimating L2 Gas Calling an Arbitrum Node's `eth_estimateGas` RPC gives a value sufficient to cover the full transaction fee at the given L2 gas price; i.e., the value returned from `eth_estimateGas` multiplied by the L2 gas price tells you how much total Ether is required for the transaction to succeed. Note that this means that for a given operation, the value returned by `eth_estimateGas` will change over time (as the L1 calldata price fluctuates.) (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and [How to estimate gas in Arbitrum](/build-decentralized-apps/02-how-to-estimate-gas.mdx) for more.) -## Tips in L2 +### L2 gas fees + +L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. + +The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. + +The algorithm compares gas usage against a parameter called the [speed limit](#the-speed-limit) which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. + +Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. + +To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. + +### L2 Tips The sequencer prioritizes transactions on a first-come first-served basis. Because tips do not make sense in this model, they are ignored. Arbitrum users always just pay the basefee regardless of the tip they choose. -## Gas Estimating Retryables +### Gas Estimating Retryables When a transaction schedules another, the subsequent transaction's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A transaction's gas estimate, then, can only be found if all the transactions succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. @@ -40,7 +95,43 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu [estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/d52739e6d54f2ea06146fdc44947af3488b89082/internal/ethapi/api.go#L999 +### The Speed Limit + +The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. + +This sets an effective speed limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. + +Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. + +## Total fee and gas estimation + +The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. + +### Allocating funds and paying what is owed + +When a batch posting report is processed at L2, the pricer allocates some of the collected funds to pay for costs incurred. To allocate funds, the pricer considers three timestamps: + +- `currentTime` is the current time, when the batch posting report message arrives at L2 +- `updateTime` is the time at which the reported batch was submitted (which will typically be around 20 minutes before currentTime) +- `lastUpdateTime` is the time at which the previous reported batch was submitted + +The pricer computes an allocation fraction `F = (updateTime-lastUpdateTime) / (currentTime-lastUpdateTime)` and allocates a fraction `F` of funds in the `L1PricerFundsPool` to the current report. The intuition is that the pricer knows how many funds have been collected between `lastUpdateTime` and `currentTime`, and we want to figure out how many of those funds to allocate to the interval between `lastUpdateTime` and `updateTime`. The given formula is the correct allocation, if we assume that funds arrived at a uniform rate during the interval between `lastUpdateTime` and `currentTime`. The pricer similarly allocates a portion of the total data units to the current report. + +Now the pricer pays out the allocated funds to cover the rewards due and the amounts due to batch posters, reducing the balance due to each party as a result. If the allocated funds aren't sufficient to cover everything that is due, some amount due will remain. If all of the amount due can be covered with the allocated funds, any remaining allocated funds are returned to the `L1PricerFundsPool`. + +### Getting L1 fee info +The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo). To estimate the L1 fee a transaction will use, the [NodeInterface.gasEstimateComponents()](/build-decentralized-apps/nodeinterface/02-reference.mdx) or [NodeInterface.gasEstimateL1Component()](/build-decentralized-apps/nodeinterface/02-reference.mdx) method can be used. + +Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. + +### Adjusting the L1 gas basefee + +After allocating funds and paying what is owed, the L1 Pricer adjusts the L1 Gas Basefee. The goal of this process is to find a value that will cause the amount collected to equal the amount owed over time. + +The algorithm first computes the surplus (funds in the `L1PricerFundsPool`, minus total funds due), which might be negative. If the surplus is positive, the L1 Gas Basefee is reduced, so that the amount collected over a fixed future interval will be reduced by exactly the surplus. If the surplus is negative, the Basefee is increased so that the shortfall will be eliminated over the same fixed future interval. + +A second term is added to the L1 Gas Basefee, based on the derivative of the surplus (surplus at present, minus the surplus after the previous batch posting report was processed). This term, which is multiplied by a smoothing factor to reduce fluctuations, will reduce the Basefee if the surplus is increasing, and increase the Basefee if the surplus is shrinking. \ No newline at end of file +Arbitrum transaction receipts include a `gasUsedForL1` field, showing the amount of gas used on L1 in units of L2 gas. --> diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index ff1fcb6da..d2efa4d1a 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -7,12 +7,11 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -In the [Transaction Lifecycle](/how-arbitrum-works/transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. +In the [Transaction Lifecycle](/how-arbitrum-works/transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). -In this section, we will discuss how contracts interact between L1 and L2, how an L1 contract is called an L2 contract, and vice versa. - +In this section, we will discuss how contracts interact between L1 and L2, how an L1 contract is called an L2 contract, and vice versa. ## Retryable Tickets @@ -82,7 +81,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - ### Automatic Redemption 4. It is very important to note that the submission of a ticket on L1 is separable / asynchronous from its execution on L2, i.e., a successful L1 ticket creation does not guarantee a successful redemption. Once the ticket is successfully created, the two following conditions are checked: (1) if the user's L2 balance is greater than (or equal to) `maxFeePerGas * gasLimit` **and** (2) if the `maxFeePerGas` (provided by the user in the ticket submission process) is greater than (or equal to) the `l2Basefee`. If these conditions are both met, ticket's submission is followed by an attempt to execute it on L2 (i.e., an **auto-redeem** using the supplied gas, as if the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile had been called). Depending on how much gas the sender has provided in step 1, ticket's redemption can either (1) immediately succeed or (2) fail. We explain both situations here: @@ -123,7 +121,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - ### Manual Redemption 5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. @@ -167,7 +164,6 @@ Here we walk through the different stages of the lifecycle of a retryable ticket - :::caution Avoid Losing Funds! If a ticket expires after 7 days without being redeemed or re-scheduled to a future date, any message and value (other than the escrowed `callvalue`) it carries could be lost without possibility of being recovered. @@ -244,4 +240,4 @@ modifier onlyFromMyL1Contract() override { The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. -These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). \ No newline at end of file +These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index 7aad30cc7..c9c1897a8 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -26,4 +26,4 @@ Unlike Retryables, which have an option to provide Ether for automatic L2 execut Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) -\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. \ No newline at end of file +\*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx index d8d6a3526..1669c1944 100644 --- a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx +++ b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx @@ -30,4 +30,4 @@ The only delay that users experience during a dispute is of their [L2 to L1 mess ### Detailed Spec -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). \ No newline at end of file +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). From 101a7c2e57a1a0a0943168fbbb21525ea3a11318 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 12 Dec 2024 15:30:29 -0600 Subject: [PATCH 26/75] resolving broken links --- .../01-quickstart-solidity-hardhat.mdx | 2 +- .../02-how-to-estimate-gas.mdx | 10 +- .../03-public-chains.mdx | 8 +- .../04-cross-chain-messaging.mdx | 4 +- .../01-comparison-overview.mdx | 6 +- .../02-block-numbers-and-time.mdx | 2 +- .../arbitrum-vs-ethereum/03-rpc-methods.mdx | 10 +- .../04-solidity-support.mdx | 2 +- .../precompiles/02-reference.mdx | 2 +- .../reference/08-mainnet-risks.mdx | 2 +- .../token-bridging/02-token-bridge-ether.mdx | 2 +- .../token-bridging/03-token-bridge-erc20.mdx | 4 +- .../02-how-to-bridge-tokens-standard.mdx | 2 +- .../01-a-gentle-introduction.mdx | 2 +- .../02-transaction-lifecycle.mdx | 26 ++--- .../how-arbitrum-works/03-sequencer.mdx | 4 +- .../04-geth-at-the-core.mdx | 12 +- .../06-optimistic-rollup.mdx | 2 +- .../07-interactive-fraud-proofs.mdx | 8 +- .../how-arbitrum-works/09-gas-fees.mdx | 107 +----------------- .../10-l1-to-l2-messaging.mdx | 4 +- .../11-l2-to-l1-messaging.mdx | 4 +- .../how-arbitrum-works/assertion-tree.mdx | 6 +- .../bold/gentle-introduction.mdx | 6 +- .../concepts/custom-gas-token-sdk.md | 2 +- .../how-tos/manage-fee-collectors.md | 4 +- .../how-tos/orbit-chain-finality.md | 6 +- .../orbit-sdk-deploying-token-bridge.md | 2 +- .../how-tos/use-a-custom-gas-token.mdx | 2 +- .../orbit-sdk-introduction.md | 12 +- .../monitoring-tools-and-considerations.mdx | 2 +- .../partials/_gentle-intro-partial.mdx | 4 +- .../arbos-releases/01-overview.mdx | 2 +- .../arbos-releases/arbos11.mdx | 2 +- .../01-get-started.mdx | 2 +- .../02-deploy-das.mdx | 4 +- .../04-configure-dac.mdx | 4 +- arbitrum-docs/stylus/concepts/how-it-works.md | 4 +- arbitrum-docs/welcome/get-started.mdx | 26 ++--- .../archive}/arbos/introduction.mdx | 0 40 files changed, 105 insertions(+), 210 deletions(-) rename {arbitrum-docs/how-arbitrum-works => website/archive}/arbos/introduction.mdx (100%) diff --git a/arbitrum-docs/build-decentralized-apps/01-quickstart-solidity-hardhat.mdx b/arbitrum-docs/build-decentralized-apps/01-quickstart-solidity-hardhat.mdx index 984e56a4d..7b275eaf4 100644 --- a/arbitrum-docs/build-decentralized-apps/01-quickstart-solidity-hardhat.mdx +++ b/arbitrum-docs/build-decentralized-apps/01-quickstart-solidity-hardhat.mdx @@ -456,7 +456,7 @@ Select `Arbitrum Sepolia` from Metamask's dropdown, paste your contract address Now that we've verified that our smart contract works on Arbitrum's Sepolia testnet, we're ready to deploy it to Arbitrum One Mainnet. This is the same process as deploying to Arbitrum's Sepolia testnet, except that we'll need to pay a transaction fee in real $ETH instead of $ASPL. -Expect to see inconsistent $ETH gas fees in this step - the [Gas and fees section](/how-arbitrum-works/gas-fees.mdx) contains more information about how gas fees are determined for Arbitrum transactions. +Expect to see inconsistent $ETH gas fees in this step - the [Gas and fees section](/how-arbitrum-works/09-gas-fees.mdx) contains more information about how gas fees are determined for Arbitrum transactions. diff --git a/arbitrum-docs/build-decentralized-apps/02-how-to-estimate-gas.mdx b/arbitrum-docs/build-decentralized-apps/02-how-to-estimate-gas.mdx index 1e449f6e5..aac9eaf00 100644 --- a/arbitrum-docs/build-decentralized-apps/02-how-to-estimate-gas.mdx +++ b/arbitrum-docs/build-decentralized-apps/02-how-to-estimate-gas.mdx @@ -11,7 +11,7 @@ Head over to [the Stylus gas docs](/stylus/reference/opcode-hostio-pricing) for ::: -This how-to is intended for users and developers interested in understanding how gas operates in Arbitrum, how it's calculated, and how to estimate it before submitting transactions. More detailed information about these calculations can be found in this [Medium article](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and the [Gas and Fees](/how-arbitrum-works/gas-fees.mdx) page. +This how-to is intended for users and developers interested in understanding how gas operates in Arbitrum, how it's calculated, and how to estimate it before submitting transactions. More detailed information about these calculations can be found in this [Medium article](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) and the [Gas and Fees](/how-arbitrum-works/09-gas-fees.mdx) page. ## Skip the formula, focus on practical know-how @@ -21,7 +21,7 @@ Multiplying the value obtained from `eth_estimateGas` by the L2 gas price will g Alternatively, to obtain the gas limit for your transaction, you can call `NodeInterface.gasEstimateComponents()` and then use the first result, which is `gasEstimate`. Next, to find the total cost, you need to multiply this amount by the L2 gas price, which is available in the third result, `baseFee`. -Note that when working with L1 to L2 messages (also known as [retryable tickets](/how-arbitrum-works/arbos/l1-l2-messaging.mdx)), you can use the function [L1ToL2MessageGasEstimator.estimateAll()](https://github.com/OffchainLabs/arbitrum-sdk/blob/main/src/lib/message/L1ToL2MessageGasEstimator.ts#L215) of the Arbitrum SDK or [NodeInterface.estimateRetryableTicket()](https://github.com/OffchainLabs/@nitroRepositorySlug@/blob/@nitroVersionTag@/nodeInterface/NodeInterface.go#L120) to get all the gas information needed to send a successful transaction. +Note that when working with L1 to L2 messages (also known as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx)), you can use the function [L1ToL2MessageGasEstimator.estimateAll()](https://github.com/OffchainLabs/arbitrum-sdk/blob/main/src/lib/message/L1ToL2MessageGasEstimator.ts#L215) of the Arbitrum SDK or [NodeInterface.estimateRetryableTicket()](https://github.com/OffchainLabs/@nitroRepositorySlug@/blob/@nitroVersionTag@/nodeInterface/NodeInterface.go#L120) to get all the gas information needed to send a successful transaction. ## Breaking down the formula @@ -35,7 +35,7 @@ As explained in the Medium article, the transaction fees to pay at any given mom Transaction fees (TXFEES) = L2 Gas Price (P) * Gas Limit (G) ``` -This Gas Limit includes the gas of the L2 computation and an additional buffer to cover the L1 gas to be paid by the Sequencer when [posting the batch including this transaction on L1](/how-arbitrum-works/inside-arbitrum-nitro.mdx#how-the-sequencer-publishes-the-sequence). +This Gas Limit includes the gas of the L2 computation and an additional buffer to cover the L1 gas to be paid by the Sequencer when [posting the batch including this transaction on L1](/how-arbitrum-works/03-sequencer.mdx). ``` Gas Limit (G) = Gas used on L2 (L2G) + Extra Buffer for L1 cost (B) @@ -46,7 +46,7 @@ This buffer takes into account the cost of posting the transaction, batched and - L1S, which estimates the amount of data the transaction will take up in the batch by compressing the transaction with Brotli. - L1P, which is the L2's estimated view of the current L1's price of data (per byte), which the L2 dynamically adjusts over time. -More information is available [in this page](/how-arbitrum-works/l1-gas-pricing.mdx). +More information is available [in this page](/how-arbitrum-works/09-gas-fees.mdx). ``` L1 Estimated Cost (L1C) = L1 price per byte of data (L1P) * Size of data to be posted in bytes (L1S) @@ -75,7 +75,7 @@ We'll use one resource available in Arbitrum: the [NodeInterface](/build-decentr - L1P (L1 estimated price per byte of data) ⇒ Estimated cost of posting 1 byte of data on L1: - Call `NodeInterface.GasEstimateComponents()`, get the fourth element `l1BaseFeeEstimate` and multiply it by 16. - L1S (Size of data to be posted on L1, in bytes) ⇒ This will depend on the data of the transaction. Keep in mind that Arbitrum adds a fixed amount to this number to make up for the static part of the transaction, which is also posted on L1 (140 bytes). We can do a small calculation to obtain this value: call `NodeInterface.GasEstimateComponents()` take the second element, `gasEstimateForL1` (this is equivalent to `B` in our formula), multiply it by P and divide it by L1P. - - For Arbitrum Nova (AnyTrust), the size of the data is also a fixed value, as only the Data Availability Certificate is posted on L1, [as explained here](/how-arbitrum-works/inside-anytrust.mdx#data-availability-certificates). + - For Arbitrum Nova (AnyTrust), the size of the data is also a fixed value, as only the Data Availability Certificate is posted on L1, [as explained here](/how-arbitrum-works/08-anytrust-protocol.mdx#data-availability-certificates). (Note: for L1P and L1S, you can also call `NodeInterface.gasEstimateL1Component()` to get `l1BaseFeeEstimate` and `gasEstimateForL1`) diff --git a/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx b/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx index cc2925b0f..c54b86550 100644 --- a/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx +++ b/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx @@ -13,11 +13,11 @@ Arbitrum chains are Layer 2 solutions built on top of the Ethereum blockchain, d ### Arbitrum One -**Arbitrum One** is a Layer 2 (L2) optimistic rollup chain that implements the Arbitrum Rollup protocol and settles to Ethereum's Layer 1 (L1) chain. It lets you build high-performance Ethereum dApps with low transaction costs and Ethereum-grade security guarantees, introducing no additional trust assumptions. This is made possible by the [Nitro](/how-arbitrum-works/inside-arbitrum-nitro.mdx) technology stack, a "Geth-at-the-core" architecture that gives Arbitrum One (and Nova) advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. +**Arbitrum One** is a Layer 2 (L2) optimistic rollup chain that implements the Arbitrum Rollup protocol and settles to Ethereum's Layer 1 (L1) chain. It lets you build high-performance Ethereum dApps with low transaction costs and Ethereum-grade security guarantees, introducing no additional trust assumptions. This is made possible by the [Nitro](/how-arbitrum-works/04-geth-at-the-core.mdx) technology stack, a "Geth-at-the-core" architecture that gives Arbitrum One (and Nova) advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. ### Arbitrum Nova -**Arbitrum Nova** is a high-performance alternative to Arbitrum One's chain. While Arbitrum One implements the purely trustless Rollup protocol, Arbitrum Nova implements the mostly trustless [AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) protocol. They key difference between Rollup and AnyTrust is that the AnyTrust protocol introduces an additional trust assumption in the form of a data availability committee (DAC). This committee (detailed below) is responsible for expediting the process of storing, batching, and posting L2 transaction data to Ethereum's L1. This lets you use Arbitrum in scenarios that demand performance and affordability, while Arbitrum One is optimal for scenarios that demand Ethereum's pure trustlessness. +**Arbitrum Nova** is a high-performance alternative to Arbitrum One's chain. While Arbitrum One implements the purely trustless Rollup protocol, Arbitrum Nova implements the mostly trustless [AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx) protocol. They key difference between Rollup and AnyTrust is that the AnyTrust protocol introduces an additional trust assumption in the form of a data availability committee (DAC). This committee (detailed below) is responsible for expediting the process of storing, batching, and posting L2 transaction data to Ethereum's L1. This lets you use Arbitrum in scenarios that demand performance and affordability, while Arbitrum One is optimal for scenarios that demand Ethereum's pure trustlessness. ## What Arbitrum testnet chains are available? @@ -57,11 +57,11 @@ Finally, Arbitrum Sepolia is a testnet chain. It's designed for testing purposes ### Nitro -Nitro is the technology that powers Arbitrum One, Arbitrum Nova (with AnyTrust configuration),and Arbitrum Sepolia. It's designed to offer high throughput and low cost, making it ideal for scaling Ethereum applications. Nitro is a major upgrade to the “Classic” stack, offering several improvements including advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. You can find more information about Nitro in [Inside Arbitrum Nitro](/how-arbitrum-works/inside-arbitrum-nitro.mdx). +Nitro is the technology that powers Arbitrum One, Arbitrum Nova (with AnyTrust configuration),and Arbitrum Sepolia. It's designed to offer high throughput and low cost, making it ideal for scaling Ethereum applications. Nitro is a major upgrade to the “Classic” stack, offering several improvements including advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. You can find more information about Nitro in [Inside Arbitrum Nitro](/how-arbitrum-works/01-a-gentle-introduction.mdx). ### AnyTrust (variant of Nitro) -AnyTrust is a variant of the Nitro technology stack that lowers costs by accepting a mild trust assumption. The AnyTrust protocol relies on an external Data Availability Committee (DAC) to store data and provide it on demand. The DAC has N members, of which AnyTrust assumes at least two are honest. Keeping the data off-chain in the happy/common case means the system can charge the user significantly lower fees. You can find more information about AnyTrust in [Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx). +AnyTrust is a variant of the Nitro technology stack that lowers costs by accepting a mild trust assumption. The AnyTrust protocol relies on an external Data Availability Committee (DAC) to store data and provide it on demand. The DAC has N members, of which AnyTrust assumes at least two are honest. Keeping the data off-chain in the happy/common case means the system can charge the user significantly lower fees. You can find more information about AnyTrust in [Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx). ### Classic (deprecated) diff --git a/arbitrum-docs/build-decentralized-apps/04-cross-chain-messaging.mdx b/arbitrum-docs/build-decentralized-apps/04-cross-chain-messaging.mdx index 03e3b72a5..2100226da 100644 --- a/arbitrum-docs/build-decentralized-apps/04-cross-chain-messaging.mdx +++ b/arbitrum-docs/build-decentralized-apps/04-cross-chain-messaging.mdx @@ -11,7 +11,7 @@ The Arbitrum protocol and related tooling makes it easy for developers to build Arbitrary L1 to L2 contract calls can be created via the `Inbox`'s `createRetryableTicket` method; upon publishing the L1 transaction, the L2 side will typically get included within minutes. Happily / commonly, the L2 execution will automatically succeed, but if reverts, and it can be rexecuted via a call to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile. -For details and protocol specification, see [L1 to L2 Messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). +For details and protocol specification, see [L1 to L2 Messages](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). For an example of retryable tickets in action, see the [Greeter](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/greeter) tutorial, which uses the [Arbitrum SDK](../sdk/1-introduction.mdx). @@ -19,6 +19,6 @@ For an example of retryable tickets in action, see the [Greeter](https://github. Similarly, L2 contracts can send Arbitrary messages for execution on L1. These are initiated via calls to the [ArbSys](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Upon confirmation (about 1 week later), they can executed by retrieving the relevant data via a call to `NodeInterface` contract's `constructOutboxProof` method, and then executing them via the `Outbox`'s `executeTransaction` method. -For details and protocol specification, see [L2 to L1 Messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx). +For details and protocol specification, see [L2 to L1 Messages](/how-arbitrum-works/11-l2-to-l1-messaging.mdx). For a demo, see the [Outbox Tutorial](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/outbox-execute). diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index 1b1389eb6..f08f33131 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -26,13 +26,13 @@ You can deploy Solidity contracts onto Arbitrum just like you do Ethereum. There ## Fees -The fees an Arbitrum transaction pays for execution essentially work identically to gas fees on Ethereum. Arbitrum transactions must also, however, pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitrum-works/gas-fees.mdx) and [L1 pricing](/how-arbitrum-works/l1-gas-pricing.mdx). +The fees an Arbitrum transaction pays for execution essentially work identically to gas fees on Ethereum. Arbitrum transactions must also, however, pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitrum-works/09-gas-fees.mdx) and L1 pricing. ## Cross-chain messaging -Arbitrum chains support arbitrary message passing from a parent chain (for example, a Layer 1 (L1) like Ethereum) to a child chain (for example, a Layer 2 (L2) like Arbitrum One or Arbitrum Nova). These are commonly known as "L1 to L2 messages". Developers using this functionality should familiarize themselves with how they work. Find more information about it in [L1 to L2 messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). +Arbitrum chains support arbitrary message passing from a parent chain (for example, a Layer 1 (L1) like Ethereum) to a child chain (for example, a Layer 2 (L2) like Arbitrum One or Arbitrum Nova). These are commonly known as "L1 to L2 messages". Developers using this functionality should familiarize themselves with how they work. Find more information about it in [L1 to L2 messaging](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). -Similarly, Arbitrum chains can also send messages to the parent chain. Find more information about them in [L2 to L1 messaging and the outbox](/how-arbitrum-works/arbos/l2-l1-messaging.mdx). +Similarly, Arbitrum chains can also send messages to the parent chain. Find more information about them in [L2 to L1 messaging and the outbox](/how-arbitrum-works/11-l2-to-l1-messaging.mdx). ## Precompiles diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 00b92cc6d..88220f9e0 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -13,7 +13,7 @@ With the release of Arbitrum Orbit, Arbitrum chains can now be L2s that settle t ::: -As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions at some later time. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/sequencer.mdx#happycommon-case-sequencer-is-live-and-well-behaved) or via the chain's [delayed inbox](/how-arbitrum-works/sequencer.mdx#unhappyuncommon-case-sequencer-isnt-doing-its-job). +As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions at some later time. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/03-sequencer.mdx#happycommon-case-sequencer-is-live-and-well-behaved) or via the chain's [delayed inbox](/how-arbitrum-works/03-sequencer.mdx#unhappyuncommon-case-sequencer-isnt-doing-its-job). Once in the chain's core inbox contract, transactions are processed in order. Generally, some time will elapse between when a message is put into the inbox (and timestamped) and when the contract processes the message and carries out the transaction requested by the message. diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx index 5f3923793..9d2df5565 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx @@ -21,7 +21,7 @@ When calling [`eth_getTransactionByHash`](https://ethereum.org/en/developers/doc ### Transaction types -In addition to the [three transaction types](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) currently supported on Ethereum, Arbitrum adds additional types listed below and [documented in full detail here](/how-arbitrum-works/arbos/geth.mdx#transaction-types). +In addition to the [three transaction types](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) currently supported on Ethereum, Arbitrum adds additional types listed below and [documented in full detail here](/how-arbitrum-works/04-geth-at-the-core.mdx#transaction-types). On RPC calls that return transactions, the `type` field will reflect the custom codes where applicable. @@ -30,8 +30,8 @@ On RPC calls that return transactions, the `type` field will reflect the custom | `100` | `ArbitrumDepositTxType` | Used to deposit ETH from L1 to L2 via the Arbitrum bridge | | `101` | `ArbitrumUnsignedTxType` | Used to call an L2 contract from L1, originated by a user through the Arbitrum bridge | | `102` | `ArbitrumContractTxType` | Used to call an L2 contract from L1, originated by a contract through the Arbitrum bridge | -| `104` | `ArbitrumRetryTxType` | Used to [manually redeem a retryable ticket](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) on L2 that failed to execute automatically (usually due to low gas) | -| `105` | `ArbitrumSubmitRetryableTxType` | Used to [submit a retryable ticket](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#submission) via the Arbitrum bridge on L1 | +| `104` | `ArbitrumRetryTxType` | Used to [manually redeem a retryable ticket](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) on L2 that failed to execute automatically (usually due to low gas) | +| `105` | `ArbitrumSubmitRetryableTxType` | Used to [submit a retryable ticket](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#submission) via the Arbitrum bridge on L1 | | `106` | `ArbitrumInternalTxType` | Internal transactions created by the ArbOS itself for certain state updates, like the L1 base fee and the block number | ### Additional fields @@ -48,7 +48,7 @@ On RPC calls that return transactions, the following fields will have a differen | Field name | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `from` | On L1 to L2 transactions, this field will contain the [_aliased_ version](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#address-aliasing) of the L1's `msg.sender` | +| `from` | On L1 to L2 transactions, this field will contain the [_aliased_ version](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#address-aliasing) of the L1's `msg.sender` | ## Transaction receipts @@ -61,7 +61,7 @@ On RPC calls that return transaction receipts, the following fields are added to | Field name | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `l1BlockNumber` | The L1 block number that would be used for `block.number` calls. More information in [Block numbers and time](/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx) | -| `gasUsedForL1` | Amount of gas spent on L1 calldata in units of L2 gas. More information in [Gas and fees](/how-arbitrum-works/gas-fees.mdx) | +| `gasUsedForL1` | Amount of gas spent on L1 calldata in units of L2 gas. More information in [Gas and fees](/how-arbitrum-works/09-gas-fees.mdx) | ## Blocks diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx index 8730e9eba..3dc50fd2b 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx @@ -22,5 +22,5 @@ Although Arbitrum supports Solidity code, there are differences in the effects o | `block.difficulty` | Returns the constant 1. | | `block.prevrandao` | Returns the constant 1. | | `block.number` | Returns an "estimate" of the L1 block number at which the sequencer received the transaction. For more information, see [Block numbers and time](/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx). | -| `msg.sender` | Works the same way it does on Ethereum for regular L2 to L2 transactions. For transactions submitted via the delayed inbox, it will return the L2 address alias of the L1 contract that triggered the message. For more information, see [address aliasing](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#address-aliasing). | +| `msg.sender` | Works the same way it does on Ethereum for regular L2 to L2 transactions. For transactions submitted via the delayed inbox, it will return the L2 address alias of the L1 contract that triggered the message. For more information, see [address aliasing](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#address-aliasing). | | OPCODE `PUSH0` | This OPCODE was added as part of ArbOS 11 and is now supported. | diff --git a/arbitrum-docs/build-decentralized-apps/precompiles/02-reference.mdx b/arbitrum-docs/build-decentralized-apps/precompiles/02-reference.mdx index 37edc16a3..321ec6202 100644 --- a/arbitrum-docs/build-decentralized-apps/precompiles/02-reference.mdx +++ b/arbitrum-docs/build-decentralized-apps/precompiles/02-reference.mdx @@ -179,7 +179,7 @@ import ArbOwnerPublicRef from '../../for-devs/dev-tools-and-resources/partials/p ### ArbRetryableTx -ArbRetryableTx ([Interface][arbretryabletx_link_interface] | [Implementation][arbretryabletx_link_implementation]) provides methods for managing retryables. The model has been adjusted for Nitro, most notably in terms of how retry transactions are scheduled. For more information on retryables, please see [the retryable documentation](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets). +ArbRetryableTx ([Interface][arbretryabletx_link_interface] | [Implementation][arbretryabletx_link_implementation]) provides methods for managing retryables. The model has been adjusted for Nitro, most notably in terms of how retry transactions are scheduled. For more information on retryables, please see [the retryable documentation](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets). Precompile address: `0x000000000000000000000000000000000000006E` diff --git a/arbitrum-docs/build-decentralized-apps/reference/08-mainnet-risks.mdx b/arbitrum-docs/build-decentralized-apps/reference/08-mainnet-risks.mdx index abe6452a1..69463cd14 100644 --- a/arbitrum-docs/build-decentralized-apps/reference/08-mainnet-risks.mdx +++ b/arbitrum-docs/build-decentralized-apps/reference/08-mainnet-risks.mdx @@ -6,7 +6,7 @@ author: dzgoldman # Arbitrum: Understanding the risks -Arbitrum One — the first permissionless Ethereum layer 2 rollup with full Ethereum smart contract functionality — is [live on mainnet](https://offchain.medium.com/mainnet-for-everyone-27ce0f67c85e) — as is [Nova](https://medium.com/offchainlabs/its-time-for-a-new-dawn-nova-is-open-to-the-public-a081df1e4ad2), our first [AnyTrust chain](/how-arbitrum-works/inside-anytrust.mdx); We're sure you're (almost) as excited as we are! +Arbitrum One — the first permissionless Ethereum layer 2 rollup with full Ethereum smart contract functionality — is [live on mainnet](https://offchain.medium.com/mainnet-for-everyone-27ce0f67c85e) — as is [Nova](https://medium.com/offchainlabs/its-time-for-a-new-dawn-nova-is-open-to-the-public-a081df1e4ad2), our first [AnyTrust chain](/how-arbitrum-works/08-anytrust-protocol.mdx); We're sure you're (almost) as excited as we are! Here are some risks you should know about before using the system: ### State Of progressive decentralization diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx index a17c9c0d5..daf80d289 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx @@ -36,7 +36,7 @@ ArbSys(100).withdrawEth{ value: 2300000 }(destAddress) Upon withdrawing, the Ether balance is burnt on the Arbitrum side, and will later be made available on the Ethereum side. -`ArbSys.withdrawEth` is actually a convenience function which is equivalent to calling `ArbSys.sendTxToL1` with empty calldataForL1. Like any other `sendTxToL1` call, it will require an additional call to `Outbox.executeTransaction` on L1 after the dispute period elapses for the user to finalize claiming their funds on L1 (see ["L2 to L1 Messages"](/how-arbitrum-works/arbos/l2-l1-messaging.mdx)). Once the withdrawal is executed from the Outbox, the user's Ether balance will be credited on L1. +`ArbSys.withdrawEth` is actually a convenience function which is equivalent to calling `ArbSys.sendTxToL1` with empty calldataForL1. Like any other `sendTxToL1` call, it will require an additional call to `Outbox.executeTransaction` on L1 after the dispute period elapses for the user to finalize claiming their funds on L1 (see ["L2 to L1 Messages"](/how-arbitrum-works/11-l2-to-l1-messaging.mdx)). Once the withdrawal is executed from the Outbox, the user's Ether balance will be credited on L1. The following diagram depicts the process that funds follow during a withdraw operation. diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx index 4cd915dc0..9596a59e9 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx @@ -44,11 +44,11 @@ Our architecture consists of three types of contracts: ![img](./assets/gatewayUML.svg) -All Ethereum to Arbitrum token transfers are initiated via the router contract on L1, the `L1GatewayRouter` contract. `L1GatewayRouter` forwards the token's deposit call to the appropriate gateway contract on L1, the `L1ArbitrumGateway` contract. `L1GatewayRouter` is responsible for mapping L1 token addresses to L1Gateway contracts, thus acting as an L1/L2 address oracle, and ensuring that each token corresponds to only one gateway. The `L1ArbitrumGateway` then communicates to its counterpart gateway contract on L2, the `L2ArbitrumGateway` contract (typically/expectedly via [retryable tickets](/how-arbitrum-works/arbos/l1-l2-messaging.mdx)). +All Ethereum to Arbitrum token transfers are initiated via the router contract on L1, the `L1GatewayRouter` contract. `L1GatewayRouter` forwards the token's deposit call to the appropriate gateway contract on L1, the `L1ArbitrumGateway` contract. `L1GatewayRouter` is responsible for mapping L1 token addresses to L1Gateway contracts, thus acting as an L1/L2 address oracle, and ensuring that each token corresponds to only one gateway. The `L1ArbitrumGateway` then communicates to its counterpart gateway contract on L2, the `L2ArbitrumGateway` contract (typically/expectedly via [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx)). ![img](./assets/bridge_deposits.png) -Similarly, Arbitrum to Ethereum transfers are initiated via the router contract on L2, the `L2GatewayRouter` contract, which calls the token's gateway contract on L2, the `L2ArbitrumGateway` contract, which in turn communicates to its corresponding gateway contract on L1, the `L1ArbitrumGateway` contract (typically/expectedly via [sending L2-to-L1 messages to the outbox](/how-arbitrum-works/arbos/l2-l1-messaging.mdx)). +Similarly, Arbitrum to Ethereum transfers are initiated via the router contract on L2, the `L2GatewayRouter` contract, which calls the token's gateway contract on L2, the `L2ArbitrumGateway` contract, which in turn communicates to its corresponding gateway contract on L1, the `L1ArbitrumGateway` contract (typically/expectedly via [sending L2-to-L1 messages to the outbox](/how-arbitrum-works/11-l2-to-l1-messaging.mdx)). ![img](./assets/bridge_withdrawals.png) diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx index f54a4a12e..bda6dde3d 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx @@ -172,7 +172,7 @@ As mentioned before, you can also call the method `outboundTransferCustomRefund` ## Step 5: Wait for execution on L2 -After calling the deposit method (or the `outboundTransferCustomRefund`, if you’re choosing the manual way), you’ll have to wait a bit until the message is executed on L2. We will verify the status of the underlying retryable ticket created to bridge the tokens. Check this page, to know more about [L1-to-L2 messages, also known as retryables](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). +After calling the deposit method (or the `outboundTransferCustomRefund`, if you’re choosing the manual way), you’ll have to wait a bit until the message is executed on L2. We will verify the status of the underlying retryable ticket created to bridge the tokens. Check this page, to know more about [L1-to-L2 messages, also known as retryables](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). You can programmatically wait for the execution of the transaction on L2 using Arbitrum’s SDK. You should first wait for the execution of the submission transaction (the one sent to the router contract) and then the execution of the L2 transaction. diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 1c9153861..9cb237a09 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -11,7 +11,7 @@ import ImageWithCaption from '@site/src/components/ImageCaptions/'; This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. -The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called [AnyTrust](/how-arbitrum-works/anytrust-protocol.mdx), which is used by the Arbitrum Nova chain. +The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called [AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx), which is used by the Arbitrum Nova chain. ## Why use Arbitrum? Why use Nitro? diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 742653084..f5ecb72dd 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -29,7 +29,7 @@ We'll now go step-by-step over the phases an Arbitrum transaction goes through, We'll also intersperse it with "finality checks," explaining what guarantees the client has over their transaction's finality (i.e., assurances that their transaction's result is guaranteed and won't later be altered) over the course of a transaction's various stages. -This section will focus on the Arbitrum Rollup protocol; see [Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the security properties of the core system itself; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization) for current decentralization status. +This section will focus on the Arbitrum Rollup protocol; see [Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the security properties of the core system itself; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization) for current decentralization status. For clarity on any terminology that may be unfamiliar, see our [glossary](/intro/glossary.mdx). @@ -47,8 +47,8 @@ Alternatively, a client can send a message to the Sequencer by signing and publi **See**: -- [Retryables](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) -- [The Sequencer](/how-arbitrum-works/sequencer.mdx) +- [Retryables](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) +- [The Sequencer](/how-arbitrum-works/03-sequencer.mdx) - [Token Bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) ### 2. Sequencer orders transaction (off-chain) @@ -61,9 +61,9 @@ Upon receiving a transaction, the Sequencer will: **See**: -- [ArbOS](/how-arbitrum-works/arbos/introduction.mdx) -- [Geth](/how-arbitrum-works/arbos/geth.mdx) -- [L1 pricing](/how-arbitrum-works/l1-gas-pricing.mdx) / [L2 Gas](/how-arbitrum-works/gas-fees.mdx) +- [ArbOS](/how-arbitrum-works/04-geth-at-the-core#arbos.mdx) +- [Geth](/how-arbitrum-works/04-geth-at-the-core#geth.mdx) +- [L1 pricing](/how-arbitrum-works/09-gas-fees.mdx) / [L2 Gas](/how-arbitrum-works/09-gas-fees.mdx) #### ~ ~ ~ FINALITY CHECK: Trusted / Soft Confirmation ~ ~ ~ @@ -91,7 +91,7 @@ The Sequencer is forced to include messages from the delayed Inbox in the queued **See:** -- ["The Sequencer / Censorship Resistance."](/how-arbitrum-works/sequencer.mdx) +- ["The Sequencer / Censorship Resistance."](/how-arbitrum-works/03-sequencer.mdx) #### ~ ~ ~ FINALITY CHECK: Ethereum-Equivalent Finality! ~ ~ ~ @@ -110,9 +110,9 @@ A staked, active validator will then run the Arbitrum VM over the inputs in the **See**: -- [ArbOS](/how-arbitrum-works/arbos/introduction.mdx) -- [Geth](/how-arbitrum-works/arbos/geth.mdx) -- [L1 pricing](/how-arbitrum-works/l1-gas-pricing.mdx) / [L2 Gas](/how-arbitrum-works/gas-fees.mdx) +- [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos) +- [Geth](/how-arbitrum-works/04-geth-at-the-core.mdx#geth) +- [L1 pricing](/how-arbitrum-works/09-gas-fees.mdx) / [L2 Gas](/how-arbitrum-works/09-gas-fees.mdx) :::note @@ -122,7 +122,7 @@ RBlock assertions include claims about the state of the Outbox; if our transacti **See**: -- [The Outbox](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) +- [The Outbox](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) #### 4a. RBlock is valid / goes unchallenged @@ -136,8 +136,8 @@ A dispute consists of two staked validators dissecting their disagreement down t **See:** -- [Challenges](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx) -- [Wasm/WAVM](/how-arbitrum-works/fraud-proofs/wasm-wavm.mdx) +- [Challenges](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#challenge-manager) +- [Wasm/WAVM](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#wasm-wavm) L1 contracts also keep track of the tree of all assertions; i.e., how many stakers are in disagreement, who is currently disputing with whom, etc. We refer to this level of Arbitrum's design architecture as its "assertion tree protocol." diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 5bf5725c4..278bdf452 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -17,7 +17,7 @@ Clients interact with the Sequencer in exactly the same way they would interact ## The Core Inbox -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/arbos/transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. ## Happy/Common Case: Sequencer Is Live and Well-behaved @@ -56,7 +56,7 @@ Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) -Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/l1-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. +Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index 5e1e9ac3e..a7974878c 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -21,7 +21,7 @@ Because the top and bottom layers rely heavily on code from geth, this structure The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. -The rest of this section will be a deep dive into Geth and ArbOS. If deep technical knowledge does not suit you, skip to the next section [Separating Execution from Proving](/how-arbitrum-works/separating-execution-from-proving.mdx). +The rest of this section will be a deep dive into Geth and ArbOS. If deep technical knowledge does not suit you, skip to the next section [Separating Execution from Proving](/how-arbitrum-works/05-separating-execution-from-proving.mdx). ## Geth @@ -197,11 +197,11 @@ Represents a user deposit from L1 to L2. This increases the user's balance by th #### [`ArbitrumSubmitRetryableTx`][arbitrumsubmitretryabletx_link]{#ArbitrumSubmitRetryableTx} -Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets) for more info. +Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets) for more info. #### [`ArbitrumRetryTx`][arbitrumretrytx_link]{#ArbitrumRetryTx} -These are scheduled by calls to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile and via retryable auto-redemption. Please see the [retryables documentation](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets) for more info. +These are scheduled by calls to the `redeem` method of the [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile and via retryable auto-redemption. Please see the [retryables documentation](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets) for more info. #### [`ArbitrumInternalTx`][arbitruminternaltx_link]{#ArbitrumInternalTx} @@ -249,7 +249,7 @@ Nitro's Geth may be configured with the following [l2-specific chain parameters] #### `EnableArbos` -Introduces [ArbOS](/how-arbitrum-works/arbos/introduction.mdx), converting what would otherwise be a vanilla L1 chain into an L2 Arbitrum rollup. +Introduces [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos), converting what would otherwise be a vanilla L1 chain into an L2 Arbitrum rollup. #### `AllowDebugPrecompiles` @@ -277,7 +277,7 @@ To aid with [outbox proof construction][proof_link], the root hash and leaf coun #### Retryable Support -Retryables are mostly implemented in [ArbOS](/how-arbitrum-works/arbos/introduction.mdx#retryables). Some modifications were required in Geth to support them. +Retryables are mostly implemented in [ArbOS](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). Some modifications were required in Geth to support them. - Added ScheduledTxes field to ExecutionResult. This lists transactions scheduled during the execution. To enable using this field, we also pass the ExecutionResult to callers of ApplyTransaction. - Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retryable activated by the original call. This allows estimating gas to enable retryables. @@ -352,7 +352,7 @@ An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequence ### Retryables -A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/arbos/l1-l2-messaging.mdx). +A Retryable is a special message type for creating atomic L1 to L2 messages; for details, see [L1 To L2 Messaging](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). ### ArbOS State diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index e7e110704..01a2598ec 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -239,7 +239,7 @@ A consequence of these rules is that once the first unresolved RBlock's deadline Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid RBlock (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. -The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. +The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. ### Detailed Spec diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 0c60a71bd..0181e3bd8 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -1,5 +1,5 @@ --- -title: Challenges: Interactive Fraud Proofs +title: Challenges/:/ Interactive Fraud Proofs description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' author: pete-vielhaber sme: TucksonDev @@ -65,7 +65,7 @@ The challenge protocol is designed so that the dispute can be resolved with a mi The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. -import ChallengeManagerDiagram from '../../diagrams/_challenge-manager.mdx'; +import ChallengeManagerDiagram from '../diagrams/_challenge-manager.mdx'; ## ChallengeManager @@ -211,7 +211,7 @@ which is limited by EIP-170 to about 24KB. Not all WASM instructions are 1:1 with WAVM opcodes. This document lists those which are not, and explains how they're expressed in WAVM. Many of the WAVM representations use opcodes not in WASM, -which are documented in [`wavm-custom-opcodes.mdx`](/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes.mdx). +which are documented in [`wavm-custom-opcodes`](#wavm-custom-opcodes-not-in-wasm). ### `block` and `loop` @@ -281,7 +281,7 @@ which are not part of WASM nor any WASM proposal. Many of these opcodes have implicit invariants about what's on the stack, e.g. "Pops an i32 from the stack" assumes that the top of the stack has an i32. If these conditions are not satisfied, execution is generally not possible. -These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](/how-arbitrum-works/fraud-proofs/osp-assumptions.mdx).) +These invariants are maintained by WASM validation and Arbitrator codegen. (See [One Step Proof Assumptions](#one-step-proof-assumptions).) ### Codegen internal diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 57ebc7055..171f2c6f3 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -18,7 +18,7 @@ The L1 component is the product of the transaction's estimated contribution to i The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. -The following sections will detail how to calculate L1 and L2 fees. If you do not need precise calculations or a technical understanding, skip to the next section, [L1 to L2 messaging](/how-arbitrum-works/l1-to-l2-messaging.mdx). +The following sections will detail how to calculate L1 and L2 fees. If you do not need precise calculations or a technical understanding, skip to the next section, [L1 to L2 messaging](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). ## L1 gas pricing @@ -132,108 +132,3 @@ After allocating funds and paying what is owed, the L1 Pricer adjusts the L1 Gas The algorithm first computes the surplus (funds in the `L1PricerFundsPool`, minus total funds due), which might be negative. If the surplus is positive, the L1 Gas Basefee is reduced, so that the amount collected over a fixed future interval will be reduced by exactly the surplus. If the surplus is negative, the Basefee is increased so that the shortfall will be eliminated over the same fixed future interval. A second term is added to the L1 Gas Basefee, based on the derivative of the surplus (surplus at present, minus the surplus after the previous batch posting report was processed). This term, which is multiplied by a smoothing factor to reduce fluctuations, will reduce the Basefee if the surplus is increasing, and increase the Basefee if the surplus is shrinking. - - diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index d2efa4d1a..850c188fa 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -7,7 +7,7 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -In the [Transaction Lifecycle](/how-arbitrum-works/transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. +In the [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). @@ -209,7 +209,7 @@ In principle, retryable tickets can alternatively be used to deposit Ether; this ## Transacting via the Delayed Inbox -While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. +While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/03-sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. ### Address Aliasing diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index c9c1897a8..a8816a116 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -7,7 +7,7 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. +Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. ### Protocol Flow @@ -24,6 +24,6 @@ An important feature in the design of the Outbox system is that calls to `confir Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. -The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx) +The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/01-a-gentle-introduction.mdx) \*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx index 1669c1944..207b9d75e 100644 --- a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx +++ b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx @@ -16,7 +16,7 @@ If, however, two or more conflicting assertions exist, the Assertion Tree bifurc Crucially, the rules of advancing an Arbitrum chain are deterministic; this means that given a chain state and some new inputs, there is only one valid output. Thus, if the Assertion Tree contains more than one leaf, then at most only one leaf can represent the valid chain-state; if we assume there is at least one honest active validator, _exactly_ one leaf will be valid. -Two conflicting assertions can be put into a dispute; see [Interactive Challenges](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx) for details on the dispute process. For the sake of understanding the Assertion Tree protocol, suffice it to say that 2-party disputes last at most a fixed amount of time (1 week), at the end of which one of the two conflicting assertions will be rejected, and the validator who posted it will lose their stake. +Two conflicting assertions can be put into a dispute; see [Interactive Challenges](/how-arbitrum-works/07-interactive-fraud-proofs.mdx) for details on the dispute process. For the sake of understanding the Assertion Tree protocol, suffice it to say that 2-party disputes last at most a fixed amount of time (1 week), at the end of which one of the two conflicting assertions will be rejected, and the validator who posted it will lose their stake. In order for an assertion to be confirmed and for its stake to be recovered, two conditions must be met: sufficient time for disputes must have passed, and no other conflicting branches in the Assertion Tree can exist (i.e., they've all been disputed / "pruned" off.) @@ -26,8 +26,8 @@ These properties together ensure that as long as at least one honest, active val Even if the Assertion Tree has multiple conflicting leaves and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid leaf (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. -The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. +The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. ### Detailed Spec -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). +For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/06-optimistic-rollup.mdx). diff --git a/arbitrum-docs/how-arbitrum-works/bold/gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/bold/gentle-introduction.mdx index e4c912339..5488e22ef 100644 --- a/arbitrum-docs/how-arbitrum-works/bold/gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/bold/gentle-introduction.mdx @@ -17,7 +17,7 @@ BoLD will eventually replace the current, permissioned fraud proof mechanism tha ## In a nutshell: - Validation for Arbitrum One and Arbitrum Nova is a privileged action currently limited to an [allow-listed set of parties, maintained by the Arbitrum DAO](https://docs.arbitrum.foundation/state-of-progressive-decentralization#allowlisted-validators) to reduce the risks of _[delay attacks](https://medium.com/offchainlabs/solutions-to-delay-attacks-on-rollups-434f9d05a07a)_. _Delay attacks_ are a class of attacks where malicious entities can open as many disputes as they are willing to forfeit bonds during the challenge period to delay confirmations of assertions (equal to the time needed to resolve those disputes one by one). -- BoLD, an acronym for Bounded Liquidity Delay, is a new challenge resolution protocol for Arbitrum chains that enables permissionless validation by mitigating the risks of delay attacks against [optimistic rollups like Arbitrum](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol). This is possible because BoLD's design ensures disputes will be resolved within a fixed time window, currently set to equal 1 challenge period (~6.4 days) for Arbitrum One and Arbitrum Nova. If there is a dispute, BoLD guarantees the maximum total time to be equal to 2 challenge periods (1 for raising disputes, 1 for resolving disputes), a 2 day grace period for the Security Council to intervene if necessary, and a small delta for computing challenges. +- BoLD, an acronym for Bounded Liquidity Delay, is a new challenge resolution protocol for Arbitrum chains that enables permissionless validation by mitigating the risks of delay attacks against [optimistic rollups like Arbitrum](/how-arbitrum-works/06-optimistic-rollup.mdx). This is possible because BoLD's design ensures disputes will be resolved within a fixed time window, currently set to equal 1 challenge period (~6.4 days) for Arbitrum One and Arbitrum Nova. If there is a dispute, BoLD guarantees the maximum total time to be equal to 2 challenge periods (1 for raising disputes, 1 for resolving disputes), a 2 day grace period for the Security Council to intervene if necessary, and a small delta for computing challenges. - Enabling permissionless validation is key milestone on [Arbitrum’s journey to becoming a Stage 2 Rollup](https://docs.arbitrum.foundation/state-of-progressive-decentralization) - the most advanced and mature rollup technology categorization, according to [L2Beat](https://medium.com/l2beat/introducing-stages-a-framework-to-evaluate-rollups-maturity-d290bb22befe). With BoLD, **any honest party can validate and bond their funds to post a correct L2 state assertions to win disputes against malicious entities.** - BoLD is currently considered to be in `alpha` release and is deployed on a public testnet. [Follow this guide](https://github.com/OffchainLabs/BoLD-validator-starter-kit) to deploy a BoLD validator to test and explore, first hand, how BoLD works to secure Arbitrum chains. To learn more about BoLD, please check out the [BoLD whitepaper](https://arxiv.org/abs/2404.10491) and [BoLD's code and specifications on Github](https://github.com/OffchainLabs/BoLD). @@ -70,9 +70,9 @@ The BoLD protocol provides the guardrails and rules for how validators challenge Let’s dive in to an overview of how BoLD actually works. -1. **An assertion is made:** Validators begin by taking the most recent confirmed [RBlock](/how-arbitrum-works/inside-arbitrum-nitro.mdx#the-rollup-chain), called `Block A`, and assert that some number of transactions afterwards, using Nitro’s deterministic State Transition Function (STF), will result in an end state, `Block Z`. If a validator claims that the end state represented by `Block Z` is correct, they will bond their funds to `Block Z` and propose that state to be posted to Ethereum. If nobody disagrees after a certain amount of time, known as the challenge period, then the state represented by the RBlock `Block Z` is confirmed as the correct state of an Arbitrum chain. However, if someone disagrees with the end state `Block Z`, they can submit a challenge. This is where BoLD comes into play. +1. **An assertion is made:** Validators begin by taking the most recent confirmed [RBlock](/how-arbitrum-works/06-optimistic-rollup.mdx#the-rollup-chain), called `Block A`, and assert that some number of transactions afterwards, using Nitro’s deterministic State Transition Function (STF), will result in an end state, `Block Z`. If a validator claims that the end state represented by `Block Z` is correct, they will bond their funds to `Block Z` and propose that state to be posted to Ethereum. If nobody disagrees after a certain amount of time, known as the challenge period, then the state represented by the RBlock `Block Z` is confirmed as the correct state of an Arbitrum chain. However, if someone disagrees with the end state `Block Z`, they can submit a challenge. This is where BoLD comes into play. 2. **A challenge is opened:** When another validator observes and disagrees with the end state represented by `Block Z`, they can permissionlessly open a challenge by asserting and bonding capital to a claim on a different end state, represented by an RBlock `Block Y`. At this point in time, there are now 2 asserted states: `Block A → Block Z` and `Block A → Block Y`. Each of these asserted states, at this point in time now that there's a challenge, are referred to _edges_ while a Merkle tree of asserted states from some start to end point (e.g. `Block A → Block Z`) is more formally known as a _history commitment._ It is important to note that Ethereum at this point in time has no notion of which edge(s) is correct or incorrect - edges are simply a portion of a claim made by a validator about the history of the chain from some end state all the way back to some initial state. Also note that because a bond put up by a validator is tied to an assertion rather than the party who put up that bond, there can be any number of honest, anonymous parties that can open challenges against incorrect claims. It is important to note that the bonds put up to open challenges are held in a Gnosis Safe multi-sig wallet controlled by the Arbitrum Foundation. -3. **Multi-level, interactive dissection begins:** To resolve the dispute, the disagreeing entities will need to come to an agreement on what the _actual, correct_ asserted state should be. [It would be tremendously expensive to re-execute](/how-arbitrum-works/inside-arbitrum-nitro.mdx#why-interactive-proving-is-better) and compare everything from `Block A → Block Z` and `Block A → Block Y`, especially since there could be potentially millions of transactions in between `A`, `Z`, and `Y`. Instead, entities take turns bisecting their respective _history commitments_ until they arrive at a single step of instruction where an arbiter, like Ethereum, can declare a winner. Note that [this system is very similar to how challenges are resolved on Arbitrum chains today](/how-arbitrum-works/inside-arbitrum-nitro.mdx#challenges) - BoLD only changes some minor, but important, details in the resolution process. Let’s dive into what happens next: +3. **Multi-level, interactive dissection begins:** To resolve the dispute, the disagreeing entities will need to come to an agreement on what the _actual, correct_ asserted state should be. [It would be tremendously expensive to re-execute](/how-arbitrum-works/06-optimistic-rollup.mdx#why-interactive-proving-is-better) and compare everything from `Block A → Block Z` and `Block A → Block Y`, especially since there could be potentially millions of transactions in between `A`, `Z`, and `Y`. Instead, entities take turns bisecting their respective _history commitments_ until they arrive at a single step of instruction where an arbiter, like Ethereum, can declare a winner. Note that [this system is very similar to how challenges are resolved on Arbitrum chains today](/how-arbitrum-works/07-interactive-fraud-proofs.mdx) - BoLD only changes some minor, but important, details in the resolution process. Let’s dive into what happens next: 1. **Block challenges**: when a challenge is opened, the edges are called _level-zero edges_ since they are at the granularity of Arbitrum blocks. The disputing parties take turns bisecting their history commitments until they identify the specific block that they disagree on. 2. **Big-step challenge:** now that the parties have narrowed down their dispute to a single block, that we call `Block B`, the back-and-forth bisection exercise continues within that block. Note that `Block B` is claimed by all parties to be some state after the initial state `Block A` but before the final states `Block Z` and `Block Y`. This time, however, the parties will narrow down on a specific _range_ of instructions for the state transition function within the block - essentially working towards identifying a set of instructions within which their disagreement lies. This range is currently defined as 2^20 steps of WASM instructions, which is the assembly of choice for validating Arbitrum chains. 3. **One-step challenge:** within that range of 2^20 instructions, the back and forth bisecting continues until all parties arrive at a single step of instruction that they disagree on. At this point in time, parties agree on the initial state of Arbitrum before the step but disagree on the end state 1 step immediately after. Remember that since Arbitrum’s state is entirely deterministic, there is only 1 correct end state. diff --git a/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md b/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md index 2f6cb58de..7d72603b7 100644 --- a/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md +++ b/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md @@ -21,7 +21,7 @@ Custom gas token support in the Arbitrum SDK introduces a suite of APIs designed 2. **Erc20Bridger Context:** - **APIs:** `getApproveGasTokenRequest` and `approveGasToken`. - - **Purpose:** In the scenario of bridging ERC20 assets to an Orbit chain, these APIs play a crucial role. Token Bridging on Arbitrum Nitro stack uses Retryable tickets and needs specific amount of fees to be paid for creation and redemption of the ticket. For more information about retryable tickets please take a look at [this](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets) part of our docs. The Orbit chain operates as a custom gas token network, necessitating the payment of fees in native tokens for the creation of retryable tickets and their redemption on the Orbit chain. To cover the submission and execution fees associated with retryable tickets on the Orbit chain, an adequate amount of native tokens must be approved and allocated on the parent chain to cover the fees. + - **Purpose:** In the scenario of bridging ERC20 assets to an Orbit chain, these APIs play a crucial role. Token Bridging on Arbitrum Nitro stack uses Retryable tickets and needs specific amount of fees to be paid for creation and redemption of the ticket. For more information about retryable tickets please take a look at [this](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets) part of our docs. The Orbit chain operates as a custom gas token network, necessitating the payment of fees in native tokens for the creation of retryable tickets and their redemption on the Orbit chain. To cover the submission and execution fees associated with retryable tickets on the Orbit chain, an adequate amount of native tokens must be approved and allocated on the parent chain to cover the fees. - **Note** that you should use `Erc20Bridger` when bridging an ERC-20 token between the parent chain and the orbit chain. **Note** that these APIs are just needed for `custom gas token` orbit chains and for ETH-powered rollup and anytrust orbit chains, you don't need to use them. diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/manage-fee-collectors.md b/arbitrum-docs/launch-orbit-chain/how-tos/manage-fee-collectors.md index 04501b651..184f26f5b 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/manage-fee-collectors.md +++ b/arbitrum-docs/launch-orbit-chain/how-tos/manage-fee-collectors.md @@ -25,8 +25,8 @@ There are four fee types that are collected on every transaction of an Orbit cha You can find more detailed information about these fee types in these pages: -- [L2 fees](/how-arbitrum-works/gas-fees.mdx) for the Orbit base fee and surplus fee -- [L1 fees](/how-arbitrum-works/l1-gas-pricing.mdx) for the Parent chain base fee and surplus fee +- [L2 fees](/how-arbitrum-works/09-gas-fees.mdx#l2-gas-pricing) for the Orbit base fee and surplus fee +- [L1 fees](/how-arbitrum-works/09-gas-fees.mdx#l1-gas-pricing) for the Parent chain base fee and surplus fee ## How to configure the fee collector addresses? diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md index 9a5b1d8cc..909db9377 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md +++ b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md @@ -9,11 +9,11 @@ content_type: how-to ## Child chain transactions -Generally, transactions executed through the sequencer on Orbit chains [achieve finality](/how-arbitrum-works/tx-lifecycle.mdx) equivalent to their parent chain once the relevant transaction data has been [posted in a batch](/how-arbitrum-works/sequencer.mdx). This means that transactions on Orbit chains are considered final in minutes. +Generally, transactions executed through the sequencer on Orbit chains [achieve finality](/how-arbitrum-works/02-transaction-lifecycle.mdx) equivalent to their parent chain once the relevant transaction data has been [posted in a batch](/how-arbitrum-works/03-sequencer.mdx). This means that transactions on Orbit chains are considered final in minutes. ## Parent chain → child chain transactions -Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/inside-arbitrum-nitro.mdx#if-the-sequencer-is-well-behaved) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that promoted it to achieve finality. +Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/03-sequencer.mdx#if-the-sequencer-is-well-behaved) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that promoted it to achieve finality. Orbit L3s may configure the finality of transactions executed through the delayed inbox to depend on different layers of finality. By default, Orbit chains will rely on the number of L1 block confirmations, effectively finalizing an L3 deposit as soon as L1 finalizes the batch posted by Arbitrum One or when a DACert is posted by Arbitrum Nova. This would be on the order of tens of minutes. @@ -33,4 +33,4 @@ Note, however, that if you choose to enable fast bridging, a re-org of un-finali ## Child chain → parent chain transactions -Normally, [outgoing transactions](/how-arbitrum-works/arbos/l2-l1-messaging.mdx) must wait until the assertion that includes their L2 message is confirmed (~one week) before a client can execute the message on L1. However, in the near future [Anytrust](/how-arbitrum-works/inside-anytrust.mdx) chains will be able to leverage their DAC to enable fast confirmations of withdrawals through the native token bridge. By immediately confirming assertions that have been signed by the DAC, finality can be reduced to ~fifteen minutes. +Normally, [outgoing transactions](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) must wait until the assertion that includes their L2 message is confirmed (~one week) before a client can execute the message on L1. However, in the near future [Anytrust](/how-arbitrum-works/08-anytrust-protocol.mdx) chains will be able to leverage their DAC to enable fast confirmations of withdrawals through the native token bridge. By immediately confirming assertions that have been signed by the DAC, finality can be reduced to ~fifteen minutes. diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge.md b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge.md index 4ff984ea8..4254434d5 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge.md +++ b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge.md @@ -93,7 +93,7 @@ Please note that after generating the raw transaction, the deployer must still s Deploying token bridge contracts is the first step in creating a bridge between the parent and the Orbit chain. -The deployment process is the same as Orbit chain contracts', where a primary contract facilitates the deployment of core contracts. The token bridge contracts are deployed on the parent and child chains by `TokenBridgeCreator`. `TokenBridgeCreator` does it in a single transaction using the [ Retryable Tickets protocol ](/how-arbitrum-works/arbos/l1-l2-messaging.mdx#retryable-tickets). +The deployment process is the same as Orbit chain contracts', where a primary contract facilitates the deployment of core contracts. The token bridge contracts are deployed on the parent and child chains by `TokenBridgeCreator`. `TokenBridgeCreator` does it in a single transaction using the [ Retryable Tickets protocol ](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets). Orbit SDK provides an API that automates the deployment by interacting with the `TokenBridgeCreator` contract. The API is `createTokenBridgePrepareTransactionRequest`, which processes the necessary inputs and generates a transaction request tailored for token bridge deployment. diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/use-a-custom-gas-token.mdx b/arbitrum-docs/launch-orbit-chain/how-tos/use-a-custom-gas-token.mdx index aa0e28190..4b247685c 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/use-a-custom-gas-token.mdx +++ b/arbitrum-docs/launch-orbit-chain/how-tos/use-a-custom-gas-token.mdx @@ -40,4 +40,4 @@ _Note: these methods use L1 to refer to the parent chain of the Orbit chain._ These parameter changes are strongly recommended to avoid charging users for a non-existent parent chain's base fee. The impact of not doing this is that Nitro will apply a parent chain's fee to all transactions. As Nitro assumes the native asset is ETH, all fees are expected to be denominated in ETH. This has the consequence of overcharging users for parent chain fees in native tokens that are more expensive than ETH (for example, if you use wrapped BTC as the custom gas token). In the case for tokens with prices much lower than ETH, the impact is far less pronounced. -In either case we strongly recommend taking these steps to avoid any issues with chain economics. You can read more about how Nitro manages fees in [Gas and fees](/how-arbitrum-works/gas-fees.mdx). +In either case we strongly recommend taking these steps to avoid any issues with chain economics. You can read more about how Nitro manages fees in [Gas and fees](/how-arbitrum-works/09-gas-fees.mdx). diff --git a/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md b/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md index 032fa5e21..005d87415 100644 --- a/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md +++ b/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md @@ -17,11 +17,11 @@ The Arbitrum Orbit SDK lets you programmatically create and manage your own Orbi There are three types of Orbit chains. Review the following table to determine which type best fits your needs: -| Chain Type | Description | Use Case | -| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| **Rollup** | Offers Ethereum-grade security by batching, compressing, and posting data to the parent chain, similarly to [Arbitrum One](https://arbitrum.io/). | Ideal for applications that require high security guarantees. | -| **AnyTrust** | Implements the [AnyTrust protocol](/how-arbitrum-works/inside-arbitrum-nitro.mdx#inside-anytrust), relying on an external [Data Availability Committee (DAC)](/intro/glossary#data-availability-committee-dac) to store data and provide it on-demand instead of using their [parent chain](https://docs.arbitrum.io/intro/glossary#parent-chain) as the Data Availability (DA) layer. | Suitable for applications that require lower transaction fees. | -| **Custom gas token** | An AnyTrust Orbit chain with the ability to specify a custom `ERC-20` gas token. | Ideal for applications that require custom gas fee tokens and lower transaction fees. | +| Chain Type | Description | Use Case | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| **Rollup** | Offers Ethereum-grade security by batching, compressing, and posting data to the parent chain, similarly to [Arbitrum One](https://arbitrum.io/). | Ideal for applications that require high security guarantees. | +| **AnyTrust** | Implements the [AnyTrust protocol](/how-arbitrum-works/08-anytrust-protocol), relying on an external [Data Availability Committee (DAC)](/intro/glossary#data-availability-committee-dac) to store data and provide it on-demand instead of using their [parent chain](https://docs.arbitrum.io/intro/glossary#parent-chain) as the Data Availability (DA) layer. | Suitable for applications that require lower transaction fees. | +| **Custom gas token** | An AnyTrust Orbit chain with the ability to specify a custom `ERC-20` gas token. | Ideal for applications that require custom gas fee tokens and lower transaction fees. | ## 2. Deploy your chain @@ -45,5 +45,5 @@ With your node configuration specified and token bridge deployed, you'll be read ## See also -- Learn more about the [AnyTrust consensus mechanism](/how-arbitrum-works/inside-arbitrum-nitro.mdx#inside-anytrust) +- Learn more about the [AnyTrust consensus mechanism](/how-arbitrum-works/08-anytrust-protocol.mdx) - Learn more about the [`ERC-20` token bridge architecture](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx) diff --git a/arbitrum-docs/launch-orbit-chain/reference/monitoring-tools-and-considerations.mdx b/arbitrum-docs/launch-orbit-chain/reference/monitoring-tools-and-considerations.mdx index 138955487..49a28024b 100644 --- a/arbitrum-docs/launch-orbit-chain/reference/monitoring-tools-and-considerations.mdx +++ b/arbitrum-docs/launch-orbit-chain/reference/monitoring-tools-and-considerations.mdx @@ -21,7 +21,7 @@ The Orbit Verification Script is currently under active development and is consi ## Orbit retryables tracker -[Retryable tickets](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) are messages sent from a parent chain and executed on the Orbit chain. Due to their asynchronous nature (they are executed several minutes after being created), if insufficient funds are provided at the time of creation, they might not automatically redeem (execute) upon arrival at the Orbit chain. When this occurs, a manual redemption of the ticket is required. The [Orbit retryables tracker](https://github.com/OffchainLabs/Orbit-retryable-tracker) is designed to assist in identifying and displaying the status of retryable tickets sent from a parent chain to the Orbit chain, and it reports any tickets that have not been automatically redeemed. +[Retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) are messages sent from a parent chain and executed on the Orbit chain. Due to their asynchronous nature (they are executed several minutes after being created), if insufficient funds are provided at the time of creation, they might not automatically redeem (execute) upon arrival at the Orbit chain. When this occurs, a manual redemption of the ticket is required. The [Orbit retryables tracker](https://github.com/OffchainLabs/Orbit-retryable-tracker) is designed to assist in identifying and displaying the status of retryable tickets sent from a parent chain to the Orbit chain, and it reports any tickets that have not been automatically redeemed. ## Data Availability Server (DAS) health checks diff --git a/arbitrum-docs/partials/_gentle-intro-partial.mdx b/arbitrum-docs/partials/_gentle-intro-partial.mdx index 1a34615f3..bf27b569b 100644 --- a/arbitrum-docs/partials/_gentle-intro-partial.mdx +++ b/arbitrum-docs/partials/_gentle-intro-partial.mdx @@ -37,7 +37,7 @@ Additionally, as long as there’s even just one honest validator, the chain wil #### Q: And how exactly is “fraud” “proven”? Sounds complicated. -Oh, it’s not so bad. In essence: if two validators disagree, only one of them (at most) can be telling the truth. In a dispute, the two validators play an interactive, call-and-response game, in which they narrow down their dispute to a single computational step (think of something small and simple, like multiplying two numbers). This one step gets executed on L1, and will, by necessity, prove that the honest party was telling the truth. For a more detailed rundown, see [here](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx). +Oh, it’s not so bad. In essence: if two validators disagree, only one of them (at most) can be telling the truth. In a dispute, the two validators play an interactive, call-and-response game, in which they narrow down their dispute to a single computational step (think of something small and simple, like multiplying two numbers). This one step gets executed on L1, and will, by necessity, prove that the honest party was telling the truth. For a more detailed rundown, see [here](/how-arbitrum-works/07-interactive-fraud-proofs.mdx). #### Q: This dispute game obviously takes some time; does this impose any sort of delay on Arbitrum users' transactions? @@ -53,7 +53,7 @@ For one, Arbitrum transactions are submitted on the L1 in batches; typically, a We really meant it, yes. Different layer 2 protocols emphasize and optimize for different things; Arbitrum was created with Ethereum compatibility as a top priority. This means users can use Arbitrum with all their favorite Ethereum wallets; developers can build and deploy contracts with all their favorite Ethereum libraries and tooling; in fact, most of the time, the experience of using Arbitrum will feel identical to that of using Ethereum (with the important exception of it being much cheaper and faster). -Much development went into achieving this level of Ethereum compatibility. But at its core: the Arbitrum itself uses a fork of [Geth](/how-arbitrum-works/arbos/geth.mdx) — the most widely used Ethereum implementation — with modifications to transform it into a trustless layer 2. This means most of the code running in Arbitrum is identical to the code running in Ethereum. We call this cutting-edge approach Nitro (developers can see the codebase [here](https://github.com/OffchainLabs/nitro)). +Much development went into achieving this level of Ethereum compatibility. But at its core: the Arbitrum itself uses a fork of [Geth](/how-arbitrum-works/04-geth-at-the-core.mdx) — the most widely used Ethereum implementation — with modifications to transform it into a trustless layer 2. This means most of the code running in Arbitrum is identical to the code running in Ethereum. We call this cutting-edge approach Nitro (developers can see the codebase [here](https://github.com/OffchainLabs/nitro)). #### Q: So builders can do all the stuff they do on Ethereum on Arbitrum, nice! But can they do _more_? diff --git a/arbitrum-docs/run-arbitrum-node/arbos-releases/01-overview.mdx b/arbitrum-docs/run-arbitrum-node/arbos-releases/01-overview.mdx index 9cc6dda08..6b503cfe9 100644 --- a/arbitrum-docs/run-arbitrum-node/arbos-releases/01-overview.mdx +++ b/arbitrum-docs/run-arbitrum-node/arbos-releases/01-overview.mdx @@ -23,7 +23,7 @@ import PublicPreviewBannerPartial from '../../node-running/partials/_upgrade-cad ArbOS upgrades are carried out by the chain's owner; in the case of Arbitrum One and Nova, the owner is the Arbitrum DAO and so an upgrade will require a governance proposal and vote to pass to complete the upgrade. [This is an example of a Nitro release that contains an ArbOS version bump, specifically to ArbOS 11](https://github.com/OffchainLabs/nitro/releases/tag/v2.2.0). -Visit [Inside Arbitrum Nitro](/how-arbitrum-works/inside-arbitrum-nitro.mdx) to learn more about Nitro's architecture; more information about ArbOS software releases is available on [the Arbitrum DAO forum](https://forum.arbitrum.foundation/t/arbitrum-arbos-upgrades/19695). +Visit [Inside Arbitrum Nitro](/how-arbitrum-works/01-a-gentle-introduction.mdx) to learn more about Nitro's architecture; more information about ArbOS software releases is available on [the Arbitrum DAO forum](https://forum.arbitrum.foundation/t/arbitrum-arbos-upgrades/19695). ## List of available ArbOS releases diff --git a/arbitrum-docs/run-arbitrum-node/arbos-releases/arbos11.mdx b/arbitrum-docs/run-arbitrum-node/arbos-releases/arbos11.mdx index daafe8ab9..eb89fa653 100644 --- a/arbitrum-docs/run-arbitrum-node/arbos-releases/arbos11.mdx +++ b/arbitrum-docs/run-arbitrum-node/arbos-releases/arbos11.mdx @@ -22,7 +22,7 @@ Formal release notes can be found [here](https://github.com/OffchainLabs/nitro/r - [EIP-3855: PUSH0 instruction](https://eips.ethereum.org/EIPS/eip-3855) - [EIP-3860: Limit and meter initcode](https://eips.ethereum.org/EIPS/eip-3860) - [EIP-6049: Deprecate SELFDESTRUCT](https://eips.ethereum.org/EIPS/eip-6049) -- Improvements and fixes for [retryable tickets](/how-arbitrum-works/arbos/l1-l2-messaging.mdx) to ensure that the fee calculation to redeem retryable tickets will take into account both the infrastructure fee and the network fee. The infrastructure fee is the minimum L2 base fee only, while the network fee collects L2 congestion charges. This is important for [AnyTrust chains](/how-arbitrum-works/inside-anytrust.mdx) like Arbitrum Nova because members of the Data Availability Committee (DAC) gets paid a percentage of the infrastructure fee but not the network fee. Previously, the calculations to determine the fee for redeeming retryable tickets did not consider the infrastructure fee. +- Improvements and fixes for [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) to ensure that the fee calculation to redeem retryable tickets will take into account both the infrastructure fee and the network fee. The infrastructure fee is the minimum L2 base fee only, while the network fee collects L2 congestion charges. This is important for [AnyTrust chains](/how-arbitrum-works/08-anytrust-protocol.mdx) like Arbitrum Nova because members of the Data Availability Committee (DAC) gets paid a percentage of the infrastructure fee but not the network fee. Previously, the calculations to determine the fee for redeeming retryable tickets did not consider the infrastructure fee. - Fixes an issue where the [`ArbOwnerPublic` precompile](/build-decentralized-apps/precompiles/02-reference.mdx#arbownerpublic) returned the incorrect list of chain owners. This does not change the parties who are able to perform chain owner actions. As intended, only the Arbitrum DAO is able to take chain owner actions for Arbitrum One and Nova. - Resolves an issue where the [`arbBlockHash` method](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) would take up all the gas when reverting. The previous incorrect behavior meant that if a transaction calls `arbBlockHash` with an out-of-range block number, then the transaction would consume all the gas when reverting. - Addition of the [`L1RewardReceipient`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`L1RewardRate`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) precompile methods to view L1 pricing parameters and make it easier to view the current chain configuration. diff --git a/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx b/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx index ae6fa069e..84370391f 100644 --- a/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx +++ b/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx @@ -15,7 +15,7 @@ content_type: overview This section offers information and a series of how-to guides to help you along the process of setting up a Data Availability Committee. These guides target two audiences: Committee members who wish to deploy a Data Availability Server, and chain owners who wish to configure their chain with the information of the Committee. -Before following the guides in this section, you should be familiarized with how the AnyTrust protocol works, and the role of the DAC in the protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx)_ to learn more. +Before following the guides in this section, you should be familiarized with how the AnyTrust protocol works, and the role of the DAC in the protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ to learn more. ## If you are a DAC member diff --git a/arbitrum-docs/run-arbitrum-node/data-availability-committees/02-deploy-das.mdx b/arbitrum-docs/run-arbitrum-node/data-availability-committees/02-deploy-das.mdx index ad7c127f3..c4cf02902 100644 --- a/arbitrum-docs/run-arbitrum-node/data-availability-committees/02-deploy-das.mdx +++ b/arbitrum-docs/run-arbitrum-node/data-availability-committees/02-deploy-das.mdx @@ -22,7 +22,7 @@ For more information related to configuring a DAC, refer to the _[Introduction]( This how-to assumes that you're familiar with: -- The DAC's role in the AnyTrust protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx)_ for a refresher. +- The DAC's role in the AnyTrust protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ for a refresher. - [Kubernetes](https://kubernetes.io/). The examples in this guide use Kubernetes to containerize your DAS. ## How does a DAS work? @@ -392,7 +392,7 @@ After you decode a batch poster transaction and get its `data` within the functi The first part (1 byte) is the `header flag`, which is used to specify which type of batch it is. Here we need to check if it has bit `0x80` (For example, `0x88` and `0x80` are both valid, but `0x55` is wrong). -The second part (32 bytes) is the keyset hash. You can learn more about what keyset is [here](/how-arbitrum-works/inside-anytrust#keysets). +The second part (32 bytes) is the keyset hash. You can learn more about what keyset is [here](/how-arbitrum-works/08-anytrust-protocol.mdx#keysets). The third part (32 bytes) is the data hash, and this is what we need to retrieve data. When you get this hash, you can retrieve data directly by following what we demonstrate in Step 4. diff --git a/arbitrum-docs/run-arbitrum-node/data-availability-committees/04-configure-dac.mdx b/arbitrum-docs/run-arbitrum-node/data-availability-committees/04-configure-dac.mdx index 3d244fde0..59a0629af 100644 --- a/arbitrum-docs/run-arbitrum-node/data-availability-committees/04-configure-dac.mdx +++ b/arbitrum-docs/run-arbitrum-node/data-availability-committees/04-configure-dac.mdx @@ -21,7 +21,7 @@ In this how-to, you'll learn how to configure the DAC in your chain. Refer to th This how-to assumes that you're familiar with: -- The DAC's role in the AnyTrust protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx)_ for a refresher. +- The DAC's role in the AnyTrust protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ for a refresher. - [Kubernetes](https://kubernetes.io/). The examples in this guide use Kubernetes to containerize your DAS. - [How to deploy a Data Availability Server (DAS)](/run-arbitrum-node/data-availability-committees/02-deploy-das.mdx). This is needed to understand where the data we'll be handling in this guide comes from. - The [Foundry toolkit](https://github.com/foundry-rs/foundry) @@ -44,7 +44,7 @@ The AnyTrust protocol assumes that for the `n` members of the DAC, a minimum of To perform this signing operation, each DAC member must generate their own set of BLS public and private keys. They should do this independently and ensure these keys are random and only used by them. You can find more information about how to generate a BLS pair of keys in [Generating BLS Keys](/run-arbitrum-node/data-availability-committees/deploy-das#step-1-generate-the-bls-keypair). -An Anytrust chain needs to know all DAC members' public keys to validate the integrity of the data being batched and posted. A _keyset_ is a list of all DAC members' RPC endpoint and BLS public key. Additionally, it also contains information about how many signatures are needed to approve a Data Availability Certificate (DACert), via a special `assumed-honest` parameter (i.e., the `h` parameter we mentioned above). This design lets the chain owner modify the DAC membership over time, and DAC members change their keys if needed. See _[Inside AnyTrust](/how-arbitrum-works/inside-arbitrum-nitro#inside-anytrust)_ for more information. +An Anytrust chain needs to know all DAC members' public keys to validate the integrity of the data being batched and posted. A _keyset_ is a list of all DAC members' RPC endpoint and BLS public key. Additionally, it also contains information about how many signatures are needed to approve a Data Availability Certificate (DACert), via a special `assumed-honest` parameter (i.e., the `h` parameter we mentioned above). This design lets the chain owner modify the DAC membership over time, and DAC members change their keys if needed. See _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ for more information. We use this keyset, and its hash to configure the SequencerInbox contract with the valid keyset, and also the batch poster (to request storing information) and full nodes (to request information already stored). diff --git a/arbitrum-docs/stylus/concepts/how-it-works.md b/arbitrum-docs/stylus/concepts/how-it-works.md index a92a3dd2e..a6892502a 100644 --- a/arbitrum-docs/stylus/concepts/how-it-works.md +++ b/arbitrum-docs/stylus/concepts/how-it-works.md @@ -41,9 +41,9 @@ EVM contracts continue to execute the same way they were before Stylus. When cal Nitro operates in two modes: a "happy case" where it compiles execution history to native code, and a "sad case" during validator disputes, where it compiles execution history to WASM for interactive fraud proofs on Ethereum. Stylus builds on Nitro's fraud-proving technology, allowing it to verify both execution history and WASM programs deployed by developers. -Stylus is made possible by Nitro’s ability to replay and verify disputes using WASM. Validators bisect disputes until an invalid step is identified and proven on-chain through a [“one-step proof.”](/how-arbitrum-works/fraud-proofs/challenge-manager.mdx#general-bisection-protocol). This deterministic fraud-proving capability ensures the correctness of any arbitrary program compiled to WASM. The combination of WASM's and Nitro's properties enables this technological leap we call Stylus. +Stylus is made possible by Nitro’s ability to replay and verify disputes using WASM. Validators bisect disputes until an invalid step is identified and proven on-chain through a [“one-step proof.”](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#general-bisection-protocol). This deterministic fraud-proving capability ensures the correctness of any arbitrary program compiled to WASM. The combination of WASM's and Nitro's properties enables this technological leap we call Stylus. -For more details on Nitro’s architecture, refer to the [documentation](/how-arbitrum-works/inside-arbitrum-nitro.mdx) or the [Nitro whitepaper](https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf). +For more details on Nitro’s architecture, refer to the [documentation](/how-arbitrum-works/01-a-gentle-introduction.mdx) or the [Nitro whitepaper](https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf). ## Why does this matter? diff --git a/arbitrum-docs/welcome/get-started.mdx b/arbitrum-docs/welcome/get-started.mdx index e38391356..45b384383 100644 --- a/arbitrum-docs/welcome/get-started.mdx +++ b/arbitrum-docs/welcome/get-started.mdx @@ -14,17 +14,17 @@ Arbitrum suite along with onboarding guidance tailored to specific audiences. The Arbitrum suite includes the protocols, chains, services, and SDKs that power the Arbitrum ecosystem: -| Component | Description | -| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -| [Arbitrum Rollup](/how-arbitrum-works/inside-arbitrum-nitro.mdx) | A **protocol** for scaling Ethereum smart contracts. | -| [Arbitrum AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) | A **protocol** for scaling Ethereum smart contracts even further, with a mild trust assumption. | -| [Arbitrum Nitro](/how-arbitrum-works/inside-arbitrum-nitro.mdx) | The node **software** that codifies the Rollup and AnyTrust protocols. | -| [Arbitrum nodes](/run-arbitrum-node/03-run-full-node.mdx) | **Machines** that run Nitro in order to service and/or interact with an Arbitrum chain. | -| [Arbitrum One](https://portal.arbitrum.io/?chains=arbitrum-one) | A public Rollup **chain**. | -| [Arbitrum Nova](https://portal.arbitrum.io/?chains=arbitrum-nova) | A public AnyTrust **chain**. | -| [Arbitrum bridge](https://bridge.arbitrum.io/) | Lets you move ETH and ERC-20 tokens between Ethereum, Arbitrum, and select Orbit chains. | -| [Arbitrum Orbit](https://orbit.arbitrum.io/) | Lets you run your own Rollup and AnyTrust chains. | -| [Arbitrum Stylus](/stylus/stylus-gentle-introduction) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | +| Component | Description | +| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | +| [Arbitrum Rollup](/how-arbitrum-works/06-optimistic-rollup.mdx) | A **protocol** for scaling Ethereum smart contracts. | +| [Arbitrum AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx) | A **protocol** for scaling Ethereum smart contracts even further, with a mild trust assumption. | +| [Arbitrum Nitro](/how-arbitrum-works/01-a-gentle-introduction.mdx) | The node **software** that codifies the Rollup and AnyTrust protocols. | +| [Arbitrum nodes](/run-arbitrum-node/03-run-full-node.mdx) | **Machines** that run Nitro in order to service and/or interact with an Arbitrum chain. | +| [Arbitrum One](https://portal.arbitrum.io/?chains=arbitrum-one) | A public Rollup **chain**. | +| [Arbitrum Nova](https://portal.arbitrum.io/?chains=arbitrum-nova) | A public AnyTrust **chain**. | +| [Arbitrum bridge](https://bridge.arbitrum.io/) | Lets you move ETH and ERC-20 tokens between Ethereum, Arbitrum, and select Orbit chains. | +| [Arbitrum Orbit](https://orbit.arbitrum.io/) | Lets you run your own Rollup and AnyTrust chains. | +| [Arbitrum Stylus](/stylus/stylus-gentle-introduction) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | ## Arbitrum for users @@ -68,7 +68,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------- | -| [Inside Nitro](/how-arbitrum-works/inside-arbitrum-nitro.mdx) | A technical deep dive into Nitro's architecture. | -| [Inside AnyTrust](/how-arbitrum-works/inside-anytrust.mdx) | A technical deep dive into the AnyTrust protocol. | +| [Inside Nitro](/how-arbitrum-works/01-a-gentle-introduction.mdx) | A technical deep dive into Nitro's architecture. | +| [Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx) | A technical deep dive into the AnyTrust protocol. | | [Arbitrum whitepaper](https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf) | The original whitepaper that introduced Nitro. | | [DAO docs](https://docs.arbitrum.foundation/gentle-intro-dao-governance) | Docs that support members of the Arbitrum DAO. | diff --git a/arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx b/website/archive/arbos/introduction.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/arbos/introduction.mdx rename to website/archive/arbos/introduction.mdx From 2ca62c76c8f346988f43a321b92d8f3c30c8ea67 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 12 Dec 2024 15:51:38 -0600 Subject: [PATCH 27/75] Updated frontmatter to be more specific --- .../how-arbitrum-works/02-transaction-lifecycle.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 2 +- arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx | 4 ++-- .../05-separating-execution-from-proving.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 4 ++-- .../how-arbitrum-works/07-interactive-fraud-proofs.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx | 4 ++-- arbitrum-docs/how-arbitrum-works/assertion-tree.mdx | 5 +++++ 11 files changed, 24 insertions(+), 19 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index f5ecb72dd..62e2c350c 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -1,9 +1,9 @@ --- title: Sequencing, Followed by Deterministic Execution -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of the Arbitrum Transaction Lifecycle, sequencing, and deterministic execution.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about the transaction lifecycle. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 278bdf452..86bdcb31e 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -3,7 +3,7 @@ title: The Sequencer and Censorship Resistance description: 'Learn the fundamentals of the Arbitrum Sequencer.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about the Sequencer. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index a7974878c..bd0095922 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -1,9 +1,9 @@ --- title: Geth at the core -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: Learn the fundamentals of Geth and ArbOS. author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about Geth and ArbOS. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 80085f8c1..26819a1ca 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -1,9 +1,9 @@ --- title: Separating Execution from Proving -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of Arbitrum Rollup protocol.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about the Arbitrum Rollup protocol. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 01a2598ec..061558412 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -1,9 +1,9 @@ --- title: Optimistic Rollup -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: Learn the fundamentals of Arbitrum's optimistic rollup. author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about Arbitrum's Optimistic Rollup protocol. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 0181e3bd8..5e6cc4151 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -1,9 +1,9 @@ --- title: Challenges/:/ Interactive Fraud Proofs -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: Learn the fundamentals of Arbitrum's Interactive Fraud Proofs and challenges. author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about Interactive Fraud Proofs and challenges. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index 0ca414de5..8a77e119a 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -1,9 +1,9 @@ --- title: AnyTrust Protocol -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of the Arbitrum AnyTrust protocol.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about AnyTrust. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index 171f2c6f3..d822bdbc7 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -1,9 +1,9 @@ --- title: Gas and Fees -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of how to calculate fees on Arbitrum.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about gas/fee calculations on Arbitrum. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index 850c188fa..61acdb120 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -1,9 +1,9 @@ --- title: L1 to L2 messaging -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of L1 to L2 messaging on Arbitrum.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about messaging between L1 and L2 within Arbitrum. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index a8816a116..cafcd9f9c 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -1,9 +1,9 @@ --- title: L2 to L1 messaging -description: 'Learn the fundamentals of Arbitrum Transaction Lifecycle.' +description: 'Learn the fundamentals of L2 to L1 messaging on Arbitrum.' author: pete-vielhaber sme: TucksonDev -user_story: As a current or prospective Arbitrum user, I need to learn more about Nitros design. +user_story: As a current or prospective Arbitrum user, I need to learn more about messaging between L2 and L1 on Arbitrum. content_type: get-started --- diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx index 207b9d75e..eb4dd0cc5 100644 --- a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx +++ b/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx @@ -1,5 +1,10 @@ --- +title: Assertion Tree +description: 'Learn the fundamentals of assertions/disputable assertions on Arbitrum.' author: dzgoldman +sme: dzgoldman +user_story: As a current or prospective Arbitrum user, I need to learn more about how assertions and disputable assertions work on Arbitrum. +content_type: get-started --- # The Assertion Tree From 1810eaa0a819bc761d08f041c8a2baea59e3193a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Thu, 12 Dec 2024 14:59:51 -0800 Subject: [PATCH 28/75] fix: add imports for mermaid diagram and address aliases --- arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index 61acdb120..26eb334ed 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -7,6 +7,10 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- +import { AddressAliasHelper } from '@site/src/components/AddressAliasHelper'; +import { MermaidWithHtml, Nodes, Node, Connection, NodeDescriptions, NodeDescription, } from '/src/components/MermaidWithHtml/MermaidWithHtml'; + + In the [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). From d27dac5e5094def98896bac3b39cfd6464e84e9f Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:08:18 -0600 Subject: [PATCH 29/75] yarn format fixes --- .../how-arbitrum-works/10-l1-to-l2-messaging.mdx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index 26eb334ed..b440d9bce 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -8,8 +8,14 @@ content_type: get-started --- import { AddressAliasHelper } from '@site/src/components/AddressAliasHelper'; -import { MermaidWithHtml, Nodes, Node, Connection, NodeDescriptions, NodeDescription, } from '/src/components/MermaidWithHtml/MermaidWithHtml'; - +import { + MermaidWithHtml, + Nodes, + Node, + Connection, + NodeDescriptions, + NodeDescription, +} from '/src/components/MermaidWithHtml/MermaidWithHtml'; In the [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. From b3a70744cef9cd70b3acbea53d8f2419d7a50c5c Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:23:25 -0600 Subject: [PATCH 30/75] resolved vercel issues --- .../how-arbitrum-works/02-transaction-lifecycle.mdx | 8 ++++---- .../how-arbitrum-works/04-geth-at-the-core.mdx | 10 +++++----- .../launch-orbit-chain/orbit-sdk-introduction.md | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 62e2c350c..63372d2bf 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -61,8 +61,8 @@ Upon receiving a transaction, the Sequencer will: **See**: -- [ArbOS](/how-arbitrum-works/04-geth-at-the-core#arbos.mdx) -- [Geth](/how-arbitrum-works/04-geth-at-the-core#geth.mdx) +- [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos) +- [Geth](/how-arbitrum-works/04-geth-at-the-core.mdx#geth) - [L1 pricing](/how-arbitrum-works/09-gas-fees.mdx) / [L2 Gas](/how-arbitrum-works/09-gas-fees.mdx) #### ~ ~ ~ FINALITY CHECK: Trusted / Soft Confirmation ~ ~ ~ @@ -136,8 +136,8 @@ A dispute consists of two staked validators dissecting their disagreement down t **See:** -- [Challenges](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#challenge-manager) -- [Wasm/WAVM](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#wasm-wavm) +- [Challenges](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#challengemanager) +- [Wasm/WAVM](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#wasm-to-wavm) L1 contracts also keep track of the tree of all assertions; i.e., how many stakers are in disagreement, who is currently disputing with whom, etc. We refer to this level of Arbitrum's design architecture as its "assertion tree protocol." diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index bd0095922..b2a221528 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -51,7 +51,7 @@ Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additiona - [`StartTxHook`](#StartTxHook) - `core.transitionDbImpl` - if `IsArbitrum()` remove tip - - [`GasChargingHook`](#GasChargingHook) + - [`GasChargingHook`](/how-arbitrum-works/geth-at-the-core#gascharginghook) - `evm.Call` - `core.vm.EVMInterpreter.Run` - [`PushCaller`](#PushCaller) @@ -78,7 +78,7 @@ If the transaction is an `ArbitrumSubmitRetryableTx`, ArbOS creates a retryable The hook returns `true` for both of these transaction types, signifying that the state transition is complete. -#### [`GasChargingHook`][gascharginghook_link]{#GasChargingHook} +#### [`GasChargingHook`][gascharginghook_link] This fallible hook ensures the user has enough funds to pay their poster's L1 calldata costs. If not, the transaction is reverted and the [`EVM`][evm_link] does not start. In the common case that the user can pay, the amount paid for calldata is set aside for later reimbursement of the poster. All other fees go to the network account, as they represent the transaction's burden on validators and nodes more generally. @@ -99,7 +99,7 @@ In Arbitrum, the BlockHash and Number operations return data that relies on unde #### [`ForceRefundGas`][forcerefundgas_link]{#ForceRefundGas} -This hook allows ArbOS to add additional refunds to the user's tx. This is currently only used to refund any compute gas purchased in excess of ArbOS's per-block gas limit during the [`GasChargingHook`](#GasChargingHook). +This hook allows ArbOS to add additional refunds to the user's tx. This is currently only used to refund any compute gas purchased in excess of ArbOS's per-block gas limit during the [`GasChargingHook`](/how-arbitrum-works/geth-at-the-core#gascharginghook). #### [`NonrefundableGas`][nonrefundablegas_link]{#NonrefundableGas} @@ -399,9 +399,9 @@ Based on this information, ArbOS maintains an L1 data fee, also tracked as part The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. -While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth#Hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. +While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](#hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. -ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](geth#GasChargingHook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. +ArbOS's larger gas pool [determines][maintain_limit_link] the per-block gas limit, setting a dynamic [upper limit][per_block_limit_link] on the amount of compute gas an L2 block may have. This limit is always enforced, though for the [first transaction][first_transaction_link] it's done in the [GasChargingHook](/how-arbitrum-works/geth-at-the-core#gascharginghook) to avoid sharp decreases in the L1 gas price from over-inflating the compute component purchased to above the gas limit. This improves UX by allowing the first transaction to succeed rather than requiring a resubmission. Because the first transaction lowers the amount of space left in the block, subsequent transactions do not employ this strategy and may fail from such compute-component inflation. This is acceptable because such transactions are only present in cases where the system is under heavy load and the result is that the user's transaction is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the transaction being automatically resubmitted in such a scenario. The reason we need a per-block gas limit is that Arbitrator WAVM execution is much slower than native transaction execution. This means that there can only be so much gas -- which roughly translates to wall-clock time -- in an L2 block. It also provides an opportunity for ArbOS to limit the size of blocks should demand continue to surge even as the price rises. diff --git a/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md b/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md index 005d87415..780f9e26f 100644 --- a/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md +++ b/arbitrum-docs/launch-orbit-chain/orbit-sdk-introduction.md @@ -17,11 +17,11 @@ The Arbitrum Orbit SDK lets you programmatically create and manage your own Orbi There are three types of Orbit chains. Review the following table to determine which type best fits your needs: -| Chain Type | Description | Use Case | -| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| **Rollup** | Offers Ethereum-grade security by batching, compressing, and posting data to the parent chain, similarly to [Arbitrum One](https://arbitrum.io/). | Ideal for applications that require high security guarantees. | -| **AnyTrust** | Implements the [AnyTrust protocol](/how-arbitrum-works/08-anytrust-protocol), relying on an external [Data Availability Committee (DAC)](/intro/glossary#data-availability-committee-dac) to store data and provide it on-demand instead of using their [parent chain](https://docs.arbitrum.io/intro/glossary#parent-chain) as the Data Availability (DA) layer. | Suitable for applications that require lower transaction fees. | -| **Custom gas token** | An AnyTrust Orbit chain with the ability to specify a custom `ERC-20` gas token. | Ideal for applications that require custom gas fee tokens and lower transaction fees. | +| Chain Type | Description | Use Case | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| **Rollup** | Offers Ethereum-grade security by batching, compressing, and posting data to the parent chain, similarly to [Arbitrum One](https://arbitrum.io/). | Ideal for applications that require high security guarantees. | +| **AnyTrust** | Implements the [AnyTrust protocol](/how-arbitrum-works/08-anytrust-protocol.mdx), relying on an external [Data Availability Committee (DAC)](/intro/glossary#data-availability-committee-dac) to store data and provide it on-demand instead of using their [parent chain](https://docs.arbitrum.io/intro/glossary#parent-chain) as the Data Availability (DA) layer. | Suitable for applications that require lower transaction fees. | +| **Custom gas token** | An AnyTrust Orbit chain with the ability to specify a custom `ERC-20` gas token. | Ideal for applications that require custom gas fee tokens and lower transaction fees. | ## 2. Deploy your chain From ed263dbf058d9fa9fe7837c7e423be2de3a16511 Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:26:00 -0600 Subject: [PATCH 31/75] added mermaid w/html helper --- arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index d822bdbc7..f900f2e56 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -7,6 +7,16 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- +import { AddressAliasHelper } from '@site/src/components/AddressAliasHelper'; +import { + MermaidWithHtml, + Nodes, + Node, + Connection, + NodeDescriptions, + NodeDescription, +} from '/src/components/MermaidWithHtml/MermaidWithHtml'; + Gas is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. There are two parties a user pays when submitting a tx: From 91434eb539e5dc0f50b7e77b94491a342a6ec7e2 Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:26:51 -0600 Subject: [PATCH 32/75] rolled back --- arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index f900f2e56..d822bdbc7 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -7,16 +7,6 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -import { AddressAliasHelper } from '@site/src/components/AddressAliasHelper'; -import { - MermaidWithHtml, - Nodes, - Node, - Connection, - NodeDescriptions, - NodeDescription, -} from '/src/components/MermaidWithHtml/MermaidWithHtml'; - Gas is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. There are two parties a user pays when submitting a tx: From 0cec2eb2eae6863ea25a9515d3cd04d9129d056d Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:44:54 -0600 Subject: [PATCH 33/75] add'l vercel build issues --- arbitrum-docs/launch-orbit-chain/how-tos/calculate-aep-fees.mdx | 2 +- arbitrum-docs/welcome/get-started.mdx | 2 -- arbitrum-sdk | 2 +- stylus-by-example | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/calculate-aep-fees.mdx b/arbitrum-docs/launch-orbit-chain/how-tos/calculate-aep-fees.mdx index 66fd35000..027853d87 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/calculate-aep-fees.mdx +++ b/arbitrum-docs/launch-orbit-chain/how-tos/calculate-aep-fees.mdx @@ -25,7 +25,7 @@ Based on the above, we interpret that an Orbit chain’s revenue sources include ### Assertion costs -The above fee system applies to an Orbit chain’s Sequencer and Batch Poster, but there is another important actor that is considered essential to the chain. These are the [validators](/how-arbitrum-works/inside-arbitrum-nitro.mdx#validators). +The above fee system applies to an Orbit chain’s Sequencer and Batch Poster, but there is another important actor that is considered essential to the chain. These are the [validators](/how-arbitrum-works/06-optimistic-rollup.mdx#validators). Validators are responsible for posting assertions on the parent chain, which are disputable claims about the new state of the Rollup. Posting an assertion is what progressed chain state on the parent chain. Validators are also responsible for securing the chain by creating disputes on false assertions. diff --git a/arbitrum-docs/welcome/get-started.mdx b/arbitrum-docs/welcome/get-started.mdx index 3c9b6cfd8..ea510728d 100644 --- a/arbitrum-docs/welcome/get-started.mdx +++ b/arbitrum-docs/welcome/get-started.mdx @@ -14,7 +14,6 @@ Arbitrum suite along with onboarding guidance tailored to specific audiences. The Arbitrum suite includes the protocols, chains, services, and SDKs that power the Arbitrum ecosystem: - | Component | Description | | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | [Arbitrum Rollup](/how-arbitrum-works/06-optimistic-rollup.mdx) | A **protocol** for scaling Ethereum smart contracts. | @@ -27,7 +26,6 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | [Arbitrum Orbit](https://orbit.arbitrum.io/) | Lets you run your own Rollup and AnyTrust chains. | | [Arbitrum Stylus](/stylus/stylus-gentle-introduction) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | - ## Arbitrum for users **Users** interact with Arbitrum either through the Arbitrum bridge or by using dApps that have been deployed to an Arbitrum chain. diff --git a/arbitrum-sdk b/arbitrum-sdk index 31852e262..f5b3d04ba 160000 --- a/arbitrum-sdk +++ b/arbitrum-sdk @@ -1 +1 @@ -Subproject commit 31852e2620641bd842c66e19005964c0b823563e +Subproject commit f5b3d04baf62356032f5475057637d989da5b6c1 diff --git a/stylus-by-example b/stylus-by-example index 262ea1344..508777e63 160000 --- a/stylus-by-example +++ b/stylus-by-example @@ -1 +1 @@ -Subproject commit 262ea1344634b0c454c2e692cd87e7dc59d47a46 +Subproject commit 508777e63fb38b7d27426a8ae0f2ffacf1c184c9 From ef43d4972257f5c04fb6748a27d691513493057d Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:47:42 -0600 Subject: [PATCH 34/75] fixing vercel build issue within welcome for stylus link --- arbitrum-docs/welcome/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/welcome/get-started.mdx b/arbitrum-docs/welcome/get-started.mdx index ea510728d..25a4860c8 100644 --- a/arbitrum-docs/welcome/get-started.mdx +++ b/arbitrum-docs/welcome/get-started.mdx @@ -24,7 +24,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | [Arbitrum Nova](https://portal.arbitrum.io/?chains=arbitrum-nova) | A public AnyTrust **chain**. | | [Arbitrum bridge](https://bridge.arbitrum.io/) | Lets you move ETH and ERC-20 tokens between Ethereum, Arbitrum, and select Orbit chains. | | [Arbitrum Orbit](https://orbit.arbitrum.io/) | Lets you run your own Rollup and AnyTrust chains. | -| [Arbitrum Stylus](/stylus/stylus-gentle-introduction) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | +| [Arbitrum Stylus](/stylus/gentle-introduction.mdx) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | ## Arbitrum for users From c379532fa12b5d82e38ef4b6369200b5e6058b97 Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 13 Dec 2024 08:49:10 -0600 Subject: [PATCH 35/75] build issues --- arbitrum-docs/welcome/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/welcome/get-started.mdx b/arbitrum-docs/welcome/get-started.mdx index 25a4860c8..25fd1709b 100644 --- a/arbitrum-docs/welcome/get-started.mdx +++ b/arbitrum-docs/welcome/get-started.mdx @@ -24,7 +24,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | [Arbitrum Nova](https://portal.arbitrum.io/?chains=arbitrum-nova) | A public AnyTrust **chain**. | | [Arbitrum bridge](https://bridge.arbitrum.io/) | Lets you move ETH and ERC-20 tokens between Ethereum, Arbitrum, and select Orbit chains. | | [Arbitrum Orbit](https://orbit.arbitrum.io/) | Lets you run your own Rollup and AnyTrust chains. | -| [Arbitrum Stylus](/stylus/gentle-introduction.mdx) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | +| [Arbitrum Stylus](/stylus/gentle-introduction.mdx) | Lets you write EVM-compatible smart contracts in Rust and any other language that compiles to Wasm. | ## Arbitrum for users From 923435eb32761dc6e79c0608223aa415ec1c8ce9 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:02:21 -0600 Subject: [PATCH 36/75] Update arbitrum-docs/build-decentralized-apps/03-public-chains.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/build-decentralized-apps/03-public-chains.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx b/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx index c54b86550..b4ab2b70d 100644 --- a/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx +++ b/arbitrum-docs/build-decentralized-apps/03-public-chains.mdx @@ -13,7 +13,7 @@ Arbitrum chains are Layer 2 solutions built on top of the Ethereum blockchain, d ### Arbitrum One -**Arbitrum One** is a Layer 2 (L2) optimistic rollup chain that implements the Arbitrum Rollup protocol and settles to Ethereum's Layer 1 (L1) chain. It lets you build high-performance Ethereum dApps with low transaction costs and Ethereum-grade security guarantees, introducing no additional trust assumptions. This is made possible by the [Nitro](/how-arbitrum-works/04-geth-at-the-core.mdx) technology stack, a "Geth-at-the-core" architecture that gives Arbitrum One (and Nova) advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. +**Arbitrum One** is a Layer 2 (L2) optimistic Rollup chain that implements the Arbitrum Rollup protocol and settles to Ethereum's Layer 1 (L1) chain. It lets you build high-performance Ethereum dApps with low transaction costs and Ethereum-grade security guarantees, introducing no additional trust assumptions. This is made possible by the [Nitro](/how-arbitrum-works/04-geth-at-the-core.mdx) technology stack, a "Geth-at-the-core" architecture that gives Arbitrum One (and Nova) advanced calldata compression, separate contexts for common execution and fault proving, Ethereum L1 gas compatibility, and more. ### Arbitrum Nova From 8a1f7e5d05d7d01a5a36cf4010a13018664ca712 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:03:00 -0600 Subject: [PATCH 37/75] Update arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../arbitrum-vs-ethereum/01-comparison-overview.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index f08f33131..edb256b25 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -26,7 +26,7 @@ You can deploy Solidity contracts onto Arbitrum just like you do Ethereum. There ## Fees -The fees an Arbitrum transaction pays for execution essentially work identically to gas fees on Ethereum. Arbitrum transactions must also, however, pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitrum-works/09-gas-fees.mdx) and L1 pricing. +The fees for executing an Arbitrum transaction work like gas fees on Ethereum. However, Arbitrum transactions must also pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitral-works/09-gas-fees.mdx) and L1 pricing. ## Cross-chain messaging From 168e649175ca40d1d7240261018956ad2e35837e Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:03:18 -0600 Subject: [PATCH 38/75] Update arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../arbitrum-vs-ethereum/02-block-numbers-and-time.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 88220f9e0..1656f7539 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -13,7 +13,7 @@ With the release of Arbitrum Orbit, Arbitrum chains can now be L2s that settle t ::: -As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions at some later time. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/03-sequencer.mdx#happycommon-case-sequencer-is-live-and-well-behaved) or via the chain's [delayed inbox](/how-arbitrum-works/03-sequencer.mdx#unhappyuncommon-case-sequencer-isnt-doing-its-job). +As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions later. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/03-sequencer.mdx#happycommon-case-sequencer-is-live-and-well-behaved) or via the chain's [delayed inbox](/how-arbitrum-works/03-sequencer.mdx#unhappyuncommon-case-sequencer-isnt-doing-its-job). Once in the chain's core inbox contract, transactions are processed in order. Generally, some time will elapse between when a message is put into the inbox (and timestamped) and when the contract processes the message and carries out the transaction requested by the message. From 252f0ad0f7e56a18d78bc50600bb143cd7f21c58 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:03:37 -0600 Subject: [PATCH 39/75] Update arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../token-bridging/02-token-bridge-ether.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx index daf80d289..c6b97d1c6 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx @@ -36,7 +36,7 @@ ArbSys(100).withdrawEth{ value: 2300000 }(destAddress) Upon withdrawing, the Ether balance is burnt on the Arbitrum side, and will later be made available on the Ethereum side. -`ArbSys.withdrawEth` is actually a convenience function which is equivalent to calling `ArbSys.sendTxToL1` with empty calldataForL1. Like any other `sendTxToL1` call, it will require an additional call to `Outbox.executeTransaction` on L1 after the dispute period elapses for the user to finalize claiming their funds on L1 (see ["L2 to L1 Messages"](/how-arbitrum-works/11-l2-to-l1-messaging.mdx)). Once the withdrawal is executed from the Outbox, the user's Ether balance will be credited on L1. +`ArbSys.withdrawEth` is a convenience function equivalent to calling `ArbSys.sendTxToL1` with empty `calldataForL1`. Like any other `sendTxToL1` call, it will require an additional call to `Outbox.executeTransaction` on L1 after the dispute period elapses for the user to finalize claiming their funds on L1 (see ["L2 to L1 Messages"](/how-arbitrum-works/11-l2-to-l1-messaging.mdx)). Once the withdrawal is executed from the Outbox, the user's Ether balance will be credited on L1. The following diagram depicts the process that funds follow during a withdraw operation. From 74857a064a54a395ba4526e0602bc75e08a77dfb Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:03:55 -0600 Subject: [PATCH 40/75] Update arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../token-bridging/03-token-bridge-erc20.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx index 9596a59e9..78dbccc62 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx @@ -44,7 +44,7 @@ Our architecture consists of three types of contracts: ![img](./assets/gatewayUML.svg) -All Ethereum to Arbitrum token transfers are initiated via the router contract on L1, the `L1GatewayRouter` contract. `L1GatewayRouter` forwards the token's deposit call to the appropriate gateway contract on L1, the `L1ArbitrumGateway` contract. `L1GatewayRouter` is responsible for mapping L1 token addresses to L1Gateway contracts, thus acting as an L1/L2 address oracle, and ensuring that each token corresponds to only one gateway. The `L1ArbitrumGateway` then communicates to its counterpart gateway contract on L2, the `L2ArbitrumGateway` contract (typically/expectedly via [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx)). +All Ethereum to Arbitrum token transfers are initiated via the router contract on L1, the `L1GatewayRouter` contract. `L1GatewayRouter` forwards the token's deposit call to the appropriate gateway contract on L1, the `L1ArbitrumGateway` contract. `L1GatewayRouter` is responsible for mapping L1 token addresses to L1Gateway contracts, thus acting as an L1/L2 address oracle and ensuring each token corresponds to only one gateway. The `L1ArbitrumGateway` then communicates to its counterpart gateway contract on L2, the `L2ArbitrumGateway` contract (typically/expectedly via [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx)). ![img](./assets/bridge_deposits.png) From 5a78d99735246c066d76edb4da6d0d990ebb7c1e Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:04:12 -0600 Subject: [PATCH 41/75] Update arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../02-how-to-bridge-tokens-standard.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx b/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx index bda6dde3d..1d584001c 100644 --- a/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx +++ b/arbitrum-docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx @@ -172,7 +172,7 @@ As mentioned before, you can also call the method `outboundTransferCustomRefund` ## Step 5: Wait for execution on L2 -After calling the deposit method (or the `outboundTransferCustomRefund`, if you’re choosing the manual way), you’ll have to wait a bit until the message is executed on L2. We will verify the status of the underlying retryable ticket created to bridge the tokens. Check this page, to know more about [L1-to-L2 messages, also known as retryables](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). +After calling the deposit method (or the `outboundTransferCustomRefund` if you’re choosing the manual way), you’ll have to wait a bit until the message is executed on L2. We will verify the status of the underlying retryable ticket created to bridge the tokens. Check this page to learn more about [L1-to-L2 messages, also known as retryables](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). You can programmatically wait for the execution of the transaction on L2 using Arbitrum’s SDK. You should first wait for the execution of the submission transaction (the one sent to the router contract) and then the execution of the L2 transaction. From 94fe399b042cf7562c2dd254491094c3cd16b023 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:04:33 -0600 Subject: [PATCH 42/75] Update arbitrum-docs/how-arbitrum-works/03-sequencer.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 86bdcb31e..e8fe7858a 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -9,7 +9,7 @@ content_type: get-started The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. -Here we will describe the mechanics of how the Sequencer typically operates, and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus mechanism thereby preserves censorship resistance even if the Sequencer is being completely unresponsive or even malicious. +Here we will describe the mechanics of how the Sequencer operates and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus, the mechanism thereby preserves censorship resistance even if the Sequencer is completely unresponsive or even malicious. Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. From 0af40052e4cf6788792f24796f6db38b1c5f4cb1 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:05:01 -0600 Subject: [PATCH 43/75] Update website/archive/12-nitro-vs-classic.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- website/archive/12-nitro-vs-classic.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/archive/12-nitro-vs-classic.mdx b/website/archive/12-nitro-vs-classic.mdx index 00c9cbde1..5f2076ac1 100644 --- a/website/archive/12-nitro-vs-classic.mdx +++ b/website/archive/12-nitro-vs-classic.mdx @@ -44,4 +44,4 @@ In short, there’s no better way to achieve Ethereum compatibility than to reus Having code that is as simple and easy to reason about as possible is important for L2 systems, which are inevitably complex. The classic stack represents a large codebase built in-house, which requires a fair amount of time and overhead to understand. The AVM together with ArbOS effectively constitute a full blockchain protocol built from the ground up. Since the AVM was custom-built, with no high-level languages yet created for it, the ArbOS logic had to be implemented in what was essentially a custom language — called “mini” — along with a mini-to-AVM compiler. -Nitro’s direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini), is much slimmer than in the classic stack; since the work of emulating the EVM is now handled by the Geth software, ArbOS needs only to implement the things specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours that have been put into an Ethereum-Geth itself — makes it a system that’s far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. \ No newline at end of file +Nitro’s direct usage of Geth means most of the work of creating an L2 VM is inherited right out of the box. The ArbOS custom logic (which, happily, can now be written in Go instead of mini) is much slimmer than in the classic stack; since the Geth software now handles the work of emulating the EVM, ArbOS needs only to implement the things specific and necessary for layer 2 (i.e., L1/L2 gas accounting, special message types for cross-chain transactions, etc.) Leaner, simpler code — much of which directly inherits engineering hours that have been put into an Ethereum-Geth itself — makes it a system that’s far more accessible for auditors and contributors, giving us strong confidence in its implementation security that will only harden as the ecosystem grows. \ No newline at end of file From 73e5ff39b40085053559f54c1651439c2e64da73 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:05:20 -0600 Subject: [PATCH 44/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 061558412..a1f63ab14 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -67,7 +67,7 @@ The second thing to understand about the rollup protocol is that _the protocol d You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. -With those preliminaries behind us, let’s jump into the details of the rollup protocol. +With those preliminaries behind us, let’s jump into the details of the Rollup protocol. The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). From 9de173c91f405108f87dd3081b48470b3b99cdbd Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:05:49 -0600 Subject: [PATCH 45/75] Update arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../data-availability-committees/01-get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx b/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx index 84370391f..18d9eb122 100644 --- a/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx +++ b/arbitrum-docs/run-arbitrum-node/data-availability-committees/01-get-started.mdx @@ -15,7 +15,7 @@ content_type: overview This section offers information and a series of how-to guides to help you along the process of setting up a Data Availability Committee. These guides target two audiences: Committee members who wish to deploy a Data Availability Server, and chain owners who wish to configure their chain with the information of the Committee. -Before following the guides in this section, you should be familiarized with how the AnyTrust protocol works, and the role of the DAC in the protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ to learn more. +Before following the guides in this section, you should be familiar with how the AnyTrust protocol works and the role of the DAC in the protocol. Refer to _[Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx)_ to learn more. ## If you are a DAC member From aefe149a7bce042b21b1538257c241be380f3918 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:07:10 -0600 Subject: [PATCH 46/75] Update arbitrum-docs/partials/_gentle-intro-partial.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/partials/_gentle-intro-partial.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/partials/_gentle-intro-partial.mdx b/arbitrum-docs/partials/_gentle-intro-partial.mdx index 02beeb9b0..0c661945d 100644 --- a/arbitrum-docs/partials/_gentle-intro-partial.mdx +++ b/arbitrum-docs/partials/_gentle-intro-partial.mdx @@ -37,7 +37,7 @@ Additionally, as long as there’s even just one honest validator, the chain wil #### Q: And how exactly is “fraud” “proven”? Sounds complicated. -Oh, it’s not so bad. In essence: if two validators disagree, only one of them (at most) can be telling the truth. In a dispute, the two validators play an interactive, call-and-response game, in which they narrow down their dispute to a single computational step (think of something small and simple, like multiplying two numbers). This one step gets executed on L1, and will, by necessity, prove that the honest party was telling the truth. For a more detailed rundown, see [here](/how-arbitrum-works/07-interactive-fraud-proofs.mdx). +Oh, it’s not so bad. In essence, if two validators disagree, only one of them (at most) can be telling the truth. In a dispute, the two validators play an interactive, call-and-response game in which they narrow down their dispute to a single computational step (think of something small and simple, like multiplying two numbers). This one step gets executed on L1 and will necessarily prove that the honest party was telling the truth. For a more detailed rundown, see [here](/how-arbitrum-works/07-interactive-fraud-proofs.mdx). #### Q: This dispute game obviously takes some time; does this impose any sort of delay on Arbitrum users' transactions? From 6a3d68de91f0b27a060b42a5324c4197382fae03 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:08:44 -0600 Subject: [PATCH 47/75] Update arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../launch-orbit-chain/how-tos/orbit-chain-finality.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md index 909db9377..eaab369ea 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md +++ b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md @@ -13,7 +13,7 @@ Generally, transactions executed through the sequencer on Orbit chains [achieve ## Parent chain → child chain transactions -Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/03-sequencer.mdx#if-the-sequencer-is-well-behaved) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that promoted it to achieve finality. +Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/03-sequencer.mdx#if-the-sequencer-is-well-behaved) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that prompted it to achieve finality. Orbit L3s may configure the finality of transactions executed through the delayed inbox to depend on different layers of finality. By default, Orbit chains will rely on the number of L1 block confirmations, effectively finalizing an L3 deposit as soon as L1 finalizes the batch posted by Arbitrum One or when a DACert is posted by Arbitrum Nova. This would be on the order of tens of minutes. From 7ff54d40c652459a6a5163adb77dfb687d978a35 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:08:59 -0600 Subject: [PATCH 48/75] Update arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../launch-orbit-chain/concepts/custom-gas-token-sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md b/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md index 7d72603b7..3783368bd 100644 --- a/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md +++ b/arbitrum-docs/launch-orbit-chain/concepts/custom-gas-token-sdk.md @@ -21,7 +21,7 @@ Custom gas token support in the Arbitrum SDK introduces a suite of APIs designed 2. **Erc20Bridger Context:** - **APIs:** `getApproveGasTokenRequest` and `approveGasToken`. - - **Purpose:** In the scenario of bridging ERC20 assets to an Orbit chain, these APIs play a crucial role. Token Bridging on Arbitrum Nitro stack uses Retryable tickets and needs specific amount of fees to be paid for creation and redemption of the ticket. For more information about retryable tickets please take a look at [this](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets) part of our docs. The Orbit chain operates as a custom gas token network, necessitating the payment of fees in native tokens for the creation of retryable tickets and their redemption on the Orbit chain. To cover the submission and execution fees associated with retryable tickets on the Orbit chain, an adequate amount of native tokens must be approved and allocated on the parent chain to cover the fees. + - **Purpose:** In the scenario of bridging ERC20 assets to an Orbit chain, these APIs play a crucial role. Token Bridging on Arbitrum Nitro stack uses retryable tickets and needs a specific fee to be paid for the creation and redemption of the ticket. For more information about retryable tickets, please take a look at [our chapter about retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets) part of our docs. The Orbit chain operates as a custom gas token network, necessitating the payment of fees in native tokens for the creation of retryable tickets and their redemption on the Orbit chain. To cover the submission and execution fees associated with retryable tickets on the Orbit chain, an adequate number of native tokens must be approved and allocated to the parent chain to cover the fees. - **Note** that you should use `Erc20Bridger` when bridging an ERC-20 token between the parent chain and the orbit chain. **Note** that these APIs are just needed for `custom gas token` orbit chains and for ETH-powered rollup and anytrust orbit chains, you don't need to use them. From 0e8f8e56461222f05e9611eaa3c0997ca4bae2cc Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:09:10 -0600 Subject: [PATCH 49/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index a1f63ab14..725035ae1 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -11,7 +11,7 @@ Arbitrum is an optimistic rollup. Let’s unpack that term. _Rollup_ -Arbitrum is a rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. +Arbitrum is a Rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. From e01066e9845bec7c95c78a964f466f814c99973b Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:09:33 -0600 Subject: [PATCH 50/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 725035ae1..7683a4e86 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -57,7 +57,7 @@ Much of the design of Arbitrum is driven by the opportunities opened up by inter ## Arbitrum Rollup Protocol -Before diving into the rollup protocol, there are two things we need to cover. +Before diving into the Rollup protocol, there are two things we need to cover. First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. From 4d574246811d0156e82ed5846be490942ce8ed06 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:09:43 -0600 Subject: [PATCH 51/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 7683a4e86..dfe51e4f8 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -59,7 +59,7 @@ Much of the design of Arbitrum is driven by the opportunities opened up by inter Before diving into the Rollup protocol, there are two things we need to cover. -First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. +First, _if you’re an Arbitrum user or developer, you don’t need to understand the Rollup protocol_. You don’t ever need to think about it unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, and you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! From fe7cecd18e7f3ac72f7cc9d8063596514c771011 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:09:54 -0600 Subject: [PATCH 52/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index dfe51e4f8..ea5c7935c 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -61,7 +61,7 @@ Before diving into the Rollup protocol, there are two things we need to cover. First, _if you’re an Arbitrum user or developer, you don’t need to understand the Rollup protocol_. You don’t ever need to think about it unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, and you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. -You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! +You’re welcome to study, observe, and even participate in the Rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) From 6c073884b097433ee32b769267a7be097192647f Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:10:05 -0600 Subject: [PATCH 53/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index ea5c7935c..767a268b9 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -63,7 +63,7 @@ First, _if you’re an Arbitrum user or developer, you don’t need to understan You’re welcome to study, observe, and even participate in the Rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! -The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) +The second thing to understand about the Rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable, and Arbitrum nodes will report that your transaction has been completed. The role of the Rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the Rollup protocol.) You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. From 0805a8de4a926b913d817af5b7516a60326c2cd5 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:10:17 -0600 Subject: [PATCH 54/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 767a268b9..ae20b810b 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -65,7 +65,7 @@ You’re welcome to study, observe, and even participate in the Rollup protocol, The second thing to understand about the Rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable, and Arbitrum nodes will report that your transaction has been completed. The role of the Rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the Rollup protocol.) -You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. +You might wonder why we need the Rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The Rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed, Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. However, once the result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum, such as processing withdrawals of funds from Nitro back to L1. With those preliminaries behind us, let’s jump into the details of the Rollup protocol. From 0bf325839d4ebe27edc32483797564dbb1cb70ef Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:10:50 -0600 Subject: [PATCH 55/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index ae20b810b..2ed4cd527 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -71,7 +71,7 @@ With those preliminaries behind us, let’s jump into the details of the Rollup The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). -The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. +The key security property of the Rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that the execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire), can force your transactions to be processed correctly. And that is true, no matter how many malicious people are trying to stop you. ### The Rollup Chain From 00548182e1c6ae7409fa9e0876c710c5901350e2 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:11:13 -0600 Subject: [PATCH 56/75] Update arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../05-separating-execution-from-proving.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 26819a1ca..054b83595 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -15,13 +15,13 @@ Separately, for _proving_, the portion of the code that is the State Transition ## WAVM -The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. +The Wasm format has many features that make it a good vehicle for fraud proofs—it is portable, structured, well-specified, and has reasonably good tools and support. However, it needs a few modifications to do the job completely. Nitro uses a slightly modified version of Wasm, which we call WAVM. A simple transformation stage turns the Wasm code produced by the Go compiler into WAVM code suitable for proving. -WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. +WAVM differs from wasm in three main ways. First, WAVM removes some wasm features that the Go compiler does not generate; the transformation phase verifies that these features are not present. -Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. +Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed-cost instructions. These transformations simplify proving. -Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. +Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to execute the State Transition Function. ## ReadPreImage and the Hash Oracle Trick From 16ee8211ebf9a2bdcc13ef21fccd20b0a198b77c Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:11:22 -0600 Subject: [PATCH 57/75] Update arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- .../05-separating-execution-from-proving.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 054b83595..6d00d1980 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -7,9 +7,9 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. +One of the challenges in designing a practical Rollup system is the tension between wanting the system to perform well in ordinary execution versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. -When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) +When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form and as a Docker image containing a compiled binary.) Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. From b9dd00252c44bfeb70ffa089447be3fec9d22868 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:11:32 -0600 Subject: [PATCH 58/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 2ed4cd527..700a3f6b4 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -75,9 +75,9 @@ The key security property of the Rollup protocol is that any one honest validato ### The Rollup Chain -The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. +The Rollup protocol tracks a chain of Rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. -Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. +Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually, every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. :::note From 6097b4dfab6ee7842aabad9bc96119f0b76fe7c5 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:11:40 -0600 Subject: [PATCH 59/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 700a3f6b4..4e154344a 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -251,7 +251,7 @@ Arbitrum full nodes normally "live at Layer 2" which means that they don’t wor Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. -Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. +Not all nodes will choose to do this. Because the Rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. Offchain Labs provides open source validator software, including a pre-built Docker image. From 2e392642b071a79f5a2dc7d73da040615d91432b Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:11:50 -0600 Subject: [PATCH 60/75] Update arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 4e154344a..0d425b638 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -259,7 +259,7 @@ Every validator can choose their own approach, but we expect validators to follo - The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. - The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. -- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) +- The _watchtower validator_ strategy never bonds. It simply watches the Rollup protocol, and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond and that that can happen before the dishonest RBlock’s deadline expires. (In practice, this will allow several days for a response.) Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. From 16bbbe518f321e0f3898c54db44c02181e9c0995 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:22:16 -0600 Subject: [PATCH 61/75] fixing link --- .../arbitrum-vs-ethereum/01-comparison-overview.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index edb256b25..97ff4b1a7 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -26,7 +26,7 @@ You can deploy Solidity contracts onto Arbitrum just like you do Ethereum. There ## Fees -The fees for executing an Arbitrum transaction work like gas fees on Ethereum. However, Arbitrum transactions must also pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitral-works/09-gas-fees.mdx) and L1 pricing. +The fees for executing an Arbitrum transaction work like gas fees on Ethereum. However, Arbitrum transactions must also pay a fee component to cover the cost of posting their calldata to the parent chain (for example, calldata on Arbitrum One, an L2, is posted to Ethereum, an L1). Find more information about the two components of gas fees in [Gas and fees](/how-arbitrum-works/09-gas-fees.mdx) and L1 pricing. ## Cross-chain messaging From b83ddf6addc2d91af7fdc5870025d15c75d4d27a Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:37:00 -0600 Subject: [PATCH 62/75] stylus-by-example submodule outdated --- stylus-by-example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stylus-by-example b/stylus-by-example index 508777e63..262ea1344 160000 --- a/stylus-by-example +++ b/stylus-by-example @@ -1 +1 @@ -Subproject commit 508777e63fb38b7d27426a8ae0f2ffacf1c184c9 +Subproject commit 262ea1344634b0c454c2e692cd87e7dc59d47a46 From 8ec5200da4292d0e956e4590602e6138c35fb04d Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 08:41:33 -0600 Subject: [PATCH 63/75] vercel broken link --- .../run-arbitrum-node/more-types/02-run-validator-node.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/run-arbitrum-node/more-types/02-run-validator-node.mdx b/arbitrum-docs/run-arbitrum-node/more-types/02-run-validator-node.mdx index c22e2b051..0eedba1ff 100644 --- a/arbitrum-docs/run-arbitrum-node/more-types/02-run-validator-node.mdx +++ b/arbitrum-docs/run-arbitrum-node/more-types/02-run-validator-node.mdx @@ -14,7 +14,7 @@ This page describes the different strategies a validator may follow and provides This how-to assumes that you're familiar with the following: - How to run a full node (see instructions [here](/run-arbitrum-node/03-run-full-node.mdx) for DAO-governed chains, and [here](/node-running/how-tos/running-an-orbit-node.mdx) for Orbit chains) -- [How the Rollup protocol works](/how-arbitrum-works/inside-arbitrum-nitro.mdx#arbitrum-rollup-protocol) +- [How the Rollup protocol works](/how-arbitrum-works/06-optimistic-rollup.mdx) - [How BoLD works](/bold/concepts/bold-technical-deep-dive.mdx#how-bold-uses-ethereum), if you're running a validator for a chain that has BoLD activated ## Validation strategies From 6c4bedd333e04a4b8a83591c214c6fd0e0cfcc57 Mon Sep 17 00:00:00 2001 From: Pete Date: Tue, 17 Dec 2024 12:54:06 -0600 Subject: [PATCH 64/75] updated vercel.json links --- vercel.json | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/vercel.json b/vercel.json index b13e7e1f6..0907b16ec 100644 --- a/vercel.json +++ b/vercel.json @@ -12,52 +12,52 @@ }, { "source": "/(proving/challenge-manager/?)", - "destination": "/how-arbitrum-works/fraud-proofs/challenge-manager", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/osp-assumptions/?)", - "destination": "/how-arbitrum-works/fraud-proofs/osp-assumptions", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wasm-to-wavm/?)", - "destination": "/how-arbitrum-works/fraud-proofs/wasm-wavm", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-custom-opcodes/?)", - "destination": "/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-floats/?)", - "destination": "/how-arbitrum-works/fraud-proofs/wavm-floats", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-modules/?)", - "destination": "/how-arbitrum-works/fraud-proofs/wavm-modules", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/(arbos/geth/?)", - "destination": "/how-arbitrum-works/arbos/geth", + "destination": "/how-arbitrum-works/04-geth-at-the-core", "permanent": false }, { "source": "/(arbos/?)", - "destination": "/how-arbitrum-works/arbos/introduction", + "destination": "/how-arbitrum-works/04-geth-at-the-core", "permanent": false }, { "source": "/(arbos/l2-to-l1-messaging/?)", - "destination": "/how-arbitrum-works/arbos/l2-l1-messaging", + "destination": "/how-arbitrum-works/11-l2-to-l1-messaging", "permanent": false }, { "source": "/(arbos/l1-to-l2-messaging/?)", - "destination": "/how-arbitrum-works/arbos/l1-l2-messaging", + "destination": "/how-arbitrum-works/10-l1-to-l2-messaging", "permanent": false }, { @@ -67,32 +67,32 @@ }, { "source": "/(inside-arbitrum-nitro/?)", - "destination": "/how-arbitrum-works/inside-arbitrum-nitro", + "destination": "/how-arbitrum-works/01-a-gentle-introduction", "permanent": false }, { "source": "/(arbos/l1-pricing/?)", - "destination": "/how-arbitrum-works/l1-gas-pricing", + "destination": "/how-arbitrum-works/09-gas-fees", "permanent": false }, { "source": "/(arbos/gas/?)", - "destination": "/how-arbitrum-works/gas-fees", + "destination": "/how-arbitrum-works/09-gas-fees", "permanent": false }, { "source": "/(inside-anytrust/?)", - "destination": "/how-arbitrum-works/inside-anytrust", + "destination": "/how-arbitrum-works/08-anytrust-protocol", "permanent": false }, { "source": "/(why-nitro/?)", - "destination": "/how-arbitrum-works/why-nitro", + "destination": "/how-arbitrum-works/01-a-gentle-introduction", "permanent": false }, { "source": "/(tx-lifecycle/?)", - "destination": "/how-arbitrum-works/tx-lifecycle", + "destination": "/how-arbitrum-works/02-transaction-lifecycle", "permanent": false }, { @@ -462,12 +462,12 @@ }, { "source": "/docs/inside_arbitrum", - "destination": "/how-arbitrum-works/inside-arbitrum-nitro", + "destination": "/how-arbitrum-works/01-a-gentle-introduction", "permanent": false }, { "source": "/docs/l1_l2_messages", - "destination": "/how-arbitrum-works/arbos/l1-l2-messaging", + "destination": "/how-arbitrum-works/10-l1-to-l2-messaging", "permanent": false }, { @@ -492,7 +492,7 @@ }, { "source": "/docs/anytrust", - "destination": "/how-arbitrum-works/inside-anytrust", + "destination": "/how-arbitrum-works/08-anytrust-protocol", "permanent": false }, { @@ -597,12 +597,12 @@ }, { "source": "/docs/finality", - "destination": "/how-arbitrum-works/tx-lifecycle", + "destination": "/how-arbitrum-works/02-transaction-lifecycle", "permanent": false }, { "source": "/docs/withdrawals", - "destination": "/how-arbitrum-works/tx-lifecycle", + "destination": "/how-arbitrum-works/02-transaction-lifecycle", "permanent": false }, { @@ -642,22 +642,22 @@ }, { "source": "/docs/censorship_resistance", - "destination": "/how-arbitrum-works/sequencer", + "destination": "/how-arbitrum-works/03-sequencer", "permanent": false }, { "source": "/docs/arbgas", - "destination": "/how-arbitrum-works/gas-fees", + "destination": "/how-arbitrum-works/09-gas-fees", "permanent": false }, { "source": "/docs/arbos", - "destination": "/how-arbitrum-works/arbos/introduction", + "destination": "/how-arbitrum-works/04-geth-at-the-core", "permanent": false }, { "source": "/docs/tx_lifecycle", - "destination": "/how-arbitrum-works/tx-lifecycle", + "destination": "/how-arbitrum-works/02-transaction-lifecycle", "permanent": false }, { @@ -667,22 +667,22 @@ }, { "source": "/docs/avm_design", - "destination": "/how-arbitrum-works/inside-arbitrum-nitro", + "destination": "/how-arbitrum-works/01-a-gentle-introduction", "permanent": false }, { "source": "/docs/dispute_resolution", - "destination": "/how-arbitrum-works/fraud-proofs/challenge-manager", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { "source": "/docs/arbos_formats", - "destination": "/how-arbitrum-works/arbos/introduction", + "destination": "/how-arbitrum-works/04-geth-at-the-core", "permanent": false }, { "source": "/docs/avm_specification", - "destination": "/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes", + "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", "permanent": false }, { From 87bb4ac319a93c2545fca1e86d72bc2cbfbf261f Mon Sep 17 00:00:00 2001 From: Pete Date: Mon, 23 Dec 2024 10:33:37 -0600 Subject: [PATCH 65/75] addressing comments --- .../02-transaction-lifecycle.mdx | 2 +- .../how-arbitrum-works/03-sequencer.mdx | 64 +++++------ .../06-optimistic-rollup.mdx | 4 - .../07-interactive-fraud-proofs.mdx | 2 +- .../how-tos/orbit-chain-finality.md | 4 +- vercel.json | 103 ++++++++++-------- .../12-nitro-vs-classic.mdx | 0 .../how-arbitrum-works/assertion-tree.mdx | 0 .../inside-arbitrum-nitro.mdx | 0 website/sidebars.js | 8 +- 10 files changed, 94 insertions(+), 93 deletions(-) rename website/archive/{ => how-arbitrum-works}/12-nitro-vs-classic.mdx (100%) rename {arbitrum-docs => website/archive}/how-arbitrum-works/assertion-tree.mdx (100%) rename website/archive/{ => how-arbitrum-works}/inside-arbitrum-nitro.mdx (100%) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 63372d2bf..6c0b3f459 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -143,7 +143,7 @@ L1 contracts also keep track of the tree of all assertions; i.e., how many stake **See:** -- [Assertion Tree Protocol](/how-arbitrum-works/assertion-tree.mdx) +- [Assertion Tree Protocol](/how-arbitrum-works/06-optimistic-rollup.mdx) #### ~ ~ ~ FINALITY CHECK: STILL THE SAME Ethereum-Equivalent Finality! ~ ~ ~ diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index e8fe7858a..cd37e866e 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -17,7 +17,16 @@ Clients interact with the Sequencer in exactly the same way they would interact ## The Core Inbox -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). + +When we add a Sequencer, the operation of the inbox changes. + +- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) +- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. + - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. + - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) + +The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. ## Happy/Common Case: Sequencer Is Live and Well-behaved @@ -26,6 +35,16 @@ Here, we start by assuming that the Sequencer is fully operational, and is runni If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. Once posted in a batch, the transactions have L1-level finality. +Transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. + +This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. + +So a Sequencer is generally a win, if the Sequencer is well behaved. + +### Cross chain communication + +As we mentioned, transactions can be received from L1 and L2. A cross chain message will eventually result in a transaction being executed on the destination chain. + ### L1 contracts can submit L2 transactions An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. @@ -74,7 +93,13 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. -### How the Sequencer Publishes the Sequence +::: info + +On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to [decentralized, fair sequencing](#decentralized-fair-sequencing). + +::: + +## How the Sequencer Publishes the Sequence So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. @@ -84,41 +109,6 @@ The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. -## Instant confirmation - -Without a Sequencer, a node can predict what the results of a client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. - -The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. - -## Inboxes, fast and slow - -When we add a Sequencer, the operation of the inbox changes. - -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) -- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. - - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) - -## If the Sequencer is well-behaved... - -A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. - -It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. - -This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. - -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. - -So a Sequencer is generally a win, if the Sequencer is well behaved. - -## If the Sequencer is malicious... - -A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. - -On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. - -Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. - ## Decentralized fair sequencing Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index 0d425b638..a2f3a9066 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -241,10 +241,6 @@ Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple d The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. -### Detailed Spec - -For a more detailed breakdown / specification of the assertion tree protocol, see [Inside Arbitrum](/how-arbitrum-works/assertion-tree.mdx). - ## Validators Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 5e6cc4151..309a6bed4 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -1,5 +1,5 @@ --- -title: Challenges/:/ Interactive Fraud Proofs +title: 'Challenges: Interactive Fraud Proofs' description: Learn the fundamentals of Arbitrum's Interactive Fraud Proofs and challenges. author: pete-vielhaber sme: TucksonDev diff --git a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md index eaab369ea..068d6e54a 100644 --- a/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md +++ b/arbitrum-docs/launch-orbit-chain/how-tos/orbit-chain-finality.md @@ -9,11 +9,11 @@ content_type: how-to ## Child chain transactions -Generally, transactions executed through the sequencer on Orbit chains [achieve finality](/how-arbitrum-works/02-transaction-lifecycle.mdx) equivalent to their parent chain once the relevant transaction data has been [posted in a batch](/how-arbitrum-works/03-sequencer.mdx). This means that transactions on Orbit chains are considered final in minutes. +Generally, transactions executed through the sequencer on Orbit chains [achieve finality](/how-arbitrum-works/02-transaction-lifecycle.mdx) equivalent to their parent chain once the relevant transaction data has been [posted in a batch](/how-arbitrum-works/06-optimistic-rollup.mdx). This means that transactions on Orbit chains are considered final in minutes. ## Parent chain → child chain transactions -Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/03-sequencer.mdx#if-the-sequencer-is-well-behaved) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that prompted it to achieve finality. +Messages being sent through the delayed inbox of a parent chain as [retryable tickets](/how-arbitrum-works/10-l1-to-l2-messaging.mdx#retryable-tickets), including deposits through token bridges, are released by the [sequencer](/how-arbitrum-works/03-sequencer.mdx) once it has reasonable confidence of finality on the parent chain. For example, on an L2 chain settling to Ethereum, the sequencer will release delayed messages to the inbox after 40 blocks. Following this, the transaction must complete another finality period for the Ethereum transaction that prompted it to achieve finality. Orbit L3s may configure the finality of transactions executed through the delayed inbox to depend on different layers of finality. By default, Orbit chains will rely on the number of L1 block confirmations, effectively finalizing an L3 deposit as soon as L1 finalizes the batch posted by Arbitrum One or when a DACert is posted by Arbitrum Nova. This would be on the order of tens of minutes. diff --git a/vercel.json b/vercel.json index 0907b16ec..eb60b10ab 100644 --- a/vercel.json +++ b/vercel.json @@ -12,87 +12,97 @@ }, { "source": "/(proving/challenge-manager/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/osp-assumptions/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wasm-to-wavm/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-custom-opcodes/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-floats/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(proving/wavm-modules/?)", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { "source": "/(arbos/geth/?)", - "destination": "/how-arbitrum-works/04-geth-at-the-core", + "destination": "/how-arbitrum-works/geth-at-the-core", "permanent": false }, { "source": "/(arbos/?)", - "destination": "/how-arbitrum-works/04-geth-at-the-core", + "destination": "/how-arbitrum-works/geth-at-the-core", "permanent": false }, { "source": "/(arbos/l2-to-l1-messaging/?)", - "destination": "/how-arbitrum-works/11-l2-to-l1-messaging", + "destination": "/how-arbitrum-works/l2-to-l1-messaging", "permanent": false }, { "source": "/(arbos/l1-to-l2-messaging/?)", - "destination": "/how-arbitrum-works/10-l1-to-l2-messaging", + "destination": "/how-arbitrum-works/l1-to-l2-messaging", "permanent": false }, { "source": "/(assertion-tree/?)", - "destination": "/how-arbitrum-works/assertion-tree", + "destination": "/how-arbitrum-works/optimistic-rollup", + "permanent": false + }, + { + "source": "/(how-arbitrum-works/assertion-tree/?)", + "destination": "/how-arbitrum-works/optimistic-rollup", "permanent": false }, { "source": "/(inside-arbitrum-nitro/?)", - "destination": "/how-arbitrum-works/01-a-gentle-introduction", + "destination": "/how-arbitrum-works/a-gentle-introduction", + "permanent": false + }, + { + "source": "/(how-arbitrum-works/inside-arbitrum-nitro/?)", + "destination": "/how-arbitrum-works/a-gentle-introduction", "permanent": false }, { "source": "/(arbos/l1-pricing/?)", - "destination": "/how-arbitrum-works/09-gas-fees", + "destination": "/how-arbitrum-works/gas-fees", "permanent": false }, { "source": "/(arbos/gas/?)", - "destination": "/how-arbitrum-works/09-gas-fees", + "destination": "/how-arbitrum-works/gas-fees", "permanent": false }, { "source": "/(inside-anytrust/?)", - "destination": "/how-arbitrum-works/08-anytrust-protocol", + "destination": "/how-arbitrum-works/anytrust-protocol", "permanent": false }, { "source": "/(why-nitro/?)", - "destination": "/how-arbitrum-works/01-a-gentle-introduction", + "destination": "/how-arbitrum-works/a-gentle-introduction", "permanent": false }, { "source": "/(tx-lifecycle/?)", - "destination": "/how-arbitrum-works/02-transaction-lifecycle", + "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, { @@ -461,13 +471,18 @@ "permanent": false }, { - "source": "/docs/inside_arbitrum", - "destination": "/how-arbitrum-works/01-a-gentle-introduction", + "source": "/(docs/inside_arbitrum/?)", + "destination": "/how-arbitrum-works/a-gentle-introduction", + "permanent": false + }, + { + "source": "/(how-arbitrum-works/sequencer/?)", + "destination": "/how-arbitrum-works/sequencer", "permanent": false }, { - "source": "/docs/l1_l2_messages", - "destination": "/how-arbitrum-works/10-l1-to-l2-messaging", + "source": "/(docs/l1_l2_messages/?)", + "destination": "/how-arbitrum-works/l1-to-l2-messaging", "permanent": false }, { @@ -491,7 +506,7 @@ "permanent": false }, { - "source": "/docs/anytrust", + "source": "/(docs/anytrust/?)", "destination": "/how-arbitrum-works/08-anytrust-protocol", "permanent": false }, @@ -596,13 +611,13 @@ "permanent": false }, { - "source": "/docs/finality", - "destination": "/how-arbitrum-works/02-transaction-lifecycle", + "source": "/(docs/finality/?)", + "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, { - "source": "/docs/withdrawals", - "destination": "/how-arbitrum-works/02-transaction-lifecycle", + "source": "/(docs/withdrawals/?)", + "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, { @@ -641,48 +656,48 @@ "permanent": false }, { - "source": "/docs/censorship_resistance", - "destination": "/how-arbitrum-works/03-sequencer", + "source": "/(docs/censorship_resistance/?)", + "destination": "/how-arbitrum-works/sequencer", "permanent": false }, { - "source": "/docs/arbgas", - "destination": "/how-arbitrum-works/09-gas-fees", + "source": "/(docs/arbgas/?)", + "destination": "/how-arbitrum-works/gas-fees", "permanent": false }, { - "source": "/docs/arbos", - "destination": "/how-arbitrum-works/04-geth-at-the-core", + "source": "/(docs/arbos/?)", + "destination": "/how-arbitrum-works/geth-at-the-core", "permanent": false }, { - "source": "/docs/tx_lifecycle", - "destination": "/how-arbitrum-works/02-transaction-lifecycle", + "source": "/(docs/tx_lifecycle/?)", + "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, { - "source": "/docs/rollup_protocol", - "destination": "/how-arbitrum-works/assertion-tree", + "source": "/(docs/rollup_protocol/?)", + "destination": "/how-arbitrum-works/optimistic-rollup", "permanent": false }, { - "source": "/docs/avm_design", - "destination": "/how-arbitrum-works/01-a-gentle-introduction", + "source": "/(docs/avm_design/?)", + "destination": "/how-arbitrum-works/a-gentle-introduction", "permanent": false }, { - "source": "/docs/dispute_resolution", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "source": "/(docs/dispute_resolution/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { - "source": "/docs/arbos_formats", - "destination": "/how-arbitrum-works/04-geth-at-the-core", + "source": "/(docs/arbos_formats/?)", + "destination": "/how-arbitrum-works/geth-at-the-core", "permanent": false }, { - "source": "/docs/avm_specification", - "destination": "/how-arbitrum-works/07-interactive-fraud-proofs", + "source": "/(docs/avm_specification/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, { diff --git a/website/archive/12-nitro-vs-classic.mdx b/website/archive/how-arbitrum-works/12-nitro-vs-classic.mdx similarity index 100% rename from website/archive/12-nitro-vs-classic.mdx rename to website/archive/how-arbitrum-works/12-nitro-vs-classic.mdx diff --git a/arbitrum-docs/how-arbitrum-works/assertion-tree.mdx b/website/archive/how-arbitrum-works/assertion-tree.mdx similarity index 100% rename from arbitrum-docs/how-arbitrum-works/assertion-tree.mdx rename to website/archive/how-arbitrum-works/assertion-tree.mdx diff --git a/website/archive/inside-arbitrum-nitro.mdx b/website/archive/how-arbitrum-works/inside-arbitrum-nitro.mdx similarity index 100% rename from website/archive/inside-arbitrum-nitro.mdx rename to website/archive/how-arbitrum-works/inside-arbitrum-nitro.mdx diff --git a/website/sidebars.js b/website/sidebars.js index d300add97..f6f4849c8 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -824,12 +824,12 @@ const sidebars = { { type: 'doc', id: 'how-arbitrum-works/transaction-lifecycle', - label: 'Transaction Lifecycle', + label: 'Sequencing, Followed by Deterministic Execution', }, { type: 'doc', id: 'how-arbitrum-works/sequencer', - label: 'Sequencer', + label: 'The Sequencer and Censorship Resistance', }, { type: 'doc', @@ -849,7 +849,7 @@ const sidebars = { { type: 'doc', id: 'how-arbitrum-works/interactive-fraud-proofs', - label: 'Interactive Fraud Proofs', + label: 'Challenges: Interactive Fraud Proofs', }, { type: 'doc', @@ -859,7 +859,7 @@ const sidebars = { { type: 'doc', id: 'how-arbitrum-works/gas-fees', - label: 'Gas/fees', + label: 'Gas and fees', }, { type: 'doc', From 994a00418bec3999ced109222ded17e4b7c0fab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 23 Dec 2024 10:30:09 -0800 Subject: [PATCH 66/75] feat: add some quicklooks --- arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index cd37e866e..8c56f6ac9 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -7,21 +7,21 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. +The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. Here we will describe the mechanics of how the Sequencer operates and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus, the mechanism thereby preserves censorship resistance even if the Sequencer is completely unresponsive or even malicious. Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. +[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. ## The Core Inbox -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). When we add a Sequencer, the operation of the inbox changes. -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) +- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) - Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) From a3bc87bd0db0c1ff08a0689a265cbc20d37ad981 Mon Sep 17 00:00:00 2001 From: Pete Date: Mon, 23 Dec 2024 12:47:25 -0600 Subject: [PATCH 67/75] Update arbitrum-docs/how-arbitrum-works/03-sequencer.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gaël Blanchemain --- arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 8c56f6ac9..566f8674a 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -93,7 +93,7 @@ On top of the delay itself, the `forceInclusion` path has the downside of uncert While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. -::: info +:::info On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to [decentralized, fair sequencing](#decentralized-fair-sequencing). From b0bcdbb5f9680bd58546c83d27d5d324565f7633 Mon Sep 17 00:00:00 2001 From: Pete Date: Mon, 23 Dec 2024 15:21:01 -0600 Subject: [PATCH 68/75] edited vercel.json --- .../how-arbitrum-works/03-sequencer.mdx | 2 +- vercel.json | 55 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index cd37e866e..bdba8cc51 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -37,7 +37,7 @@ Once posted in a batch, the transactions have L1-level finality. Transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. +This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is faster; but if your message doesn't use the Sequencer, finality will be slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. So a Sequencer is generally a win, if the Sequencer is well behaved. diff --git a/vercel.json b/vercel.json index eb60b10ab..950b655a5 100644 --- a/vercel.json +++ b/vercel.json @@ -15,31 +15,61 @@ "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/challenge-manager/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(proving/osp-assumptions/?)", "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/osp-assumptions/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(proving/wasm-to-wavm/?)", "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/wasm-wavm/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(proving/wavm-custom-opcodes/?)", "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/wavm-custom-opcodes/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(proving/wavm-floats/?)", "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/wavm-floats/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(proving/wavm-modules/?)", "destination": "/how-arbitrum-works/interactive-fraud-proofs", "permanent": false }, + { + "source": "/(fraud-proofs/wavm-modules/?)", + "destination": "/how-arbitrum-works/interactive-fraud-proofs", + "permanent": false + }, { "source": "/(arbos/geth/?)", "destination": "/how-arbitrum-works/geth-at-the-core", @@ -65,11 +95,6 @@ "destination": "/how-arbitrum-works/optimistic-rollup", "permanent": false }, - { - "source": "/(how-arbitrum-works/assertion-tree/?)", - "destination": "/how-arbitrum-works/optimistic-rollup", - "permanent": false - }, { "source": "/(inside-arbitrum-nitro/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", @@ -85,6 +110,11 @@ "destination": "/how-arbitrum-works/gas-fees", "permanent": false }, + { + "source": "/(arbos/l1-gas-pricing/?)", + "destination": "/how-arbitrum-works/gas-fees", + "permanent": false + }, { "source": "/(arbos/gas/?)", "destination": "/how-arbitrum-works/gas-fees", @@ -95,6 +125,16 @@ "destination": "/how-arbitrum-works/anytrust-protocol", "permanent": false }, + { + "source": "/(arbos/l1-l2-messaging/?)", + "destination": "/how-arbitrum-works/l1-to-l2-messaging", + "permanent": false + }, + { + "source": "/(arbos/l2-l1-messaging/?)", + "destination": "/how-arbitrum-works/l2-to-l1-messaging", + "permanent": false + }, { "source": "/(why-nitro/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", @@ -615,6 +655,11 @@ "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, + { + "source": "/arbos/introduction", + "destination": "/how-arbitrum-works/geth-at-the-core", + "permanent": false + }, { "source": "/(docs/withdrawals/?)", "destination": "/how-arbitrum-works/transaction-lifecycle", From eaa5828c95070c12c64d74f4f3753ccd2d7cd2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 23 Dec 2024 16:00:32 -0800 Subject: [PATCH 69/75] fix: quicklooks errors --- arbitrum-docs/how-arbitrum-works/03-sequencer.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index c3ce71c57..df8f1c6f8 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -7,7 +7,7 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [Arbitrum One currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. +The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. Here we will describe the mechanics of how the Sequencer operates and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus, the mechanism thereby preserves censorship resistance even if the Sequencer is completely unresponsive or even malicious. @@ -17,7 +17,7 @@ Clients interact with the Sequencer in exactly the same way they would interact ## The Core Inbox -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see “[Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)”). +When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)). When we add a Sequencer, the operation of the inbox changes. From 906e76fbc7000223b756e30c29b72242aa45b244 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 26 Dec 2024 14:38:12 -0600 Subject: [PATCH 70/75] adding Medhi's changes for Transaction Lifecycle and Sequencing --- .../02-transaction-lifecycle.mdx | 160 ++---------- .../how-arbitrum-works/03-sequencer.mdx | 242 +++++++++++++----- 2 files changed, 209 insertions(+), 193 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 6c0b3f459..3e2b987b7 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -2,163 +2,51 @@ title: Sequencing, Followed by Deterministic Execution description: 'Learn the fundamentals of the Arbitrum Transaction Lifecycle, sequencing, and deterministic execution.' author: pete-vielhaber -sme: TucksonDev +sme: gmehta2 user_story: As a current or prospective Arbitrum user, I need to learn more about the transaction lifecycle. content_type: get-started --- -This diagram summarizes how transactions are processed in Nitro. +This section explores the various methods users can employ to submit transactions for inclusion on the Arbitrum chain. We discuss the different pathways available—sending transactions to the sequencer or bypassing it by submitting transactions through the delayed inbox contract on the parent chain. By outlining these options, we aim to clarify how users can interact with the network, detail the processes involved in each method, and identify the modules responsible for handling these transactions. This overview will enhance your understanding of the initial steps in Arbitrum ecosystem's transaction lifecycle and prepare you for a detailed exploration of transaction inclusion mechanisms in the subsequent sections. -![seq-then-exec](../assets/seq-then-exec.png) +The first subsection, **Submitting Transactions to the Sequencer**, presents four different methods users can utilize to send their transactions to the sequencer: via Public RPC, Third-Party RPC, Arbitrum Nodes, and the Sequencer Endpoint. Transactions sent through the first three pathways will route through our Load Balancer before reaching the sequencer. In contrast, the Sequencer Endpoint allows transactions to bypass the Load Balancer and be sent directly to the sequencer. -Let's follow a user's transaction through this process. +The second subsection, **Bypassing the Sequencer**, describes an alternative method where users can include their transactions on the Arbitrum chain without relying on the sequencer. By sending transactions directly to the delayed inbox contract on the parent chain (Layer 1), users gain additional flexibility, ensuring that their transactions can be processed even if the sequencer is unavailable or if they prefer not to use it. -First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. +This diagram illustrates the various pathways for submitting transactions to the Arbitrum chain. It highlights the options for sending transactions through the sequencer or bypassing it and using the delayed inbox contract on the parent chain. -Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. +## Submitting a transaction to the Sequencer +This section outlines the different methods for users to submit transactions to the sequencer on the Arbitrum chain. There are four primary ways to do this: Public RPC, Third-Party RPCs, Arbitrum Nodes, and the Sequencer Endpoint. We will explore these methods in detail, explaining when to choose one over the other and how to use each to effectively submit transactions to the Arbitrum sequencer. -Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) +### 1. Public RPC -The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. +Arbitrum provides public RPCs for its main chains: Arbitrum One, Arbitrum Nova, and Arbitrum Sepolia. Due to their rate-limited nature, these RPC endpoints are suitable for less resource-intensive operations. Public RPCs can be an accessible option for general use cases and light interactions with the network. -It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. +For more details on the specific RPC endpoints for each chain, please see [this section](https://docs.arbitrum.io/build-decentralized-apps/reference/node-providers#arbitrum-public-rpc-endpoints) of the documentation. -## Deep dive: The lifecycle of an Arbitrum transaction +### 2. Third-Party RPC -We'll now go step-by-step over the phases an Arbitrum transaction goes through, starting with a client creating a signed transaction, to it ultimately being confirmed back on layer 1. +Users also have the option to interact with Arbitrum's public chains using third-party node providers. These providers are often the same popular ones used for Ethereum, making them reliable choices for resource-intensive operations. We recommend using these third-party providers when performance and scalability are critical. -We'll also intersperse it with "finality checks," explaining what guarantees the client has over their transaction's finality (i.e., assurances that their transaction's result is guaranteed and won't later be altered) over the course of a transaction's various stages. +You can find a list of supported third-party providers [here](https://docs.arbitrum.io/build-decentralized-apps/reference/node-providers#third-party-rpc-providers). -This section will focus on the Arbitrum Rollup protocol; see [Inside AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the security properties of the core system itself; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization) for current decentralization status. +### 3. Arbitrum Nodes -For clarity on any terminology that may be unfamiliar, see our [glossary](/intro/glossary.mdx). +Another approach for sending transactions to the sequencer is through self-hosted Arbitrum nodes. Running a node gives you direct control over your transactions, which go to the sequencer via the sequencer feed. -### 1. Sequencer receives transaction +. Please see the [Arbitrum Node](https://docs.arbitrum.io/run-arbitrum-node/overview) documentation to learn more about setting up and running a node. -Typically, a transaction's lifecycle starts with the Sequencer, the entity designated with transaction ordering, receiving a transaction from a client. The Sequencer can receive a transaction one of two ways: +### 4. Sequencer Endpoint -##### 1a. Directly / Offchain +The Sequencer Endpoint is the most direct method for users looking to minimize delays in their transactions reaching the sequencer. Unlike standard RPC URLs, the Sequencer Endpoint supports only `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls, bypassing the load balancer entirely. This endpoint makes it an optimal choice for users who require the quickest transaction processing time. -For typical transacting within the L2 environment (i.e., using an L2 native dapp), a client will connect their wallet to an L2 node and directly deliver a signed transaction. +## Bypassing the Sequencer +This section delves into an alternative method for submitting transactions to the Arbitrum chain, bypassing the sequencer. This page focuses on how users can send their transactions directly to the delayed inbox contract on the parent chain rather than through the sequencer. This method offers two distinct paths a transaction can take, with each route interacting with the network differently to achieve transaction inclusion. This approach provides users with greater flexibility and ensures that transactions can still be processed if the sequencer is unavailable or if users choose not to depend on it. This section highlights these alternative submission mechanisms and underscores the robustness and decentralization features inherent in the Arbitrum network. -##### 1b. ... or from L1 (via the Delayed Inbox). +In **Diagram 3**, we demonstrate how users can submit their transactions using the delayed inbox contract to bypass the sequencer. As illustrated in the diagram, there are two possible paths for transaction handling. When a transaction is submitted to the delayed inbox, the sequencer may automatically pick it up, include it as an ordered transaction, and send it to the sequencer feed. However, if the sequencer does not process the transaction within 24 hours, users have the reliable option to call the `forceInclude` function on the sequencer inbox contract. This action ensures that the sequencer to picks up the transaction and includes it in the ordered transaction list, providing users with a sense of security about their transactions. -Alternatively, a client can send a message to the Sequencer by signing and publishing an L1 transaction in the Arbitrum chain's Delayed Inbox. This functionality is most commonly used for depositing ETH or tokens via a bridge. +To send a transaction to the delayed inbox instead of submitting it to the sequencer, users can construct their transaction and then call the [`sendL2Message`](https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/bridge/AbsInbox.sol#L150) function, passing the data of the serialized signed transaction as an argument. This function allows users to send a generic L2 message to the chain, suitable for any message that does not require L1 validation. -**See**: +If the sequencer is not back online within 24 hours or decides to censor the transaction, users can invoke the [`forceInclusion`](https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/bridge/SequencerInbox.sol#L284) function on the SequencerInbox contract. This action ensures their transaction is included on the chain, bypassing the sequencer's role. -- [Retryables](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) -- [The Sequencer](/how-arbitrum-works/03-sequencer.mdx) -- [Token Bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) - -### 2. Sequencer orders transaction (off-chain) - -Upon receiving a transaction, the Sequencer will: - -- Order it in its off-chain Inbox -- Locally execute it using the Arbitrum Nitro VM (including collecting/allocating L1 and L2 fees, etc.) -- "Instantly" give a transaction receipt to the client ("instant" in that it doesn't require any additional on-chain confirmations, and typically takes less than a second - with the average user experiencing ~260ms). - -**See**: - -- [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos) -- [Geth](/how-arbitrum-works/04-geth-at-the-core.mdx#geth) -- [L1 pricing](/how-arbitrum-works/09-gas-fees.mdx) / [L2 Gas](/how-arbitrum-works/09-gas-fees.mdx) - -#### ~ ~ ~ FINALITY CHECK: Trusted / Soft Confirmation ~ ~ ~ - -At this phase, the client's acceptance of finality relies on trusting the Sequencer, i.e., a malicious/faulty Sequencer could deviate between what it promised in the transaction receipt and what is ultimately published in a batch (see phase 3). - -:::note - -Even a malicious/faulty Sequencer can only, at worst, reorder or temporarily delay transactions; it cannot, e.g., forge a client's transaction or propose an invalid state update. Given the degree of trust in the Sequencer at phase 2, we sometimes refer to the "instant" receipt that the Sequencer provides as a "soft confirmation." - -::: - -### 3. Sequencer posts transaction in a batch (on-chain) - -The Sequencer will eventually post a batch of L2 transactions which includes our client's transaction onto the underlying L1 (as calldata); under normal conditions, the Sequencer will post batches [every few minutes](https://arbiscan.io/batches). - -##### 3a. What if the Sequencer never includes our transaction? - -Even if the Sequencer never includes our transaction in a batch, the client can include it in the L2 by posting in the delayed inbox and then "force including" it after some delay period (currently ~ @arbOneForceIncludePeriodHours@ hours on Arbitrum One). - -:::note - -The Sequencer is forced to include messages from the delayed Inbox in the queued order that they appear on chain, i.e. it processes messages using the "first in, first out" method. Thus, it can't selectively delay particular messages while including others; i.e., delaying the message at the front of the queue means delaying all messages behind it as well. - -::: - -**See:** - -- ["The Sequencer / Censorship Resistance."](/how-arbitrum-works/03-sequencer.mdx) - -#### ~ ~ ~ FINALITY CHECK: Ethereum-Equivalent Finality! ~ ~ ~ - -At this stage, assuming that a client believes there to be at least one well behaved active Arbitrum validator. Currently, the process of validation on the Arbitrum protocol is permissioned, but it's important to be aware that our latest dispute protocol, [BoLD (Bounded Liquidity Delay)](https://medium.com/offchainlabs/bold-permissionless-validation-for-arbitrum-chains-9934eb5328cc), has the potential to allow validation on Arbitrum chains without requiring permission, thereby potentially eliminating the necessity for restricted validation. The client can treat their transaction's finality as equivalent to an ordinary Ethereum transaction. In other words, their L2 transaction has the same finality as the L1 transaction that recorded it in a batch. This means the client should use whatever finality heuristic they use for regular Ethereum transactions (i.e., waiting on L1 block confirmations, etc.), applied to the L1 batch-posting transaction. This also means that a client uncomfortable with the trust model of the Sequencer's soft confirmations (phase 2) can simply wait for the Sequencer to post their transaction in a batch (phase 3). - -How are we able to make such bold a claim? A few (related) things: - -- Once the Sequencer posts a batch, its transactions' ordering is entirely determined by the L1; the Sequencer effectively has no more say in our transaction's lifecycle at all. -- The Inbox contract on L1 ensures that when the Sequencer posts a batch, it posts data sufficient for any Arbitrum Node to reconstruct and validate the state of the L2 chain; i.e., the availability of this "input" data is guaranteed by Ethereum itself. -- Execution on Arbitrum is fully deterministic; i.e., a current chain state along with new input data is sufficient to compute the new chain state; thus, the moment this input data is available (i.e., when the Sequencer posts a batch), the L2 chain's state can be computed. -- Arbitrum's fault-proof system is sound; i.e., if any validator (later) tries to deviate from the valid L2 state, an honest validator will ultimately be able to challenge this and win. Since we already know that valid state will ultimately win out, we can treat our transaction as L1-finalized now. - -### 4. Validator asserts RBlock that includes transaction - -A staked, active validator will then run the Arbitrum VM over the inputs in the Inbox (just like the Sequencer did earlier, except now only over transactions posted on L1) and make an on-chain assertion about the chain's latest state, i.e., a rollup block or "RBlock." RBlocks typically get asserted every 30-60 minutes. - -**See**: - -- [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos) -- [Geth](/how-arbitrum-works/04-geth-at-the-core.mdx#geth) -- [L1 pricing](/how-arbitrum-works/09-gas-fees.mdx) / [L2 Gas](/how-arbitrum-works/09-gas-fees.mdx) - -:::note - -RBlock assertions include claims about the state of the Outbox; if our transaction triggered any L2 to L1 messages, a RBlock will include an update to the Outbox to reflect its inclusion. - -::: - -**See**: - -- [The Outbox](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) - -#### 4a. RBlock is valid / goes unchallenged - -In the happy / common case, the validator asserted a valid RBlock, and over the course of the dispute window — 1 week on Arbitrum One — no other validators challenge it. - -#### 4b. Assertion is challenged! - -If two validators assert different RBlocks, only (at most) one of them can be valid, so they are put into a dispute. - -A dispute consists of two staked validators dissecting their disagreement down to a single L2 block, and then dissecting the sequence of VM instructions within this block down to a single OPCODE, then finally, executing this single operation. The underlying VM the Arbitrum uses is [WebAssembly (Wasm)](https://webassembly.org), or, more precisely, "WAVM." This is all refereed by contracts on L1. - -**See:** - -- [Challenges](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#challengemanager) -- [Wasm/WAVM](/how-arbitrum-works/07-interactive-fraud-proofs.mdx#wasm-to-wavm) - -L1 contracts also keep track of the tree of all assertions; i.e., how many stakers are in disagreement, who is currently disputing with whom, etc. We refer to this level of Arbitrum's design architecture as its "assertion tree protocol." - -**See:** - -- [Assertion Tree Protocol](/how-arbitrum-works/06-optimistic-rollup.mdx) - -#### ~ ~ ~ FINALITY CHECK: STILL THE SAME Ethereum-Equivalent Finality! ~ ~ ~ - -Remember in phase 3 when said that once the L1 has committed to inputs, we can guarantee the L2 output? We meant it! Even during a dispute, Arbitrum nodes continue to execute and active validators continue to make assertions on the valid leaf in the state-tree; nothing that can happen in phase 4 has any effect on the L1-level finality we've already locked in at phase 3. - -### 5. RBlock is confirmed on L1 - -Once any and all disputes have been resolved and sufficient time has passed, our RBlock can be confirmed on L1 (any Ethereum account on L1 can confirm it). Upon confirmation, the Outbox root on L1 gets updated. - -#### ~ ~ ~ FINALITY CHECK: L2-to-L1 Messages Executable on L1 ~ ~ ~ - -If our client's transaction didn't include any L2-to-L1 messages (e.g., withdrawals), phase 5 has no material effect on their transaction. If it did include an L2-to-L1 transaction, it is only after confirmation that the message can be executed in the Outbox on L1. - -:::note - -Even before phase 5, the client has L1 finality on the _result_ of their L2-to-L1 message, they just can't execute it yet; i.e., they have a guarantee that they'll eventually be able to, e.g., finalize their withdrawal, they just can't claim their funds on L1 until the RBlock is confirmed. - -::: +Additionally, the Arbitrum SDK provides the [`InboxTools`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L64C14-L64C24) class, which simplifies the process of submitting transactions to the delayed inbox. Users can utilize the [`sendChildSignedTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L401C16-L401C33) method to send the transaction and the [`forceInclude`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L367) method to ensure its inclusion. The SDK also offers helper methods like [`signChildTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L429) to assist with signing the transaction during the creation of the serialized signed transaction hex string, streamlining the entire process. \ No newline at end of file diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index df8f1c6f8..0c8e5fa05 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -2,115 +2,243 @@ title: The Sequencer and Censorship Resistance description: 'Learn the fundamentals of the Arbitrum Sequencer.' author: pete-vielhaber -sme: TucksonDev +sme: gmehta2 user_story: As a current or prospective Arbitrum user, I need to learn more about the Sequencer. content_type: get-started --- -The Sequencer is a specially designated Arbitrum full node which, under normal conditions, is responsible for submitting users’ transactions onto L1. In principle, a chain’s Sequencer can take different forms; as [currently stands](https://docs.arbitrum.foundation/state-of-progressive-decentralization), the Sequencer is a single, centralized entity; eventually, sequencing affordances could be given to a distributed committee of sequencers which come to consensus on ordering. However, regardless of its form, the Sequencer has a fundamental limitation that doesn’t apply to any other part of the system: it must operate under its own security assumptions; i.e., it can’t, in principle, derive security directly from layer 1. This brings up the question of how Arbitrum Rollup maintains its claim to censorship resistance when-and-if the Sequencer misbehaves. +The Sequencer is a pivotal component of the Arbitrum network and is responsible for efficiently ordering and processing transactions. It plays a crucial role in providing users with fast transaction confirmations while maintaining the security and integrity of the blockchain. In Arbitrum, the Sequencer orders incoming transactions and manages the batching, compression, and posting of transaction data to parent chain, optimizing costs and performance. -Here we will describe the mechanics of how the Sequencer operates and how any user can bypass the Sequencer entirely to submit any Arbitrum transaction (including one that, say, initiates an L2 to L1 message to withdraw funds) directly from layer 1. Thus, the mechanism thereby preserves censorship resistance even if the Sequencer is completely unresponsive or even malicious. +In this section, we will explore the operation of the Sequencer in detail. The topics covered include: -Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. +- **Sequencing and Broadcasting (Sequencer Feed)**: An overview of the real-time transaction feed provided by the Sequencer, which allows nodes to receive instant updates on the transaction sequence. +- **Batch-Posting**: How the Sequencer groups transactions into batches, compresses them to reduce data size and sends them to the Sequencer Inbox Contract on the parent chain. This section also delves into the L1 pricing model and how it affects transaction costs. + - **Batching** + - **Compression** + - **Submitting to Sequencer Inbox Contract** + - **L1 Pricing Model** +- **Finality**: Understanding how transaction finality is achieved in Arbitrum through both soft and hard finality mechanisms, ensuring that transactions are confirmed securely and reliably. (not as a sequencer task) -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. +By examining these aspects, you will understand the Sequencer's role within the Arbitrum ecosystem, including how it enhances transaction throughput, reduces latency, and maintains a fair and decentralized network. -## The Core Inbox +## Sequencing and Broadcasting +The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro architecture. It enables real-time dissemination of transaction data as they are accepted and ordered by the Sequencer. It allows users and nodes to receive immediate updates on transaction sequencing, facilitating rapid transaction confirmations and enhancing the network's overall responsiveness. -When we talk about “submitting a transaction into an Arbitrum chain,” we’re talking about getting it included into the chain’s core Inbox, represented by the `sequencerInboxAccs` byte array in `Bridge`. Once transactions are included in the core Inbox, their ordering is fixed, execution is fully deterministic, and we can trustlessly treat the resultant state as having L1-level finality (see [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx)). +### How the Sequencer Publishes the Sequence -When we add a Sequencer, the operation of the inbox changes. +The Sequencer communicates the transaction sequence through two primary channels: -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) -- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "delayed inbox" queue, which is managed by an L1 Ethereum contract. - - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) +1. **Real-Time Sequencer Feed**: A live broadcast that publishes transactions instantly as they are sequenced. Nodes and clients subscribed to this feed receive immediate notifications, allowing them to process transactions without delay. +2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process) -The Sequencer’s role (or lack thereof) concerns strictly what happens prior; i.e., how a transaction makes its way into the core Inbox. We’ll break down the possible routes a transaction can take into two scenarios: a well-behaved Sequencer, and a faulty Sequencer. +### Real-Time Sequencer Feed -## Happy/Common Case: Sequencer Is Live and Well-behaved +The real-time feed represents the Sequencer's commitment to process transactions in a specific order. By subscribing to this feed, nodes and clients can: -Here, we start by assuming that the Sequencer is fully operational, and is running with the intent of processing users’ transactions in as safe and timely a manner as possible. The Sequencer can receive a user’s transaction two ways — either directly via an RPC request, or via the underlying L1. +- **Receive Immediate Notifications**: Obtain instant information about newly sequenced transactions and their ordering. +- **Process Transactions Promptly**: Utilize the sequenced transactions to update the state locally, enabling rapid application responses and user interactions. +- **Benefit from Soft Finality**: Gain provisional assurance about transaction acceptance and ordering before the parent chain reaches finality. -If a user is posting a “standard” Arbitrum transaction (i.e., interacting with an L2 native dapp), the user will submit the signed transaction directly to the Sequencer, much like how a user submits a transaction to an Ethereum node when interacting with L1. Upon receiving it, the Sequencer will execute it and nearly instantaneously deliver the user a receipt. Some short time later — [usually no more than a few minutes](https://arbiscan.io/batches) — the Sequencer will include the user’s transaction in a batch and post it on L1 by calling one of the `SequencerInbox`’s `addSequencerL2Batch` methods. Note that only the Sequencer has the authority to call these methods; this assurance that no other party can include a message directly is, in fact, the very thing that gives the Sequencer the unique ability to provide instant, "soft-confirmation" receipts. -Once posted in a batch, the transactions have L1-level finality. +This mechanism is particularly valuable for applications requiring low latency and high throughput, such as decentralized exchanges or real-time gaming platforms. -Transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. +### Soft Finality and Trust Model -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is faster; but if your message doesn't use the Sequencer, finality will be slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. +"Soft finality" refers to the preliminary confirmation of transactions based on the Sequencer's real-time feed. Key aspects include: -So a Sequencer is generally a win, if the Sequencer is well behaved. +- **Dependence on Sequencer Integrity**: The feed's accuracy and reliability depend on the Sequencer operating honestly and without significant downtime. +- **Immediate User Feedback**: Users can act on transaction confirmations swiftly, improving the user experience. +- **Eventual Consistency with the Parent Chain**: While the real-time feed provides quick updates, ultimate security, and finality are established once transactions are posted to and finalized on the parent chain. (See the **Finality** section for an in-depth discussion.) -### Cross chain communication +Understanding this trust model is essential. While we expect the Sequencer to behave correctly, users and developers should know that soft finality depends on this assumption. In scenarios where absolute certainty is required, parties may wait for transactions to achieve finality on the parent chain. -As we mentioned, transactions can be received from L1 and L2. A cross chain message will eventually result in a transaction being executed on the destination chain. +### Role of the Sequencer Feed in the Network -### L1 contracts can submit L2 transactions +The Sequencer Feed serves several vital functions within the Arbitrum ecosystem: -An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. +- **State Synchronization**: Nodes use the feed to stay synchronized with the latest state of the network, ensuring consistency across the decentralized platform. +- **Application Development**: Developers can build applications that respond instantly to network events, enabling features like live updates, instant notifications, and real-time analytics. +- **Ecosystem Transparency**: The feed promotes transparency and trust within the community by providing visibility into transaction sequencing and network activity. -The advantage of this method is that it is simple and has relatively low latency. The disadvantage, compared to the other method we’ll describe soon, is that the L2 transaction might revert if the L1 caller doesn’t get the L2 gas price and max gas amount right. Because the L1 caller can’t see the result of its L2 transaction, it can’t be absolutely sure that its L2 transaction will succeed. +### Considerations and Limitations -This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. +While the Sequencer Feed offers significant advantages, consider the following: -### L1 to L2 ticket-based transactions +- **Reliance on Sequencer Availability**: The effectiveness of the real-time feed depends on the Sequencer's uptime and responsiveness. Network issues or Sequencer downtime can delay transaction visibility. +- **Provisional Nature of Soft Finality**: Until transactions reach finality on the parent chain, there is a small risk that the provisional ordering provided by the feed could change in exceptional circumstances. +- **Security Implications**: For high-stakes transactions where security is paramount (e.g., centralized exchange deposits and withdrawals), users may prefer to wait for parent chain confirmation despite the longer latency. -Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. +Developers and users should design their applications and interactions with these factors in mind, choosing the appropriate balance between speed and certainty based on their requirements. -When saving a transaction for retry, Nitro records the sender’s address, destination address, callvalue, and calldata. All of this is saved, and the callvalue is deducted from the sender’s account and (logically) attached to the saved transaction. +### **Delayed Messages on the Sequencer Feed** -If the redemption succeeds, the transaction is done, a receipt is issued for it, and the ticketID is canceled and can’t be used again. If the redemption fails, for example because the packaged transaction fails, the redemption reports failure and the ticketID remains available for redemption. +As illustrated in the diagram, the Sequencer feed not only sends child chain transactions posted directly to the Sequencer but also incorporates parent chain-submitted child chain transactions. These include L2 messages submitted on L1 and retryable transactions. The Sequencer agent monitors the finalized messages submitted to the parent chain’s Delayed Inbox Contract. Once finalized, it processes them as incoming messages to the feed, ensuring they are added as ordered transactions. -Normally the original submitter will try to cause their transaction to succeed immediately, so it never needs to be recorded or retried. As an example, our "token deposit" use case above should, in the happy, common case, still only require a single signature from the user. If this initial execution fails, the ticketID will still exist as a backstop which others can redeem later. +It is important to note that the Nitro node can be configured to add delayed inbox transactions immediately after their submission to the parent chain, even before finalization. However, this approach introduces a risk of child chain reorganization if the transaction fails to finalize on the parent chain. To mitigate this risk, on Arbitrum One and Nova, the Sequencer only includes these transactions in the feed once they are finalized on the Ethereum chain. -Submitting a transaction in this way carries a price in ETH which the submitter must pay, which varies based on the calldata size of the transaction. Once submitted, the ticket is valid for about a week. If the ticket has not been redeemed in that period, it is deleted. +You can also explore how the feed sends incoming messages via WebSocket and learn how to extract message data from the feed on this page: [Read Sequencer Feed](https://docs.arbitrum.io/run-arbitrum-node/sequencer/read-sequencer-feed). -When the ticket is redeemed, the pre-packaged transaction runs with sender and origin equal to the original submitter, and with the destination, callvalue, and calldata the submitter provided at the time of submission. +## Batch-Posting +Batch-Posting is a fundamental process in the operation of the Sequencer within the Arbitrum network. It involves collecting multiple child chain transactions, organizing them into batches, compressing the data to reduce size, and sending these batches to the Sequencer Inbox Contract on parent chain. This mechanism is crucial for ensuring that transactions are securely recorded on the parent chain blockchain while optimizing for cost and performance. -This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. +In this section, we will explore the Batch-Posting process in detail, covering the following topics: -### L2 to L1 ticket-based calls +- **Batching**: How the Sequencer groups incoming transactions into batches for efficient processing and posting. +- **Compression**: The methods used to compress transaction data, minimizing the amount of data that needs to be posted on parent chain and thereby reducing costs. +- **Sending to Sequencer Inbox Contract**: The procedure for submitting compressed batches to the Sequencer Inbox Contract on parent chain, ensuring secure and reliable recording of transactions. +- **L1 Pricing Model**: An overview of the pricing challenges associated with posting data to parent chain and the adaptive algorithms used to determine fair costs per transaction. -Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. +Understanding Batch-Posting is essential for grasping how Arbitrum achieves scalability and cost-efficiency without compromising security. By delving into these subtopics, you'll gain insight into the Sequencer's role in optimizing transaction throughput and minimizing fees, as well as the innovative solutions implemented to address the challenges of Layer 1 data pricing. -These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) +## Batching & Compression +The Sequencer in Arbitrum is critical in collecting and organizing child chain transactions before posting them to the parent chain. The batching process is designed to optimize for both cost efficiency and timely transaction inclusion. -Alternatively, a user can submit their L2 message to the Sequencer by posting it on the underlying L1. This path is necessary if the user wishes to perform some [L1 operation along with the L2](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](/build-decentralized-apps/token-bridging/01-overview.mdx) (escrow on L1, mint on L2). The user does this by publishing an L1 transaction (i.e., sending a normal transaction to an L1 node) that calls one of the relevant methods on the `Inbox` contract; i.e., `sendUnsignedTransaction`. This adds a message onto what we’ll call “the delayed Inbox”, (represented by the `delayedInboxAccs` in the `Bridge` contract), which is effectively a queue that messages wait in before being moved over to the core `Inbox`. The Sequencer will emit an L2 receipt about ~10 minutes after the transaction has been included in the delayed Inbox (the reason for this delay is to minimize the risk of short term L1 reorgs which could in turn cause an L2 reorg and invalidate the Sequencer’s L2 receipts.) Again, the last step is for the Sequencer to include the L2 message in a batch — when calling the batch submission methods, the Sequencer specifies how many messages in the delayed inbox to include — finalizing the transaction. +**Transaction Collection and Ordering:** -In sum — in either happy case, the user first delivers their message to the Sequencer, who in turn ensures that it arrives in the core Inbox. +- **Continuous Reception:** The Sequencer continuously receives transactions submitted by users. +- **Ordering:** Transactions are ordered based on the sequence in which they are received, maintaining a deterministic transaction order. +- **Buffering:** Received transactions are temporarily stored in a buffer awaiting batch formation. -## Unhappy/Uncommon Case: Sequencer Isn’t Doing Its Job +**Batch Formation Criteria:** -Now let’s suppose the Sequencer, for whatever reason, is entirely failing to carry out its task of submitting messages. A user can still get their transaction included in two steps: +- **Size Thresholds:** Batch formation occurs when accumulated transactions reach a predefined size limit. This limit ensures that the fixed costs of posting data to the parent chain are amortized over more transaction, improving cost efficiency. +- **Time Constraints:** The Sequencer also monitors the time elapsed since the last posted batch to prevent undue delays. Upon reaching the maximum time threshold, the Sequencer will create a batch with the transactions collected so far, even if the batch doesn't meet the size threshold. -First, they submit their L2 message via L1 into the delayed Inbox as described above: note that although atomic cross-chain messages are the common case for using the delayed Inbox, it can in principle be used to submit _any_ L2 message. +**Batch Creation Process:** -Once in the delayed Inbox, we obviously can’t rely on the Sequencer to include the transaction in a batch. Instead, we can use `SequencerInbox`’s `forceInclusion` method. Once a message has been in the delayed Inbox for a sufficient amount of time, `forceInclusion` can be called to move it from the delayed Inbox into the core Inbox, at which point it’s finalized. Crucially, any account can call `forceInclusion`. +- **Aggregation:** Once the batch formation criteria ( the size or time threshold) are satisfied, the Sequencer aggregates the buffered transactions into a single batch. +- **Metadata Inclusion:** The batch includes the necessary metadata of all transactions. +- **Preparation for Compression:** Batch preparation for the compression stage begins, where techniques will minimize the data size before posting to parent chain. -Currently, on Arbitrum One, this delay time between submission and force inclusion is roughly @arbOneForceIncludePeriodHours@ hours, as specified by `maxTimeVariation.delayBlocks` / `maxTimeVariation.delaySeconds`. A force inclusion from L1 would directly affect the state for any unconfirmed L2 transactions; keeping conservatively high delay value ensures it should only be used under extraordinary circumstances. +This batching mechanism allows the Sequencer to efficiently manage transactions by balancing the need for cost-effective parent chain posting with the requirement for prompt transaction processing. By strategically grouping transactions into batches based on size and time criteria, the Sequencer reduces per-transaction costs and enhances the overall scalability of the Arbitrum network. -On top of the delay itself, the `forceInclusion` path has the downside of uncertainty around transaction ordering; i.e., while waiting for a message's max delay to pass, a malicious Sequencer could, in principle, directly post messages in front of it. However, there’s ultimately nothing the Sequencer can do to stop it from being included in the core Inbox, at which point its ordering is finalized. +### Compression +The Sequencer employs compression when forming transaction batches to optimize the data and cost of batches posted to the parent chain. Arbitrum uses the Brotli compression algorithm due to its high compression ratio and efficiency, crucial for reducing parent chain posting costs. -While the slow, “unhappy” path isn’t optimal, and should rarely, if ever, be necessary, its availability as an option ensures Arbitrum Rollup always preserves its trustless security model, even if the permissioned parts of the system act faulty. +### Compression level in the Brotli algorithm +Brotli’s compression algorithm includes a parameter: **compression level**, which ranges from **0 to 11**. This parameter allows you to balance two key factors: -:::info +- **Compression Efficiency**: Higher levels result in greater size reduction. +- **Computational Cost**: Higher levels require more processing power and time. -On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to [decentralized, fair sequencing](#decentralized-fair-sequencing). +As the compression level increases, you achieve better compression ratios at the expense of longer compression times. -::: +### Dynamic compression level setting +The compression level on Arbitrum is dynamically adjusted based on the current backlog of batches waiting to be posted to parent chain by Sequencer. In scenarios where multiple batches are queued in the buffer, the **compression level** can be dynamically adjusted to improve throughput. When the buffer becomes overloaded with overdue batches, the compression level decreases. -## How the Sequencer Publishes the Sequence +This tradeoff prioritizes speed over compression efficiency, enabling faster processing and transmitting pending batches. Doing so, clears the buffer more quickly, ensuring smoother overall system performance. -So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. +Now that transactions are batched and compressed, they can be passed to batch-poster to be sent to the parent chain. -The real-time feed is published by the Sequencer so that anyone who subscribes to the feed receives instant notifications of each transaction as it is sequenced. Nitro nodes can subscribe to the feed directly from the Sequencer, or through a relay that forwards the feed. The feed represents the Sequencer's promise that it will record transactions in a particular order. If the Sequencer is honest and doesn't have a long downtime, this promise will be kept. So anyone who trusts the Sequencer to keep its promises can rely on the feed to get instant information about the transaction sequence--and they can run the sequenced transactions through the state transition function to learn the results of each transaction immediately. This is "soft finality" for transactions; it's "soft" because it depends on the Sequencer keeping its promises. +## Submitting transactions to the Sequencer Inbox -The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically--perhaps every few minutes in production--the Sequencer concatenates the next group of transactions in the feed, compresses them for efficiency, and posts the result as calldata on Ethereum. This is the final and official record of the transaction sequence. As soon as this Ethereum transaction has finality on Ethereum, the Layer 2 Nitro transactions it records will have finality. These transactions are final because their position in the sequence has finality, and the outcome of the transactions is deterministic and knowable to any party. This is "hard finality". +After batching and compressing transactions, the Sequencer posts these batches to the parent chain to ensure security and finality. This process involves the **Batch Poster**, an Externally Owned Account (EOA) controlled by the Sequencer. The Batch Poster is responsible for submitting the compressed transaction batches to the **Sequencer Inbox Contract** on parent chain. -The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. +There are two primary methods the Sequencer uses to send batches to the parent chain, depending on whether the chain supports EIP-4844 (Proto-Danksharding) and the current network conditions: -## Decentralized fair sequencing +### 1. Using Blobs with `addSequencerL2BatchFromBlobs` -Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. +- **Default Approach**: When the parent chain supports EIP-4844, the Sequencer utilizes blob transactions to post batches efficiently. +- **Method**: The Batch Poster calls the `addSequencerL2BatchFromBlobs` function of the Sequencer Inbox Contract. +- **Process**: + - Batch data gets included as blobs—large binary data structures optimized for scalability. + - The transaction includes metadata about the batch but does not include the batch data itself in the calldata. +- **Benefits**: + - **Cost Efficiency**: Blobs allow cheaper data inclusion than calldata, reducing gas costs. + - **Scalability**: Leveraging blobs enhances the network's ability to handle large volumes of transactions. -How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. +### 2. Using Calldata with `addSequencerL2Batch` + +- **Alternative Approach**: If the **Blob Base Fee** is significantly high or the blob space is constrained during batch posting, the Sequencer may opt to use calldata. +- **Method**: The Batch Poster calls the `addSequencerL2Batch` function of the Sequencer Inbox Contract. +- **Process**: + - The compressed batch transactions are included directly in the transaction's calldata. +- **Considerations**: + - **Cost Evaluation**: The Sequencer dynamically assesses whether using calldata is more cost-effective than blobs based on current gas prices and blob fees. + - **Compatibility**: If the parent chain does not support EIP-4844, this method is the default and only option for batch posting. + +**Note**: The Sequencer continuously monitors network conditions to choose the most economical method for batch posting, ensuring optimal operation under varying circumstances. + +### Authority and Finality + +- **Exclusive Access**: Only the Sequencer can call these methods on the Sequencer Inbox Contract. This exclusivity ensures that no other party can directly include messages. +- **Soft-Confirmation Receipts**: The Sequencer's unique ability to immediately process and include transactions allows it to provide users with instant, "soft-confirmation" receipts, +- **Parent chain Finality**: Once batches post, the transactions achieve parent-chain-level finality, secured by Parent chain’s consensus mechanism. + +By efficiently sending compressed transaction batches to the Sequencer Inbox Contract using the most cost-effective method available, the Sequencer ensures transactions are securely recorded on parent chain. This process maintains the integrity and reliability of the network, providing users with fast and secure transaction processing. + +## Finality +Finality in blockchain systems refers to the point at which a transaction becomes irreversible and permanently included in the blockchain's ledger. In the context of Arbitrum's Nitro architecture, understanding finality is crucial for developers and users to make informed decisions about transaction confirmations, security guarantees, and application design. + +Arbitrum offers two levels of finality: + +1. **Soft Finality**: Provided by the Sequencer's real-time feed, offering immediate but provisional transaction confirmations. +2. **Hard Finality**: Occurs when transactions are included in batches posted to and finalized on the parent chain, providing strong security assurances. + +This section explores the concepts of soft and hard finality, their implications, trust considerations, and guidance for utilizing them effectively within the Arbitrum network. + +### Soft Finality + +Soft finality refers to the preliminary confirmation of transactions based on the Sequencer's real-time feed. Key characteristics include: + +- **Immediate Confirmation**: Transactions are confirmed almost instantly as they are accepted and ordered by the Sequencer. +- **Provisional Assurance**: The confirmations are provisional and rely on the Sequencer's integrity and availability. +- **High Performance**: Enables applications to offer rapid responses and real-time interactions, enhancing user experience. + +**Advantages of Soft Finality**: + +- **Low Latency**: Users receive immediate feedback on transaction status. +- **Optimized for Speed**: Ideal for applications where responsiveness is critical. +- **Improved User Experience**: Reduces waiting times and uncertainty. + +**Limitations of Soft Finality**: + +- **Trust Dependency**: Relies on the Sequencer's honesty and ability to maintain uptime.. +- **Potential for Reordering**: In rare cases, if the Sequencer acts maliciously or encounters issues, the provisional ordering could change. +- **Not Suitable for High-Value Transactions**: For transactions requiring strong security guarantees, soft finality may not suffice. + +### Hard Finality + +Hard finality occurs when batched transactions get posted to the parent chain. Key characteristics include: + +- **Strong Security Guarantees**: When included in blocks on the parent chain, transactions inherit the parent chain's security assurances. +- **Irreversibility**: Once finalized, transactions are immutable and cannot be altered or reversed. +- **Data Availability**: All transaction data is recorded on-chain, ensuring transparency and verifiability. + +**Advantages of Hard Finality**: + +- **Maximum Security**: Protected by the robustness of the parent chain's consensus mechanism. +- **Trust Minimization**: This does not require trust in the Sequencer; security comes from the underlying blockchain. +- **Suitable for High-Value Transactions**: Ideal for scenarios where security and immutability are paramount. + +**Limitations of Hard Finality**: + +- **Higher Latency**: Achieving hard finality takes longer due to the time required for the parent chain to process and finalize batches. +- **Cost Considerations**: Posting batches to the parent chain incurs fees, which may affect transaction costs. + +### Trust Considerations + +Understanding the trust assumptions associated with each level of finality is essential: + +- **Soft Finality Trust Model**: + - **Reliance on the Sequencer**: Users must trust that the Sequencer operates honestly, sequences transactions correctly, and remains available. + - **Risk of Misbehavior**: If the Sequencer acts maliciously, it could reorder or censor certain transactions before they achieve hard finality. +- **Hard Finality Trust Model**: + - **Reliance on the Parent Chain**: Security is based on the consensus and integrity of the parent chain. + - **Reduced Trust in Sequencer**: Even if the Sequencer misbehaves, transactions included in posted batches are secured once finalized on the parent chain. + +### Application Implications + +Developers and users should consider the appropriate level of finality based on their specific use cases: + +- **When to Rely on Soft Finality**: + - **Low-Risk Transactions**: For transactions where the potential impact of reordering or delays is minimal. + - **User Experience Priority**: Applications where responsiveness and immediacy enhance user engagement, such as gaming or social platforms. + - **Frequent Transactions**: Scenarios involving a high volume of small transactions where waiting for hard finality is impractical. +- **When to Require Hard Finality**: + - **High-Value Transactions**: Financial transfers, large trades, or any transaction where security is critical. + - **Regulatory Compliance**: Situations requiring strict adherence to security standards and auditable records. + - **Centralized Exchanges (CeXs)**: For deposit and withdrawal operations where certainty of transaction finality is mandatory. + +### Decentralized Fair Sequencing + +Arbitrum’s long-term vision includes transitioning from a centralized Sequencer to a decentralized, fair sequencing model. In this framework, a committee of servers (or validators) collectively determines transaction ordering, ensuring fairness, reducing the influence of any single party, and making it more resistant to manipulation. By requiring a supermajority consensus, this approach distributes sequencing power among multiple honest participants, mitigates the risks of front-running or censorship, and aligns with the broader blockchain principles of enhanced security, transparency, and decentralization. \ No newline at end of file From 5b97cd4bfc48b3655d1bde0c7efce1a504d19376 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 26 Dec 2024 14:39:39 -0600 Subject: [PATCH 71/75] yarn format --- .../02-transaction-lifecycle.mdx | 10 ++- .../how-arbitrum-works/03-sequencer.mdx | 73 ++++++++++--------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 3e2b987b7..16a303955 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -16,11 +16,12 @@ The second subsection, **Bypassing the Sequencer**, describes an alternative met This diagram illustrates the various pathways for submitting transactions to the Arbitrum chain. It highlights the options for sending transactions through the sequencer or bypassing it and using the delayed inbox contract on the parent chain. ## Submitting a transaction to the Sequencer -This section outlines the different methods for users to submit transactions to the sequencer on the Arbitrum chain. There are four primary ways to do this: Public RPC, Third-Party RPCs, Arbitrum Nodes, and the Sequencer Endpoint. We will explore these methods in detail, explaining when to choose one over the other and how to use each to effectively submit transactions to the Arbitrum sequencer. + +This section outlines the different methods for users to submit transactions to the sequencer on the Arbitrum chain. There are four primary ways to do this: Public RPC, Third-Party RPCs, Arbitrum Nodes, and the Sequencer Endpoint. We will explore these methods in detail, explaining when to choose one over the other and how to use each to effectively submit transactions to the Arbitrum sequencer. ### 1. Public RPC -Arbitrum provides public RPCs for its main chains: Arbitrum One, Arbitrum Nova, and Arbitrum Sepolia. Due to their rate-limited nature, these RPC endpoints are suitable for less resource-intensive operations. Public RPCs can be an accessible option for general use cases and light interactions with the network. +Arbitrum provides public RPCs for its main chains: Arbitrum One, Arbitrum Nova, and Arbitrum Sepolia. Due to their rate-limited nature, these RPC endpoints are suitable for less resource-intensive operations. Public RPCs can be an accessible option for general use cases and light interactions with the network. For more details on the specific RPC endpoints for each chain, please see [this section](https://docs.arbitrum.io/build-decentralized-apps/reference/node-providers#arbitrum-public-rpc-endpoints) of the documentation. @@ -41,7 +42,8 @@ Another approach for sending transactions to the sequencer is through self-hoste The Sequencer Endpoint is the most direct method for users looking to minimize delays in their transactions reaching the sequencer. Unlike standard RPC URLs, the Sequencer Endpoint supports only `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls, bypassing the load balancer entirely. This endpoint makes it an optimal choice for users who require the quickest transaction processing time. ## Bypassing the Sequencer -This section delves into an alternative method for submitting transactions to the Arbitrum chain, bypassing the sequencer. This page focuses on how users can send their transactions directly to the delayed inbox contract on the parent chain rather than through the sequencer. This method offers two distinct paths a transaction can take, with each route interacting with the network differently to achieve transaction inclusion. This approach provides users with greater flexibility and ensures that transactions can still be processed if the sequencer is unavailable or if users choose not to depend on it. This section highlights these alternative submission mechanisms and underscores the robustness and decentralization features inherent in the Arbitrum network. + +This section delves into an alternative method for submitting transactions to the Arbitrum chain, bypassing the sequencer. This page focuses on how users can send their transactions directly to the delayed inbox contract on the parent chain rather than through the sequencer. This method offers two distinct paths a transaction can take, with each route interacting with the network differently to achieve transaction inclusion. This approach provides users with greater flexibility and ensures that transactions can still be processed if the sequencer is unavailable or if users choose not to depend on it. This section highlights these alternative submission mechanisms and underscores the robustness and decentralization features inherent in the Arbitrum network. In **Diagram 3**, we demonstrate how users can submit their transactions using the delayed inbox contract to bypass the sequencer. As illustrated in the diagram, there are two possible paths for transaction handling. When a transaction is submitted to the delayed inbox, the sequencer may automatically pick it up, include it as an ordered transaction, and send it to the sequencer feed. However, if the sequencer does not process the transaction within 24 hours, users have the reliable option to call the `forceInclude` function on the sequencer inbox contract. This action ensures that the sequencer to picks up the transaction and includes it in the ordered transaction list, providing users with a sense of security about their transactions. @@ -49,4 +51,4 @@ To send a transaction to the delayed inbox instead of submitting it to the seque If the sequencer is not back online within 24 hours or decides to censor the transaction, users can invoke the [`forceInclusion`](https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/bridge/SequencerInbox.sol#L284) function on the SequencerInbox contract. This action ensures their transaction is included on the chain, bypassing the sequencer's role. -Additionally, the Arbitrum SDK provides the [`InboxTools`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L64C14-L64C24) class, which simplifies the process of submitting transactions to the delayed inbox. Users can utilize the [`sendChildSignedTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L401C16-L401C33) method to send the transaction and the [`forceInclude`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L367) method to ensure its inclusion. The SDK also offers helper methods like [`signChildTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L429) to assist with signing the transaction during the creation of the serialized signed transaction hex string, streamlining the entire process. \ No newline at end of file +Additionally, the Arbitrum SDK provides the [`InboxTools`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L64C14-L64C24) class, which simplifies the process of submitting transactions to the delayed inbox. Users can utilize the [`sendChildSignedTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L401C16-L401C33) method to send the transaction and the [`forceInclude`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L367) method to ensure its inclusion. The SDK also offers helper methods like [`signChildTx`](https://github.com/OffchainLabs/arbitrum-sdk/blob/792a7ee3ccf09842653bc49b771671706894cbb4/src/lib/inbox/inbox.ts#L429) to assist with signing the transaction during the creation of the serialized signed transaction hex string, streamlining the entire process. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 0c8e5fa05..6e9071a1c 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -13,23 +13,24 @@ In this section, we will explore the operation of the Sequencer in detail. The t - **Sequencing and Broadcasting (Sequencer Feed)**: An overview of the real-time transaction feed provided by the Sequencer, which allows nodes to receive instant updates on the transaction sequence. - **Batch-Posting**: How the Sequencer groups transactions into batches, compresses them to reduce data size and sends them to the Sequencer Inbox Contract on the parent chain. This section also delves into the L1 pricing model and how it affects transaction costs. - - **Batching** - - **Compression** - - **Submitting to Sequencer Inbox Contract** - - **L1 Pricing Model** + - **Batching** + - **Compression** + - **Submitting to Sequencer Inbox Contract** + - **L1 Pricing Model** - **Finality**: Understanding how transaction finality is achieved in Arbitrum through both soft and hard finality mechanisms, ensuring that transactions are confirmed securely and reliably. (not as a sequencer task) -By examining these aspects, you will understand the Sequencer's role within the Arbitrum ecosystem, including how it enhances transaction throughput, reduces latency, and maintains a fair and decentralized network. +By examining these aspects, you will understand the Sequencer's role within the Arbitrum ecosystem, including how it enhances transaction throughput, reduces latency, and maintains a fair and decentralized network. ## Sequencing and Broadcasting -The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro architecture. It enables real-time dissemination of transaction data as they are accepted and ordered by the Sequencer. It allows users and nodes to receive immediate updates on transaction sequencing, facilitating rapid transaction confirmations and enhancing the network's overall responsiveness. + +The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro architecture. It enables real-time dissemination of transaction data as they are accepted and ordered by the Sequencer. It allows users and nodes to receive immediate updates on transaction sequencing, facilitating rapid transaction confirmations and enhancing the network's overall responsiveness. ### How the Sequencer Publishes the Sequence The Sequencer communicates the transaction sequence through two primary channels: 1. **Real-Time Sequencer Feed**: A live broadcast that publishes transactions instantly as they are sequenced. Nodes and clients subscribed to this feed receive immediate notifications, allowing them to process transactions without delay. -2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process) +2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process) ### Real-Time Sequencer Feed @@ -37,7 +38,7 @@ The real-time feed represents the Sequencer's commitment to process transactions - **Receive Immediate Notifications**: Obtain instant information about newly sequenced transactions and their ordering. - **Process Transactions Promptly**: Utilize the sequenced transactions to update the state locally, enabling rapid application responses and user interactions. -- **Benefit from Soft Finality**: Gain provisional assurance about transaction acceptance and ordering before the parent chain reaches finality. +- **Benefit from Soft Finality**: Gain provisional assurance about transaction acceptance and ordering before the parent chain reaches finality. This mechanism is particularly valuable for applications requiring low latency and high throughput, such as decentralized exchanges or real-time gaming platforms. @@ -49,7 +50,7 @@ This mechanism is particularly valuable for applications requiring low latency a - **Immediate User Feedback**: Users can act on transaction confirmations swiftly, improving the user experience. - **Eventual Consistency with the Parent Chain**: While the real-time feed provides quick updates, ultimate security, and finality are established once transactions are posted to and finalized on the parent chain. (See the **Finality** section for an in-depth discussion.) -Understanding this trust model is essential. While we expect the Sequencer to behave correctly, users and developers should know that soft finality depends on this assumption. In scenarios where absolute certainty is required, parties may wait for transactions to achieve finality on the parent chain. +Understanding this trust model is essential. While we expect the Sequencer to behave correctly, users and developers should know that soft finality depends on this assumption. In scenarios where absolute certainty is required, parties may wait for transactions to achieve finality on the parent chain. ### Role of the Sequencer Feed in the Network @@ -67,7 +68,7 @@ While the Sequencer Feed offers significant advantages, consider the following: - **Provisional Nature of Soft Finality**: Until transactions reach finality on the parent chain, there is a small risk that the provisional ordering provided by the feed could change in exceptional circumstances. - **Security Implications**: For high-stakes transactions where security is paramount (e.g., centralized exchange deposits and withdrawals), users may prefer to wait for parent chain confirmation despite the longer latency. -Developers and users should design their applications and interactions with these factors in mind, choosing the appropriate balance between speed and certainty based on their requirements. +Developers and users should design their applications and interactions with these factors in mind, choosing the appropriate balance between speed and certainty based on their requirements. ### **Delayed Messages on the Sequencer Feed** @@ -78,6 +79,7 @@ It is important to note that the Nitro node can be configured to add delayed inb You can also explore how the feed sends incoming messages via WebSocket and learn how to extract message data from the feed on this page: [Read Sequencer Feed](https://docs.arbitrum.io/run-arbitrum-node/sequencer/read-sequencer-feed). ## Batch-Posting + Batch-Posting is a fundamental process in the operation of the Sequencer within the Arbitrum network. It involves collecting multiple child chain transactions, organizing them into batches, compressing the data to reduce size, and sending these batches to the Sequencer Inbox Contract on parent chain. This mechanism is crucial for ensuring that transactions are securely recorded on the parent chain blockchain while optimizing for cost and performance. In this section, we will explore the Batch-Posting process in detail, covering the following topics: @@ -90,7 +92,8 @@ In this section, we will explore the Batch-Posting process in detail, covering t Understanding Batch-Posting is essential for grasping how Arbitrum achieves scalability and cost-efficiency without compromising security. By delving into these subtopics, you'll gain insight into the Sequencer's role in optimizing transaction throughput and minimizing fees, as well as the innovative solutions implemented to address the challenges of Layer 1 data pricing. ## Batching & Compression -The Sequencer in Arbitrum is critical in collecting and organizing child chain transactions before posting them to the parent chain. The batching process is designed to optimize for both cost efficiency and timely transaction inclusion. + +The Sequencer in Arbitrum is critical in collecting and organizing child chain transactions before posting them to the parent chain. The batching process is designed to optimize for both cost efficiency and timely transaction inclusion. **Transaction Collection and Ordering:** @@ -112,9 +115,11 @@ The Sequencer in Arbitrum is critical in collecting and organizing child chain This batching mechanism allows the Sequencer to efficiently manage transactions by balancing the need for cost-effective parent chain posting with the requirement for prompt transaction processing. By strategically grouping transactions into batches based on size and time criteria, the Sequencer reduces per-transaction costs and enhances the overall scalability of the Arbitrum network. ### Compression -The Sequencer employs compression when forming transaction batches to optimize the data and cost of batches posted to the parent chain. Arbitrum uses the Brotli compression algorithm due to its high compression ratio and efficiency, crucial for reducing parent chain posting costs. + +The Sequencer employs compression when forming transaction batches to optimize the data and cost of batches posted to the parent chain. Arbitrum uses the Brotli compression algorithm due to its high compression ratio and efficiency, crucial for reducing parent chain posting costs. ### Compression level in the Brotli algorithm + Brotli’s compression algorithm includes a parameter: **compression level**, which ranges from **0 to 11**. This parameter allows you to balance two key factors: - **Compression Efficiency**: Higher levels result in greater size reduction. @@ -123,9 +128,10 @@ Brotli’s compression algorithm includes a parameter: **compression level**, wh As the compression level increases, you achieve better compression ratios at the expense of longer compression times. ### Dynamic compression level setting -The compression level on Arbitrum is dynamically adjusted based on the current backlog of batches waiting to be posted to parent chain by Sequencer. In scenarios where multiple batches are queued in the buffer, the **compression level** can be dynamically adjusted to improve throughput. When the buffer becomes overloaded with overdue batches, the compression level decreases. -This tradeoff prioritizes speed over compression efficiency, enabling faster processing and transmitting pending batches. Doing so, clears the buffer more quickly, ensuring smoother overall system performance. +The compression level on Arbitrum is dynamically adjusted based on the current backlog of batches waiting to be posted to parent chain by Sequencer. In scenarios where multiple batches are queued in the buffer, the **compression level** can be dynamically adjusted to improve throughput. When the buffer becomes overloaded with overdue batches, the compression level decreases. + +This tradeoff prioritizes speed over compression efficiency, enabling faster processing and transmitting pending batches. Doing so, clears the buffer more quickly, ensuring smoother overall system performance. Now that transactions are batched and compressed, they can be passed to batch-poster to be sent to the parent chain. @@ -140,21 +146,21 @@ There are two primary methods the Sequencer uses to send batches to the parent c - **Default Approach**: When the parent chain supports EIP-4844, the Sequencer utilizes blob transactions to post batches efficiently. - **Method**: The Batch Poster calls the `addSequencerL2BatchFromBlobs` function of the Sequencer Inbox Contract. - **Process**: - - Batch data gets included as blobs—large binary data structures optimized for scalability. - - The transaction includes metadata about the batch but does not include the batch data itself in the calldata. + - Batch data gets included as blobs—large binary data structures optimized for scalability. + - The transaction includes metadata about the batch but does not include the batch data itself in the calldata. - **Benefits**: - - **Cost Efficiency**: Blobs allow cheaper data inclusion than calldata, reducing gas costs. - - **Scalability**: Leveraging blobs enhances the network's ability to handle large volumes of transactions. + - **Cost Efficiency**: Blobs allow cheaper data inclusion than calldata, reducing gas costs. + - **Scalability**: Leveraging blobs enhances the network's ability to handle large volumes of transactions. ### 2. Using Calldata with `addSequencerL2Batch` -- **Alternative Approach**: If the **Blob Base Fee** is significantly high or the blob space is constrained during batch posting, the Sequencer may opt to use calldata. +- **Alternative Approach**: If the **Blob Base Fee** is significantly high or the blob space is constrained during batch posting, the Sequencer may opt to use calldata. - **Method**: The Batch Poster calls the `addSequencerL2Batch` function of the Sequencer Inbox Contract. - **Process**: - - The compressed batch transactions are included directly in the transaction's calldata. + - The compressed batch transactions are included directly in the transaction's calldata. - **Considerations**: - - **Cost Evaluation**: The Sequencer dynamically assesses whether using calldata is more cost-effective than blobs based on current gas prices and blob fees. - - **Compatibility**: If the parent chain does not support EIP-4844, this method is the default and only option for batch posting. + - **Cost Evaluation**: The Sequencer dynamically assesses whether using calldata is more cost-effective than blobs based on current gas prices and blob fees. + - **Compatibility**: If the parent chain does not support EIP-4844, this method is the default and only option for batch posting. **Note**: The Sequencer continuously monitors network conditions to choose the most economical method for batch posting, ensuring optimal operation under varying circumstances. @@ -167,6 +173,7 @@ There are two primary methods the Sequencer uses to send batches to the parent c By efficiently sending compressed transaction batches to the Sequencer Inbox Contract using the most cost-effective method available, the Sequencer ensures transactions are securely recorded on parent chain. This process maintains the integrity and reliability of the network, providing users with fast and secure transaction processing. ## Finality + Finality in blockchain systems refers to the point at which a transaction becomes irreversible and permanently included in the blockchain's ledger. In the context of Arbitrum's Nitro architecture, understanding finality is crucial for developers and users to make informed decisions about transaction confirmations, security guarantees, and application design. Arbitrum offers two levels of finality: @@ -220,25 +227,25 @@ Hard finality occurs when batched transactions get posted to the parent chain. K Understanding the trust assumptions associated with each level of finality is essential: - **Soft Finality Trust Model**: - - **Reliance on the Sequencer**: Users must trust that the Sequencer operates honestly, sequences transactions correctly, and remains available. - - **Risk of Misbehavior**: If the Sequencer acts maliciously, it could reorder or censor certain transactions before they achieve hard finality. + - **Reliance on the Sequencer**: Users must trust that the Sequencer operates honestly, sequences transactions correctly, and remains available. + - **Risk of Misbehavior**: If the Sequencer acts maliciously, it could reorder or censor certain transactions before they achieve hard finality. - **Hard Finality Trust Model**: - - **Reliance on the Parent Chain**: Security is based on the consensus and integrity of the parent chain. - - **Reduced Trust in Sequencer**: Even if the Sequencer misbehaves, transactions included in posted batches are secured once finalized on the parent chain. + - **Reliance on the Parent Chain**: Security is based on the consensus and integrity of the parent chain. + - **Reduced Trust in Sequencer**: Even if the Sequencer misbehaves, transactions included in posted batches are secured once finalized on the parent chain. ### Application Implications Developers and users should consider the appropriate level of finality based on their specific use cases: - **When to Rely on Soft Finality**: - - **Low-Risk Transactions**: For transactions where the potential impact of reordering or delays is minimal. - - **User Experience Priority**: Applications where responsiveness and immediacy enhance user engagement, such as gaming or social platforms. - - **Frequent Transactions**: Scenarios involving a high volume of small transactions where waiting for hard finality is impractical. + - **Low-Risk Transactions**: For transactions where the potential impact of reordering or delays is minimal. + - **User Experience Priority**: Applications where responsiveness and immediacy enhance user engagement, such as gaming or social platforms. + - **Frequent Transactions**: Scenarios involving a high volume of small transactions where waiting for hard finality is impractical. - **When to Require Hard Finality**: - - **High-Value Transactions**: Financial transfers, large trades, or any transaction where security is critical. - - **Regulatory Compliance**: Situations requiring strict adherence to security standards and auditable records. - - **Centralized Exchanges (CeXs)**: For deposit and withdrawal operations where certainty of transaction finality is mandatory. + - **High-Value Transactions**: Financial transfers, large trades, or any transaction where security is critical. + - **Regulatory Compliance**: Situations requiring strict adherence to security standards and auditable records. + - **Centralized Exchanges (CeXs)**: For deposit and withdrawal operations where certainty of transaction finality is mandatory. ### Decentralized Fair Sequencing -Arbitrum’s long-term vision includes transitioning from a centralized Sequencer to a decentralized, fair sequencing model. In this framework, a committee of servers (or validators) collectively determines transaction ordering, ensuring fairness, reducing the influence of any single party, and making it more resistant to manipulation. By requiring a supermajority consensus, this approach distributes sequencing power among multiple honest participants, mitigates the risks of front-running or censorship, and aligns with the broader blockchain principles of enhanced security, transparency, and decentralization. \ No newline at end of file +Arbitrum’s long-term vision includes transitioning from a centralized Sequencer to a decentralized, fair sequencing model. In this framework, a committee of servers (or validators) collectively determines transaction ordering, ensuring fairness, reducing the influence of any single party, and making it more resistant to manipulation. By requiring a supermajority consensus, this approach distributes sequencing power among multiple honest participants, mitigates the risks of front-running or censorship, and aligns with the broader blockchain principles of enhanced security, transparency, and decentralization. From c36e4d8dc0efc4776a82d7f7d1217a86f1486a94 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 26 Dec 2024 14:42:31 -0600 Subject: [PATCH 72/75] fixing vercel broken link --- .../arbitrum-vs-ethereum/02-block-numbers-and-time.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 1656f7539..04b01ba9d 100644 --- a/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/arbitrum-docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -13,7 +13,7 @@ With the release of Arbitrum Orbit, Arbitrum chains can now be L2s that settle t ::: -As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions later. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/03-sequencer.mdx#happycommon-case-sequencer-is-live-and-well-behaved) or via the chain's [delayed inbox](/how-arbitrum-works/03-sequencer.mdx#unhappyuncommon-case-sequencer-isnt-doing-its-job). +As in Ethereum, Arbitrum clients submit transactions, and the system executes those transactions later. In Arbitrum, clients submit transactions by posting messages to the Ethereum chain, either [through the sequencer](/how-arbitrum-works/03-sequencer.mdx) or via the chain's [delayed inbox](/how-arbitrum-works/03-sequencer.mdx). Once in the chain's core inbox contract, transactions are processed in order. Generally, some time will elapse between when a message is put into the inbox (and timestamped) and when the contract processes the message and carries out the transaction requested by the message. From c89b2fd61b7f2604d4e0c86ad0b46b699398f0e9 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 26 Dec 2024 14:51:33 -0600 Subject: [PATCH 73/75] minor formatting changes --- .../02-transaction-lifecycle.mdx | 8 +++---- .../how-arbitrum-works/03-sequencer.mdx | 22 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 16a303955..9304512dd 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -9,13 +9,13 @@ content_type: get-started This section explores the various methods users can employ to submit transactions for inclusion on the Arbitrum chain. We discuss the different pathways available—sending transactions to the sequencer or bypassing it by submitting transactions through the delayed inbox contract on the parent chain. By outlining these options, we aim to clarify how users can interact with the network, detail the processes involved in each method, and identify the modules responsible for handling these transactions. This overview will enhance your understanding of the initial steps in Arbitrum ecosystem's transaction lifecycle and prepare you for a detailed exploration of transaction inclusion mechanisms in the subsequent sections. -The first subsection, **Submitting Transactions to the Sequencer**, presents four different methods users can utilize to send their transactions to the sequencer: via Public RPC, Third-Party RPC, Arbitrum Nodes, and the Sequencer Endpoint. Transactions sent through the first three pathways will route through our Load Balancer before reaching the sequencer. In contrast, the Sequencer Endpoint allows transactions to bypass the Load Balancer and be sent directly to the sequencer. +The first subsection, [Submitting Transactions to the Sequencer](#submitting-transactions-to-the-sequencer), presents four different methods users can utilize to send their transactions to the sequencer: via Public RPC, Third-Party RPC, Arbitrum Nodes, and the Sequencer Endpoint. Transactions sent through the first three pathways will route through our Load Balancer before reaching the sequencer. In contrast, the Sequencer Endpoint allows transactions to bypass the Load Balancer and be sent directly to the sequencer. -The second subsection, **Bypassing the Sequencer**, describes an alternative method where users can include their transactions on the Arbitrum chain without relying on the sequencer. By sending transactions directly to the delayed inbox contract on the parent chain (Layer 1), users gain additional flexibility, ensuring that their transactions can be processed even if the sequencer is unavailable or if they prefer not to use it. +The second subsection, [Bypassing the Sequencer](#bypassing-the-sequencer), describes an alternative method where users can include their transactions on the Arbitrum chain without relying on the sequencer. By sending transactions directly to the delayed inbox contract on the parent chain (Layer 1), users gain additional flexibility, ensuring that their transactions can be processed even if the sequencer is unavailable or if they prefer not to use it. This diagram illustrates the various pathways for submitting transactions to the Arbitrum chain. It highlights the options for sending transactions through the sequencer or bypassing it and using the delayed inbox contract on the parent chain. -## Submitting a transaction to the Sequencer +## Submitting transactions to the Sequencer This section outlines the different methods for users to submit transactions to the sequencer on the Arbitrum chain. There are four primary ways to do this: Public RPC, Third-Party RPCs, Arbitrum Nodes, and the Sequencer Endpoint. We will explore these methods in detail, explaining when to choose one over the other and how to use each to effectively submit transactions to the Arbitrum sequencer. @@ -35,7 +35,7 @@ You can find a list of supported third-party providers [here](https://docs.arbit Another approach for sending transactions to the sequencer is through self-hosted Arbitrum nodes. Running a node gives you direct control over your transactions, which go to the sequencer via the sequencer feed. -. Please see the [Arbitrum Node](https://docs.arbitrum.io/run-arbitrum-node/overview) documentation to learn more about setting up and running a node. +Please see the [Arbitrum Node](https://docs.arbitrum.io/run-arbitrum-node/overview) documentation to learn more about setting up and running a node. ### 4. Sequencer Endpoint diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index 6e9071a1c..e03c8c543 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -11,13 +11,12 @@ The Sequencer is a pivotal component of the Arbitrum network and is responsible In this section, we will explore the operation of the Sequencer in detail. The topics covered include: -- **Sequencing and Broadcasting (Sequencer Feed)**: An overview of the real-time transaction feed provided by the Sequencer, which allows nodes to receive instant updates on the transaction sequence. -- **Batch-Posting**: How the Sequencer groups transactions into batches, compresses them to reduce data size and sends them to the Sequencer Inbox Contract on the parent chain. This section also delves into the L1 pricing model and how it affects transaction costs. - - **Batching** - - **Compression** - - **Submitting to Sequencer Inbox Contract** - - **L1 Pricing Model** -- **Finality**: Understanding how transaction finality is achieved in Arbitrum through both soft and hard finality mechanisms, ensuring that transactions are confirmed securely and reliably. (not as a sequencer task) +- [Sequencing and Broadcasting (Sequencer Feed)](#sequencing-and-broadcasting): An overview of the real-time transaction feed provided by the Sequencer, which allows nodes to receive instant updates on the transaction sequence. +- [Batch-Posting](#batch-posting): How the Sequencer groups transactions into batches, compresses them to reduce data size and sends them to the Sequencer Inbox Contract on the parent chain. This section also delves into the L1 pricing model and how it affects transaction costs. + - [Batching](#batching-and-compression) + - [Compression](#compression) + - [Submitting to the Sequencer Inbox Contract](#submitting-to-the-sequencer-inbox-contract) +- [Finality](#finality): Understanding how transaction finality is achieved in Arbitrum through both soft and hard finality mechanisms, ensuring that transactions are confirmed securely and reliably. (not as a sequencer task) By examining these aspects, you will understand the Sequencer's role within the Arbitrum ecosystem, including how it enhances transaction throughput, reduces latency, and maintains a fair and decentralized network. @@ -87,11 +86,10 @@ In this section, we will explore the Batch-Posting process in detail, covering t - **Batching**: How the Sequencer groups incoming transactions into batches for efficient processing and posting. - **Compression**: The methods used to compress transaction data, minimizing the amount of data that needs to be posted on parent chain and thereby reducing costs. - **Sending to Sequencer Inbox Contract**: The procedure for submitting compressed batches to the Sequencer Inbox Contract on parent chain, ensuring secure and reliable recording of transactions. -- **L1 Pricing Model**: An overview of the pricing challenges associated with posting data to parent chain and the adaptive algorithms used to determine fair costs per transaction. Understanding Batch-Posting is essential for grasping how Arbitrum achieves scalability and cost-efficiency without compromising security. By delving into these subtopics, you'll gain insight into the Sequencer's role in optimizing transaction throughput and minimizing fees, as well as the innovative solutions implemented to address the challenges of Layer 1 data pricing. -## Batching & Compression +## Batching and compression The Sequencer in Arbitrum is critical in collecting and organizing child chain transactions before posting them to the parent chain. The batching process is designed to optimize for both cost efficiency and timely transaction inclusion. @@ -135,7 +133,7 @@ This tradeoff prioritizes speed over compression efficiency, enabling faster pro Now that transactions are batched and compressed, they can be passed to batch-poster to be sent to the parent chain. -## Submitting transactions to the Sequencer Inbox +## Submitting to the Sequencer Inbox Contract After batching and compressing transactions, the Sequencer posts these batches to the parent chain to ensure security and finality. This process involves the **Batch Poster**, an Externally Owned Account (EOA) controlled by the Sequencer. The Batch Poster is responsible for submitting the compressed transaction batches to the **Sequencer Inbox Contract** on parent chain. @@ -162,7 +160,9 @@ There are two primary methods the Sequencer uses to send batches to the parent c - **Cost Evaluation**: The Sequencer dynamically assesses whether using calldata is more cost-effective than blobs based on current gas prices and blob fees. - **Compatibility**: If the parent chain does not support EIP-4844, this method is the default and only option for batch posting. -**Note**: The Sequencer continuously monitors network conditions to choose the most economical method for batch posting, ensuring optimal operation under varying circumstances. +:::note +The Sequencer continuously monitors network conditions to choose the most economical method for batch posting, ensuring optimal operation under varying circumstances. +::: ### Authority and Finality From 2e96858c513bab475f51908342bd55336831eeff Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 27 Dec 2024 13:51:09 -0600 Subject: [PATCH 74/75] adding infographics --- arbitrum-docs/assets/batching.png | Bin 0 -> 56603 bytes .../assets/bypassing-the-sequencer.png | Bin 0 -> 41852 bytes arbitrum-docs/assets/compression.png | Bin 0 -> 51630 bytes arbitrum-docs/assets/sequencer-feed.png | Bin 0 -> 101245 bytes arbitrum-docs/assets/sequencer-operations.png | Bin 0 -> 86526 bytes .../assets/submit-to-sequencer-inbox.png | Bin 0 -> 31474 bytes .../assets/submit-tx-to-sequencer.png | Bin 0 -> 24780 bytes .../02-transaction-lifecycle.mdx | 6 + .../how-arbitrum-works/03-sequencer.mdx | 12 +- .../inside-arbitrum-nitro.md | 679 ++++++++++++++++++ 10 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 arbitrum-docs/assets/batching.png create mode 100644 arbitrum-docs/assets/bypassing-the-sequencer.png create mode 100644 arbitrum-docs/assets/compression.png create mode 100644 arbitrum-docs/assets/sequencer-feed.png create mode 100644 arbitrum-docs/assets/sequencer-operations.png create mode 100644 arbitrum-docs/assets/submit-to-sequencer-inbox.png create mode 100644 arbitrum-docs/assets/submit-tx-to-sequencer.png create mode 100644 arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md diff --git a/arbitrum-docs/assets/batching.png b/arbitrum-docs/assets/batching.png new file mode 100644 index 0000000000000000000000000000000000000000..6e311e261e21732722c3b88d86cbae09d080522c GIT binary patch literal 56603 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 literal 0 HcmV?d00001 diff --git a/arbitrum-docs/assets/bypassing-the-sequencer.png b/arbitrum-docs/assets/bypassing-the-sequencer.png new file mode 100644 index 0000000000000000000000000000000000000000..289d0d5997b2a858dabea3ac613b25d9c30305b7 GIT binary patch literal 41852 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 KuTiRI^ZY-zGkBK( literal 0 HcmV?d00001 diff --git a/arbitrum-docs/assets/compression.png b/arbitrum-docs/assets/compression.png new file mode 100644 index 0000000000000000000000000000000000000000..2a8ec38f16312dca0f0f3e71b71c7d4b6d6bb183 GIT binary patch literal 51630 zcmb5W2|Uzm8$WEzQW{$%hU{BHD8?@PE=!2)Yu3iTWJxuM!Pt^5ON%8Wp%7A-NVHk9 z8%vQjOQ_y!>N(GOPUrvmzwi6`=p3Ch^ZVWRbzkfE`d)Xuv7y$%{cQV5NJtLqXser$ zkWipVNJzJ+DdE37FuM{A|04A_(K=01*^B%Hf1&Zyw(=(-p>rVqA-$)3*Mo$Fi$q6V z)hxtzF^?w1?EN=P6jR-4U6oV~mNsS$Qc~6D=VBWy4jnQ(bZp*^!Hh!B!#tCs+T4b| zoU4fWkgAKR$zgT=L)Q;c>`xH79KBfXKqH?W^|YPVP~hQ6_}tQZ(o{{h+)N-%@ z{?eTMI88JajjSFQd-R_F=PvQ5Eql|dWPB?B{F%5&7ISE~fb^Eoi&ISjs7}wwS~Lmm zsTM1>NGzXh;9!Czt=07FxR8wt46Hkt0Vekg|Rgi_e}tE7Q=>aFLXh z#G03xm2yc*`M+0~>mPg2=E|et!y#W?7^qaN zJoo5AAll!*JaBRF%ta9)q1HFI^&Uii4_h2>IhrA2e!4kqqp#M()a?E_^E|0bI{~z8 z((ZGW=NnJ?4A++CXi3V;+PY0G1Q=ku)80|&h>*SgMn&Ce_)kfzkooY?uahn%O7+e^ zbbWPmt{{Br@uf?-)||YIb(~vuCF(tm_fi4~uJd zXCA-U$q_v9V|UrY35h}zemuRjd$z*#ZZ9wUDX(oz4khQ%VeG*8>U-JcXEpp!l&G^R z-6^`>{^R6YZOPVT*=x5ln{pOt(s9VUc|qrT;f!`?XJ%X;U3kBrNkG4~HHN&u!b&P& z;ln|hu@I*b)mJx8kWY6cwd45pBm(A*1HKG1A7Bwx+1>oIsd$@MU^l8L5QlFRe6U0}%VygQZ|FAf-y36Mnk6g$< zUh(UV4L4Lu2<|M@6fCa_l$E*AMgDWWW!3kb(X5JmX=?k^4D%JN@VF26t*K($>NMg?!l&HH>|7*!ivld^clynhHw`ypJ`tCBo$o=cYQ5;iwoXyTx15x^9-LC&Qaj@G~5i(u$yE5~I-803SC)2$h@9Jk? zHq4XDj%+z>8$x<%=j)k|-AXI1%Q2$^(}g!`dg6>;_@`{sfqM{ez*hahv+X9#pFi7>QDNmgg!rf8kkDAFa~XpP zI&jDbEuM7kxsj%>pLj%yGEKlhj$2mUY(9g7N0X;2t*SBL(`8P@FfL_fJ4sZ~+{{eU zSgprfCzhmRJ`a&qEz_P;5k&+E27w$NQpP^L=%cBh_&e5hTA3)eKWwVs7}SB|@MWTY zS7GHpHs45$bx^L)5T6V<`(7?p@qCwc)Owp677ZC&D+q zzJ7l1`6KhVwb%o8JCW(6AI*fVRn5+h1JfrjDxE>!piVPL{nH>gD9Eg=tUewVw~cst z?I62^eJe>%o_u{eAqA^0ZXLh(GlZ^XlJp9Hr%TrPYYy$#3PEP>V@=<%is74P^-d|N zTffz7j)@-=FEMoJPq7WEav9&z&*r^)^{R`YQGTM4(l>Q)cJ@3x!%%8h1p@z$dSEoJ zXC!VZ#1h>VR5M^3eq1hiNz{r%E15(7Y%f+-b1ZgxBRIk!*C-t+@XXot&){KC6{EsM zFC-!&vR}LZ%+RT`7tr8RCk;K$KjWFh@MGQ)QocCf-gej2)RbVQ&D>|~+^M3r)kRG~ zriB{Ab*DP6Zn;Oi%GFIry1sF?{@2sVNk_AhnmdFUeFRnpMJn?`Ce-(2fhxZ62(uC2 z|ET1zC=yIh4K^6M3tkHMfuMc6(WeaGw%SpNpUm36BZp+mjfcL+2 z^1{1&AL4^7bRO|O2(k!WX`}kt7`i_Dp|tM)Rv5Pa>k=yLh-Sg^o$WhvnQ;sMhiA1a zQZy7i40m*hG$J!EKg)ss=v}`NIg?>g)+HuvlIHZLM2EjF#bBdujeg{|-eGM6gz(wn za*L`CTZQ9V5Ppnr)&fQ$!aT&$V2Y!^C{aSaUadAhJ^U#Y2}sc%crmvc=&f9%e` zy-Rn;r~sR<7#`UYxbR_Jzd0V;A!{4eY0+t>9U#Jw(cv1R{g*OosOVrnzb_B2vU)fx zH6F2U@Q8*TnY%)Gg({lw;Kib{PF#D7$H?lWMgrNDST_`Ds{41GDPYk^vp|E z#V~12EHX4?du4Lm7b3Su*_S(Eik%C~aH`4)Rd)#zq&zu)BsR@1HA#umY*bY26|~v#4b?2?!fO8QaE!cs~`FZ$^0l!M6YddamN^4LY(dVJc|9P|D(HKUY+LPd&Pj~5rMMWd{#Kpz+_^ygtrG~M+x_MF!^vQ2JNv4D2 z1^8D9pAlJ6s~QT3JXf93aeN-*Prv6^R8(Za?l-P{q$i$=@BZ~99Phr6ru2U%SJ4!? zT*kU+Z9%?v2gn_GwGvq`s$2-@+Hwvbif0mZoP2f_JQ?cqKvi;2kNzPR7RS*iftF1n zC(hM*#mg@>sChFT!@QfAhji{SD2+1V4lUNb@0Sl>Nz%YO&?Cip|MUQNsh_7K3wx!@ zAt~BUGn6acOyO25!*AB04;?yW|LM(b%a%yR&E@fA-3*aJ+vYI$;acIcv-it-Q@tIj z4zkrc4OE`bIpsb4?iK$@S=@1E%vl#k#QI3I%ahH$*SK|vR2N~mpD_+T5Gxh? z4^^;HjSf9nNgb;tH|dFQtG}$}S}5PBgP-hesJ8gi zvI`y!+@3E^W*iIsSm!qI1c zL)WYlbI+vp!3R$BZ*Cu`Z21vpR$<*}&NNYdc;C1sK64q{=qd=ZJ#%hE%8)>*W9jW@`b9vR&{ra(I zBai*fDMZZfMPGPd_9C(4$M@Y6-ow&O-)3`eob+Vbzkk2+ZCz%qqnE{xNQr-{$tmK+ zNZg-Bl@)|_>`r2iPF-X7X)R7xN|=3>sDSUS#KYXd}uw6t9xFk7&2cmlqRPYYmHm3~!p;ez$S_|j)0#w+86Lqr6 zn^vA3V3iLN%@nt*WJ3ka)B8O6Bv9))fQb6BUhu%8hUKL_eA5;!|Yu)1c zIHY7{=$2hk$6dE39u(rJQeu`}q}BOjKRG0wsq1`3FIb|vqW7wneBXZfY<0vozu2uw z{OLANWy93|ubK z7M%|sy$ZRv{mMk!V8cb;*`DlJNtc)Run}-H?r%!s-6TAIeWAe)Z7Hh#G&97)8_?_2JJ)sr+Lk!eVLN2A+o&PUEO8v}M0bU(qjtkx2= zPmL-6SWUh_V>=pBY@dTSn$A29LSm0TD2|iQ_@0!HV#_{int+8-% z+wd=s7%#2o5VWPo`1LX;hCQpyhmgK4-%s@_ZrQweragHku&}+`{8^0tAv=^C6oL)w zz48|cT?{?>iaEIUgkZss|H{v-X+o5xda4N!;$ubR9tSVmR#-ixJjf<38#9)SqTP+q3IHg=^Ax*xvu`pnY9^8q=$owk= zFve-LrVv85MuXpr@zm(gJl$O~{rEg`Z?$vnR{7GT44%QaNipoJF>Q&2=Qe%+#Sd0) zp>k=wVB5@{^cPPzuUh6vAi~pCgMP?eKp6|~<$Ri8MS&aTw&C1ZA^p?-1KB$g zzBBYPhdDdiire;dWOCnrc)45cZ9s7YUr57b1l8I&Xyq~Hlg7f$@aSZnKj{^`b zZ0OUb#Ej7myokkpf5uUF0Tn13GENGzAmtM55S&Twxwv+=H&3i@6Z(=84-+wjXgwM~ zWz{UoYoqWsZ5gt?i@81w!LCi|$X-8-V8y2j8Z|OJ$v7OZ8%s^BDlMXbf!Yxpk>{WE zQhLIH1H#|JAe&Ei3+ut+>8t+;Ybh+SI3%?{enz;TLr54!8I{m*QiDvD5T+3lvND;D zYmeEY^ws%?i-ZT1!WOl!=Er#Ii5r~|L-AA{nrvG{kdwwo(ePtRWrtY*+@qxt*B5@R z#v)8FtO;&APF^9a`-Yjwi4TYvA z>V^_43!63lbChK6-7d%QA)S{-1MqF*->x~e9r#m~RhZz&3~tmGIE#k!)?E66Q>%QL zeRa{^I38=6&0$klq!`fec2f+M7<|$brFdGY6b&i<*4^qn_zq2%sdc`It{%bY$s}@ALyanG ze=72J4azd7-7i`gMf<1z0A4#m8&jKwEcH1WzHP}6#HmnrENuMCNWnzJ_iW@FOZOq& z3NM71RX=kclb|V@b-JT`igBWICl~n-J+wbbe8|?5hyzxNOkB37w3gnBc-+J&k)E`QXrHe0m$*Ub7CyWS=W!6Bfdj{D%HM{u#*t9RKmSilE zJ=}n-pW=-zdOn+Rb}=8fi0!z5Hb(PJm+c1XB1XGHNdH!%c`lXmTff~}pKdfxja=A2 z*hnWx?Es$^{bSUC3g>XByCB$n9`kDmM1Ds|m1G)3xfcHmEJb^RuHtU7v^{vdQteGj zDunX7k~e=g9Vtdb(fBWr{2b(ld|WQC*V5+^?rBJqDBhiy_9R*4UDy*Pdn)w)j);@w z-@rWV7OvH5omycGaHby^>D{k&KP1M1Le0=daJCjt*N$1LSoZ4&WsmK2~ zD&moo5v_4J@9e&VJV6%1`zbGOTRuDQX1f1l!g(r*z?ohOZ}%R;4bWKnhe<)#F7pbb zbg?CIA!L8c=(oi+TnAgjhO-Yud+~Y>oY?5hyQ7WxM>>vIBf2T~ z@BE`lH+o-gGaG6%hhpL<)oABjrQVIqNf&&fdZ#{tf}FL&O_B;sOb)3Ne+JE`@to<; za-j7*MZ`~!NJ+fI&n02JKy=-VNb3E01`eA$xWu3F1Hvfj;;Hz5?HEDkaU7L9q{weI zv7v|XIHSjHvLEZbsMlnOFFbJANC4cjFPZ+L;qtcs*Z+KZf|JZc7?nE;-&sl0!KTc| zHA-VH^B&nUsWL^+Yjzy?*UGHOJOoiA+06_nnnXRU`c-W!ce5?_B%GRzr0pM_5f!pt zM(tyG&=3i}>+PeMi+Jwo1oMK}tC|NjAbilDvQ_`nY4WuZ9i$C;i_m5$^L^Q|ub`<3?0q}MYwt>2%o($^a#IM_P#7IZltgiQcS2(pWM(dSL)XxYV zSQxLvZ9=u8aXPY&ckbYyex2(i__Na0hhHw}B0d^peAV#|^60VOj;eK;;)dVk7PWr_ z@dXnK5CP_#Zk58KZ%Sl`GV)`xrH5Gll*OP5yppoZb!3WeS@esk;=_?**IMS$jxF|} zpC@e6rip?-($$qe__R=gaCco5pW53$H~7c06n?LJcRlz0KvuWVZejMQVn{7@3uGhY zTG+ZvcfW_0t+;1J-OoKp@i0c^M+tkchv)2fg%2$Jm`<{er9X6M`$lxf$z+gqHu(cS zOb2TL>!j~XSC!}2koz#$sjOJ7_XBPAXK3-H`cDcSme=@N|-L|wBDyE)6S=Y)3^9Xe~Nn_vu1+&y1iA@y$$^IMB; z^g*;Q+Ts^)9)YxKEUaR0Mx~TNFb(N(E-^hD$r&nR|^t?HX|HnK$* zTPvce3s2mlZ2Xmi<#V@+)aKJL%l}3~CeDL8amMG_;u#)?>(+3apr7-p<5Rt+Rs#PO zFpq#fW$oj$4gRW)ogX#D9Zbc=;&_LUlM8=uVnT^bKN)SOKgi4Imk*0tKhgJ&{5Vc6 z^LE@sUpP@x^LbJV1%FCvL>nZPbSA$4t}~7|EHvQ|-+MM2T?Fx555l?rGdX6|)>c{Y zmpCD(&y`B(YgFJ56G}&>BN0ZvWBLE{;MNU_28FH-ZbvF+Sa`Dj6-!+E{MsSNid5Yu z&lubYVf>dEF|gur$JA=Ueh6CN*{^l!V{cr6Kx%+|`m5P#E7(fM-^{7H`PU9~8Ez5! zWQB8o$P1aEOP(mqY^OxsA6CT!hQI3FN^DLp3Z}d*(zs4}o#psv*Pr)8wf^4C3MXln zHsUVx?4&oO3eu`0BNLEPBEAfhq4|A_9VOh}k`Ss@C zm3cMMCJb)ffAZ=g{>+raNUil5(7qd|vIpsDQdA_#ZcgnGE$BGhNxRzAgI}}&`UjW(mG)xwexB1RKwMZZt~|#snMM@ zH+bM{3Q7k|79wr%uvIoo+u!Kbgg2~h3~Sdj_VG;OJIr7a2K)e}>MXrZD>vDl{fGnv zrf7c8J53ky5-}J1obz|~HE0B<7CDgml#^LV1*Hf^bs%2;GK7-oSnddyr2+e1`&G5bOz`lUSex=c##rB z)hOWy=(d(tp6Y%K94Mu>!c_`w6)93{I6lm5$o8aqJ1(}Soo;fD^|#X{p4i05ige6& z4oiuG=wBsP3_Yp!K8&C`=BjejTlWvQMNvZXZ`|wr?h$=giY6?GnADbu!GfOntvWMu z#mG`18Ibw(9J!LEj1mSQ;u5y~M97L5S_R-^J_(e&Q-A$(nrw`3;vVSs%4|u0zvo-n zF9xxrknwxJkBBG2&KA_;0N+DUMb`_GCNue6C4PKcRCnZ3Hy?bwYVIJF7V+^CWHVu+Fz(f_5LMv#{E8*-W8ej1FDM}p?&gA|sWa1TP!n9VG6Y$%2-9d+Pm^=y9456Tjz- z9FcVCbe(f-VAGTBLoc|A;6*fss6e!~mS~`~nA6Z&+45u~flOmuQ*`?3en45dYaTv) z&zKoJ7D{zAYVX0hJ8M(d#isx_HvTz&?+ov|Dmy!u*tph^*=#pxnH~(pYGeI-QB+Ys zcgKm0fVdv|A#U$ZIiKc@v$+>Lj)3Yp>Eay_vUR7)Jk(p?65ZOm)cZi9s;v7bkb|nM z9Eg!N>aVpzZ)1Wi(b~<4a)F;GfQn%GBtZCOmUyz&L)Xa>|JUY@(>n`QS;MT`6R~uD z1LuP$K@^`iR?y509B4xahr+VE4_sfdHSK&Z7x7f%!{i>zJ_>55`M%8jf#Zw)??K~d zlw)q-9Z-&A(G&^>t%r$g`RFepDCh>BIrh9f_0TJ~Z)-FD0!H}}p%+W5N2ahZm(Ow< zGOfHWm^Mr!d)NVc;E-?9-;HjAEX+?`>xy5U?o`W6 zt+07)f{%B{b9WmP0spR`2RtXBt9PZ;^lll42~V&^A=skOG-wE`Tjw#I*4Gpf!bGQ! za#7?(@ys2Tl2E-eKg;Iwb@8XH z>@q&;(EeLlX{U2q9DH!WDj_Zjh+SIEXU)v6m>Qz?87Y64UV6Igv-im;%7jO~Mg@$L zvNN|^2-P#$bymC8h$7;+EG{15+R*4D5^hLzSU!jcYQG$*9?@U5vt=ToTcFr~c`+9~ zmly2VmBKA#QHdO`_f00sv>CWa0*1NwK6`3k><405DI0r@z6PQ5`__0>ZyL`X=)bDP zQXX{FMU?LLm*}M3Xny*o?&G@}R)*tfiV{|8x?=DXiMZ)Nz6m2T4{qXGqON^-SiDw2 z^d^hBPMySHPh`n}3)JU9)!IA(YFuu_8eZ43t5;~vh;Yq0QoBPF`VHn%Cb4P>*I%;K zx^h4$Pc8ZFqDbu+w11st_Tz<6d}@A8Eb2^tDCR*iY%-r8nnV%-=ioqR4IU@rc=;3I zo0HQo_$Jt)x|gkB;fUywn)yD-PW)nfI;vGu8>+lLhg>#g+b`#c0#Z|hhH!4MSTSM6 zuwu}Zu{uHJ+yO+n232cIrD<-%(==o{hj1^o?QcTfEpXg^JSi$(@YUQSd?{lT(ll7R z$Zz`u^8y&T4zQ}VoN=iaE$t@URjVU;)tz%9vcKrX=$^D#a`KCMqODF#rH%e#L=pXm zERtkw?5_ZpNFgdnZ|WBT=zUqkSD=`2qtz1h#KDP$Z!V(x3xTqD3mVU5vu}JO*K^Y4 zcLtr3RqN=9J^%PRXSXWoFHJz-PWKyU>a^~=!HfN=GVpSZLma}UGPg;`A@CziXKC1> zdAY(sI;(QwfB-+{l%c}0@35s3yB?GyBR$zgDOq)|F03W+5IS0Ze)qMB{5DhO9suO4 z<7vpK?+bS3g`eFDF4d_APG~-2d-Y>;34g%;j<}PgXWW(q>ydcCw`m2_vX5iHvj33 z>gr%sV<{&yENss-df@BEWX2;N%FV^nDYg2)R=n$se{W%zbT>b2f8ghLXLy_js~w2+ z3`5k+A>afu(J6kV=i$dVCw-moC+A^f5wi&a0*0nmm2EST6_*PZ#(Kh$Lah0#Nnne| z+dHaP;^J68wS()mYl67SuX3%Ee=cD~C=%TvzuZ*3!~H!iF{2Bd+*;_p4NAa(AY2(9 z%>R{8e1fhuZ}ar){!Yj&+J;ogK51}y9K^2FV1eA?&~xJitE?ZF()X{X8AA+8&$`Wt z6a~Tj5VGZ#pGrXf!kqN{a4YD`@CD)P)G0)d08AX&Uge(={QM=&%0Iuoz9H>_V>}2_ zIUKq3NpRy50~ZMWXo&$zYl*#=usx^zGyY+(+<_!^nX_MB<)WA%!+2h1UY?Sg8Utx} z`E4;CQS}>Lk~h0%PQF?0ni0XB;Q1;3C29>X$SPqUJ-pKd(drP#@HpMtt{QeF>XBXP za!&)!sW%sO!8(k=!&NjVVc>Xnu}PZd``bExJq5}w&v(_kC4hF82%TL)1@kK(ArYU` z&sX@iHdQA3_>{!Jo4|av->IAgDP&(#@kp$wZ-EG;n!ji)D~Onq%e?0(q20$`KOWgw z_L?CSXll^V^8>lz7;VYQKg|r z@f|1uR-%ZWx}+u_`E=b!pLUSoJzF|GL@GDVfM<$G+xh&cqC%8k3z9Vf{TtCl75==7 zQ{|z%8qe1W>O>vN#i1P8#3%uxj(QjCdRsR`S0n8R2aRAudQnFEORD7?xM}gIZwDf` z8plgRx1c{;=7nVNXKWWSFSB2teWe=>ZEUqyuhWc_tX_2j6Kk?*y|*1s_TqdWx3qg7 z36PbNZemJJclc+;oZA+1Uw?B5sO3n@-i@JfmmA_tp_8sQ0Yv z#fW`KK1^KvfL#BifV z`7q#Gfh=R*-cMq4^Hn#Xpy&ilYnlD2azBFpoid8lqHHuN>BVA^oZ#%)>8back)`fU= zS*?lyRG(%XE4qt3M4pSO^VedRZ6_Sy;ESNEi+nhDUEl&*d_Rx*wMdWI{aiVKb%n) z@$JeF$k97{^W@FHr;Ig+m+LcryHZf3ww1%3`-&m7Fgu&$1V5&So52Ge7qJZl=l;7! zM~O@^>pHJ9MW>ywAQ`PwnRt*-g1(exD#ZKIqb?4)WR;?XtvGNez5?B08W>=@5m#GS zXn2mWnpkM=U)BA7BvlX{l`TKnQ{1JGNcG{-;p0^=$?0l_?sbt~me}Vf*TtS_HwUZZ z<})DpJ(&{{|5b?CG1?1Y#M@u0W)opeia4*!6iA+GN|o5Y=4m|QJW+D<<1H;a(l&h8 z;`V)e9s!o|ImQRLQ-hB@vvvqJZ}wVtJ|BCOt5Nd&U8(6AOUrw2HfGX-QOZq?2_?%$ zKyP6_p0PedYOeIH^6Y)K{LtAWBjL-@knO(QKa0QDJMo9G%&WT0u#T%t~V0-Q`l(z?)7g&JY#mzKjY1l-+F28Dg=}XJk8; zNi0)sPJ_hwW9(&Yh{a7Hw*v<1>=;YBtkfE{`iVGD7Hc)7(M+o5E?%27UU_-z_v0yQ zK~4-q)j(p|_|))DEJf|9;07aKu^QpBHXXzMwN^p8I9xM#w>cp#FqAdj>!1}ISB)AW zZn13bgSBs|(fgT6^Gc=@+>Ywc6S^3N4rlSADL56{=WTHVCtiKEK`dJI`?c(gWiFzBJ^yMt6cjD*XfJxW)Ox z8DQi+8@RrhqUL%0NT83QVXr;DL3<`;Mu4Z&*(zh4?j@o{>&7$@_Kul`q*sDou_<+L&(gQto;r+pMqV`Uh@dVaDRrxQggQ9| zr{+`<%0?{9sEMK^|1S$Lk;%^l?S6^%*1QDgxQWG%zTXXP%GJ!F5*`^Ll(c3S6*~6s z@2WEbE)~H=d;%$>L{4-XK+Kj%W{%Xxo6i?&MWs@w3TigWz20Yk<_<(XYpf|5KD!;Z zAKSn;%{+0^Y1i_xuoPi5WAT0}2%pXBBwU5CTJ(ztEXQ@bC(eu$SOm(;tqJb?F zX1qAsxHTHISjVWS=d5QrM0y)!bx|lL6Pc2h;y#pE7%>WV!4f)^}!9z1>t194wI z!`0A6uPv3{DS!oIh31Vg)x`?!DW$UmM7LMs$O}UKq}h5E*tO|m8t>kEKMB16)v0q4=7eAYrTdTVfvzNI$60=QZj67DT$a>+C_bTG`#9O zTg0}JP56_RDQ_6(p3iL7&dF3fe6gB|sQ7LJC zz5s%%M9|64p~-C1karRgpmdH?nbtmS-u0u`Fq~o3z;bLqAl`KAnZPqA7*5)TF{F#c zQgq?NeO84KiEFe+$QY=Ak^T%4vU@*(p=EU3!s;=aA8MexrK4;oFA(belSE5ahCa7(D7scdBIB{rc;&{K@B_VyzJ)wke zGfs^TrVcSj-{6reI*_2Wx6&?TU03n4BtJ# zo)7T!F3P^z z3I+9I9Mqep$1PINTYUgpx3rr2us0xjVvUQE=E-UZVSACra&65{$lI#{5o_A`V13i= z{4fn-d5?d=8n62J%;zsU_`+|3EWamBfYZvg>#X6)}#QTB$&FZ{#_ z5EiYQxOPup-OT|5iajw@grJ;|?8w*6{*l82fV&)g^*}#}hhIzb^lY;&XDh03_AHsK zm+f1xctFO)$XvCtM2mQ`R^9Dq*K!MAe&&VSN&I%UbC5=_#3x(Z9ib@Vysqq)td0|0PaQJCP6 zZn+mV3jm`~_zB4Imj#%paQXDEyy|Jf|Cc!o@~!cx7Pii=RNk$vFCXh2A+~T1RXemR z_ps#-{hG`1eHQ_M>h8hkn{VQyAL80=-{iA!971jyvZO7~%7G<0psz%EV{z@0wft;K z$N5fHQeWD-p7M2jDF5TS82C_E1_@D=q5PNH+*m5Q%~##s-N067CYJg!9@k4&M<#uFh83CIQU6C~Wt1k3_~L z;MYOs(&&K`$?IX#xpgQeAPt$%1fqEwGC=H?$av*wr6V_Q-62$Cq?S*UWim+t?K3I?B> zYwcPh@9G^y;3uFh<*R#{SKmloi-_ZDV&2UY`x<9`M;ac)zYRbU1=_Y(MDW{r)wvs< zt?~pwK(_#6zmRX<=4H!uL=)+pP3FhNzV^F41Am)AbNB$D4}6Q9{KQAE z%9Y2D@gyGL@>p@54)Jw!Sbkjl>Y2w?ono&WY?(YaYz8;cOs!~J?N4%C~SJ? zmg!x?Yd$yUi~g^}uD~4G)|~L*yH%j_L=Q0#kJyj+o;FBsVM#nF8>s^zLz;d37{%Ps z!;ICr(VY1H3Q)FhP{QMJ-j2liyPGFHdF18U*E&sCeb1@oWh_94sshO1LB~bTR>TpB z;9p!k-=u4q2q=L0%iN|5wSd)b^Rj=kJo?`sYS z+|0FwSz35MAT)nzOP?sJ%%V!E!l@J6+>v<19~T#Y!l}a^<_rZXFmd~vYt)`=69U%) zv&7E#6>HITi4Q_`YaeWDJ2nr4V~Vb*_9ME%(a ze-{AOJ30LAG~U6;9Rx7h)0h%HLm{n)g}wH&1CO2`M$a_BB^Uz^P$NU2ySKt{{P@&{ zl4Q7AWtLqTI*yQX3i_B2E>3F_p+Xqpz1ac{A3ae)hCuRPGsY4HPztC_3G)G2(*q&P z15Q`^MMt+_wg6G?$_I&ZXZTz0uK3i9j7OJNr(WFk8muOT?rAGPS9&^#Pu^c^GP-^y z3(7nzrq#oCi#5`=UpuGrT!NaQW53)EAeAmdi)WQhcg5`bv0ZAkFpMqX0Lgsnw-rOcldS2{;7s z|EV+5kZ7JyLEl(mNhtr2BEN>C(vt4 z&gy?C{GaxZB-uHBs;{o6SdZMzl0wOj5hz9ujT4$d@qZBurs!8n2-PQUW)hdq&O48_ z_Q(WJxs|8F4UC46}&M=c__6*LTIvTOkk|X2mlXu}` zSk+vJ-g0G8mc!R?OL0}RR{~7=F|&<#_Gb^IMaxr5q^VgW^%=k|58LmqR5U=s)gIh` zhae$Xa)e}wRAPVhyWNlbCeDzgksv8kw9)BQe)7=_B!-lJ6e{8*KWjsEno)d%`)Rgw zqh0Z5!1r+1j|N#UTiAx)PFTe8oJ7H`6=seHhRm$+YJOmfT*TuDfHuaaLAIm|0YVp5 z$#C7+0@1-*v`0voqum>h8gY{8C)$wMY9^DmsQ_4El0EsO_nvcM2=DY3>S=(276(aRW8#bnI7z1z$lTT@Fb54=BhU zmTbGjtp3Vs=TWDo~Tg*IW*}= zJoRcIV;odGYU7I%e4tEURW~Px1c}axuGku_osOKnVwoR)?rM!Vn}+n`eXUL;v)7!p za{4^iN#aHtXj{jxXVt$;XmH&Zy+pz?X!>v%N#b>t;>}fXr!i0>M*MtvtV$YkcwQnJ zOT3}rr3c_WAu#epgiQ4dlsI7UeJ>Im=v;Of6GzfT;s7eH6kNx2^D|;`57oyH;X*x7 zr+KQy_G4)1-%~A*HrCsNAMgxb9PB@=@|3O<&ka*_izq+-=wP*O!)M;zVp!ZHchz~u zommSKr2{TrT_#y8+}$?LpI(YSl<(MtY_o^^mLA#+PAwdO=>n)5eaj4PNRaYsCK`Xl z-wf=kdq7TwPzQQN9}M7eC_`;ku(>$I`^gE5{57*$MX)zVXpKE@Dm4Wco0vL46klT7 z#-uswZZ7(zK{~rqqSq(ly7naO4otmvTvS6n5XN4ALXo<<|e8mSaSrxaUy+KTlL@X*3VKCH*(3OgTg80Yx zI|ey7?+p8t4fqp9clleJKGtkpp-CwZd=hJNM=pO~pQY3K zILSQKglW>;x-_Kt80vX&KRLYg)S@?)qatrXSqKhK3g!$`aP#zS#I6bSl8zAX2!W%5 zOE#*nsy&h|Qfng)rLK;J0mtXN@s#%^Rj8dGNpu_n+bdO_dCxaNr2l-NFshNRB$1$O z{iNgbK%h2oaZXhFf`P=K2+a+h);NL+93}Kiw^yoRI^QKPbT->WUJW1r-y1}>{<=ZL zisuLt#_Kf!8Q#=hEr3Xc-!!b0{gqer@)Z;OUNk1>tq4J+eMfF_8dRnPk$(I)C&ZGy z$ulANY-oANH{A`{`XSN@F_Hqyj|c0Zfo#u0s)keZfll84eS&(vc(LwK>;KCXHNUmw zyQ`71%ex%_;uovxC;{UCn zyAh!)&)E9yO#Kd`mA8}7{-uU##3O?Wq)BYC@A%Uh$S!`Q8yv%?5ftiWq^3(^p!(*dGXEAH-xnZK1} zQh}~&QJ3(cPCI#>y=$1a3T<|iFWhR^%wS0~LjU)>c6dKNN5EA(osdUCZe6RRRCsQR z)Q4zN?D?00nHwTibG;G>1e3#k)kKyYC*%7cy(G!S)s+SA=j`#SFiRawF3)Se{gx0inx!njuvz^PpYyYvep z7XHzpM3r#DXM#6+6CKTr<7KI%CBZ#T6{G50_^xe=4pF{_jPZvL{`11bUyg9I6T3q(Oz>?-qibsy0+^{5r=WM2(=%O!?AR2267FlSvuYm&ip2o-AATqd}Z+VH`ClV0M8&HhF zelx%jMqjV-D-}TLphPEuak~NUbqIhd+=i>7bI~$1)qVvs)0vdbcL=ha&ZUhfm{i`z z-8g(DGiHx^B3f_mv<8VACiA^n()_du$-tG*fep0_KNd5!vDW7{H|Hi~MfA^a^O$5J zVF2BX39ceCSxU=zFcV^0ys){gnTQ3yXT1LTt3azh_A6Fd3mWUgnPxOQU&0{mXZS)h zkv$caT}V=OY^?breKZ}(Vo+#b`IXNYT2f=>cLUF+PM3TqX_{Jlx?35Zj(mE+Bc2qv zOmX7;7*cnJEI5dcWQ#PgxotD_3jQtcdh#eAX$zzivQMwS6e2lKy1{+_(AuJP?&yhO zHTk=`EgJ6%=n}@sR_xsem|I?14`4C4_D6*$ZaV?8@sXs7saytFW*ka8LZOOz z0Mt1FM~^sVYc6G|NKw!E-8|@)zMsV9F05AY+(3cDj3qLndPSUQbNN zix4jpIr?LTsy1}v*k~Iq*A4lq&fAk1>d}FPhE~6bEK#e=#G&u)&5yqQH7*4en< z`vGkw|D~;R;6Nv6Nv26`Sc;DAlV28L;W%b+*V{XGjf;DT-9*J45$gA(aeJK2)3^Hr zFWAd=xY7-76us@!)n^C9?ICTld}>p7iIR9V8USH5kQlTV{M>VcHUUljSlBT%8cif; zoUUPqtmq6NPNL_Zyko+tb(~!Oq4#e4Yof)=#ID#MKp9##k$R~ufVsf;*9g7v1-_@i zlW3oMaeWnl|MI2#mJi5?BtAf8G<6Z>7wxIW zoe_q_UEH%vzPdd%MOK1)%30aHN)sJU;v7;WR&(Wnl3U6HTVl+9|NvufO)k`)@u}uEBip&c2`n~^jtq0`2Chb zbQDR0xLH~;b*m`}CClNX8CA`G&Lpg7n-28Yk#r5utUr*;AVX8pBtGTbaP1zZn6 zfVT=%kWo+mAFAFw9?JHO|3)fW>=X@QW^9qNkIFWKv1iSaWGh=@EFo*f*q5Bs)X)ELkg_U_xF09KknCkcfXozuDP!BJkDeJ9Pa~Z3(h3^`TN(4MLjk@oISk@+snaw_Dl|U9=)9eufYhJZ zg_D2%iBJ4&uw>sgP&Bb3N}93Zo4=5%v&aV2 zqK8mk#Se&gMkGzqP(fzc~vdy+q69^j|MsdJu9_ z>(BdoQrJ<@jlBis>(?qwiQCho0RzuOz`WJpVbp*_N{Jgz!C<|nh>(myG!At%xP|bD zPeSE~kQ+&dz4y3n?qMM-F_XB7`qI!3b)MqN)}sT8aeDiCq;Le!zyE%PU0B=GO#YIP z7L*E3h4+lu$Ayfu6(Qi#-Bq||9TPQqdkZ8=W#+urtl~DyV2&o>AJyf*$3mW*J%07d z6%DrwdEfwA=Oo_DTc99hwoMQQKfCcu<+0T&Cup|F5cK6vk0Ql|YXb^$ZeN%X*^{Bl zmQyBJc-60|I*E2|uP6!yh=k;bxHNMdz5X0ilK# zlo!4#O(rYG)vLeq=oi-dlCsGc@Dn`t@b`KULf`fQd1g4HzUZg`)qcC%S5eFe|BJqc zxRJhCcbPr>lcdtD{&~>ad@)SBD%-zos^Xi;T7X>*h1d^h6zSGczc!lnsYq=my;J(c zQ4Ct-+n6h)GTz0$O>ve0D}P@Ghv=V!v%?%7LyOHcsD7l~V7XRsUAhMx5hMi*$?x&R zGmC2A;C+$dhk{eUL^>Y^v{dGLDTvnn^{<9wkaaSTg2t}UU3S<0O(*xO#_c`@0tHg9 zg^n4<(8=OV-nGRcO4A37^wmU`EnCUmcd8Tj4w!a!vtTa}dES`bI~8C@{kJRpLiVG@ z?Q2U_EsT^;pNUlC-Ri?j10y>;D}Q_6yvBS(snUdko5Jant#7Z;|3kD-&j+^G=Db_E zFN_vvUNY&zt>iCuq@OUCde{H_YI%af$yyr6+2t0_NBmZ=-~kkQyagH0GFU=ZyO6bX ziJ2vMyN^8f;p|k{GVUokPej3o%bj<&wzg(@b#+m4?%(uZirVSf6^9{giH(%2a{(in zKf9KmznTxiIkYfg1)W@D(>M|3(W7HlC5HUK<~nml1d$AVC4cSRfFwY9<$d2?9k;@m z#(xXf-EMpZx%@bySwreoFRl$(tl8Ftzyg`I<{bfv57)ukigs4s*B-hnunWgPu?-T% z2jf!O^`W~U7@Fm?+Dk%sEKm(L38|7w#VZRo(dg}zB{j9MHu&DqA~xsEr8vNQTSZ$R@ofglMCpGymVB|`xDna*^kL=(UPTZVgKn&?ZNI3ljapk@CO^q zP<8UV(klOugLGB}!dYUC)1Hw>riGHd^0agl3l`MnJO?co7{#&N4*VwCD^MfVzONGU zW&b9gd`R={BEy=OH0xm1+dGne>@(hw5h(wd1l5p7thJN&VX!}Dh3u-Z1U6!VE<4wo zn;Y+uGW4uT^mjI1p^?`hga6tKZgPcgE@o0N}B3)Lf!yyMQNymBfrx9aq2KCCDfvHVfCOH9T7ExIdaMZJbe z*dAR~_~3z{XQL(~^)B}F6vz&Zg>|1=**=>kOzTUXeRTC3R=KZ?eEE$$E zj%YzpbR-+KPVTtlNoduyPWHvT&y8eF&vyUERwpx2H)H3nx4)`xJGi#|EL$yL_$a6^9{HihGPMU2p{>Sv!^2|r} zm7vJI=-Z>ID;!;)Y_f0V@<}q0p31;3BuK$4l(8|hHHchXS7VHdK44{t?mx0(Up4p( zs={x6wBU6a+ut~b*i|iJZ_}W2z@qDcuntmeZz(yi4#WP{baY^UFNFU>9PJRR<3y9!V&fSz(0z zF{d?-w$4>MMYM~ERHYGHQol5N0DkP-B6o$~-v_klyUozqV|krV6;7fFXfNKrTKDTm zQhF=s09YYTWO#cyg+@X+4CTp+6t+_TF~I*9$yn z#J0Ozn#(r%jxxyN4yipKrEqvBrzz?DiSu4cI&pBcq#J|PKCl!Zj`;Ef_ET%uPsh!? zlKS*JdMob7_@An+?~E^kEz|txqi91(5La1Ds8nwUO59eXMSLk|I$qU6$f#(qViT_x zWF0ZNYnJ$|o=sgyUc9L3;n}+%0M0q<*gka#I#plkAHFZTn;ygm(lQcp-)$$Wc8)Ns zS)nYfundijURckFa45PvIjqVFFR(~HeC%OHb<1{xlNVHrS6fCtk;1GDg^M|ho)Uk4 zx@W$?igTKY^M zB!*L7mlJeZ#!wX_q~lgtK|c-5O@fTm9it^DD{lY4r(k1C|B407!s};uuHeL=)QSFJ9=h-R-8|FpDaLI>drGIaX6Y|w+$GZMTUiTzfL{_R zc`ilcfyrf46_-DNGyBrZBd%5#yhTcH75ZTELYJlA!n}n{Jj}j_5+nFd(1aB2lI)Bz zd95KZ>Wm{h-Hpy=s}%B+25;vM6t_!NVoH;pB`X`G-)Y{7@z^Kifd7HCnVE;}E+>Uu z29ZlLR;p^UJUUuW`i>T28PjEw zgqKk#{3Uj3mCXnV6Efbkq@t=`8`OvDO~p91wUdsB(i2XvBo-T{~mfN*AKvuOAp>ztR=Qu(yr8U+1rT~`wGPqwEd4%lG zmMZi*1)Evp_Zok&$fX=J2{3zg`S1~eC(|q0D3dt6ziHN|cZJzMFG`4NNmojoHY%)6 zPA@(}F&B!&HMf~xuq|f1!^N52;@{72>UN&!j{tAKu4F(^2|KX&lY!!pneYTYHY0So z^@zlTI!hT^=o*pTFlE2VPkhlG)EK{Hb4ZE@JNt>1C24sT1&{9!w%Lc*G^;QUAxNBH zVIoxjQEL%|w_i~i74Z@ag0 zZg%Y(1i3p&;A8boh6)p%0%M}%mxM^#8I$b+&Aoz`lAK%1M6|BfT9bha8VtrHG=i+pwT57!E;$ z8COShFyyYh@75WU(k|w43@uYAj!PxcHl`T1JC^zfSVMk6{V=5dvGSQRTJkYH=buV` z%3{ioWA(@VQ+Fc9^s56dAcy`EhF%m)_GlF+AB&CYXlt&DY&9@8P7D5XoF3)x>RW8t z)Za>aXl%ASEIn-T>ZbR@#&)!T0*4p0*K{7D7DPWE{PDi$&nmER1b|DFS#mTJm2x3= z>9Rlhp`s!*p6X}Z$B5q>Q+@VDVA8}%{F{1@eW2v;L8uC`O6BPxO(QYPO5(UduABk- zdWHwOrKZ|+htfFq(XDfbg?u!)moObCS+*q=K0w#5k~^tUv9PZj{Ut2Zf9U>DY#M3@ zWgnCj3-+=_)k*|tR_|1ep4^w)pU7=b{iYFIZR*C=dgjId!C6_~B2x1sDQiE|+Ry90 zDm<-8UZd{`*bmCo2Lb=-QC*NU1W}aDv4eZKB$v+d%%Le%BH z^C@OAbCz_huayqAvY)pYfhI(^cb{GXH4RT19H`m%sdU9XpQjwJ2;`i*b)*t31^vY( zR4{xnxBOyCsEfQIdI6YA`BxU@u?~W~C<--Yo+zlC`-txT;}4<1uu_tYC+|XZmJ6xM z|D{CKEsu1>Z&sM9pAHy_!)F~^!}oquV&}er3Fm!CmvQ)1KH_WJ zklLqLeJU(kc>~iB495K9WO;FHeAJi0p+y3mKerbCS+eLmBMqZ?-EmiRJROSgD(r~3UF{8FfqIIjM9GXV{`R3Y5s z+ym6P>n(LRq3Llp;#7TkY%u?!(Uta~eWz{4deZ~)!A&Cdwq3WOMcs`ziBzOWna6li z3YxR%q84YbQOCrThyVM21OY)eOCPXD?KK;}xI)nW@M3`+X#;wFiBD5XMr&unyV%WI zwfk;*pz;<^V#y`VnJ-qMLZp}20;q>LI~{7u^n7ksJM+HKp-T$egKBiwYCVd%-ukAX zQe=euHM^WlPp|c9zK=}08`7QH%HGc(oA~9n^WT+~b#c9VfWQ$%^z+U^tyP_B z4~Q_*Q`bxs$^4U~)Bf9)szN`j((nGV2XC>apTqO7>lHJvJ8c?0V??tMV7-mC=OAs+ z;YfN$g_Xc|*7UEmtejNQNivtfzXz59?S@!S6}S#d)m(_TmTrxJwteN`^pPK@-*<*= z*p5K&|B+sfJs`Cql8R0$6-{howC_wuTh*rYql$;;L(@<)4~z8f4z4&F9Z1kUoETv9 zes<<+XlQ7C2-PayjFq_X6J*y{TV)}Z{;SbccSjm>4n9R9;aPM}RP9YMv6E_b3{4Vr zF|V+`qzrQgFP?BM_JI-Iu$?0kMRi}PpNc2mh~xh919U3~tL^r|v_P~llqw{3Xvd7( zNAe{`u;~Y?C;XoXuApt=$9>N$AQo3Q@%_$6(q*ynUcCYaKn42fmfgc`7&Z^7Wwbdq zD4N+h<+rtsQ5T_1A>)yK#sJw}MRg(bV~(2N1D8stM%!aX7W6Si0CDQBO1JDs4Z0RP z`GOj9{6J#6B(toHh_As?Hx($K^%ye$M#=Ok4k3IWY>jvC<6v_U1wq7~NU?mnX9*L2 zFm~7GlcUcRQ#h0g#D?uB(MfAVibLGgv7EPFf~T2@4*W^uvzkd=wwYd4Be2CyxcP(B zjQ)9xK72ZuHOk>g!SlsucBb5vS2`Yw6?hZrsxsQY;IHTW%Q9Um|7zkHKTn39vB{%n zB4vj|z|irqualuD_W|-!x>^Dn;iX%`?gFX)>exK#%hAA@?hC zEXmNTe2FU1rqVw$t#*R>Yb~G>KR*eDB@T(S${uu z7sM}za;u?5Vo4s!k&-iS!+(Py|H$zlAFn+4p_f6;|M~F$@YyoU0gUE&b9~K(+VgEM zYf|=^g9u!9fe!TO==Kmckw0gtJkLwr4zF__p?mr(fl zwkDO`!99w0>c&QzlX|rp$%~xsGTrl1vc4t_TF3fr#6z{nM~W>1UZnW+G4}&G#(sEf znM-;{h}Nd$rQd)COZYmOqz3EG0(uBe>5!sFzh4y7Tbx%0s+%CcZ6@Axre%+#1RgYA zYSMR*1&+PlEWelxT>oqXBjn*Z$$`Bwd98Lws+NbEc@;FHz>Z_h<*9* zQ8Y^+oqye81djls$I&?j&5#L+rOh8y(RvGyIQeq;3lb^#iTVjw_y){_{}OwrBmF zu(Cs((7EK7_ic2ov9)Ow{FwpENaCA%C#;2nHs){&KcIz7t)sy-%H!jycV-m)m6`BsqSxZdyi9BaV~ z^UC36CvJ$i5FAC?5Sw_a{$7;(UhIi=KA^4ReQZO|sFIb9csq;1brm3xXngu#-VgkA(H40)na-d4bj_cx=3tZPH~-sD zz^l9q{2P2Bt)|-haJxd4Kfh_%pld8Gd1tCfP?=P*?`M03dQ#@j^}ZKOBT%{$9zU6m z;z@XA_Wa5fe-OuW%MZgI737E0)RJ>jhVK6g4E8ncp;OuK^)Y+#nK`V3=S)lV3WqUd z4u07b&O>S{U%v)3GPvYC^iwL}t%W>V*Po0mRQA=g$-o2ZzoIzK)K}pplZtO+y6E?a z|E20h`OE%MNG?_qPJ8>q-^=OG9(+*weyWkaTiUD;KY=})01uX#fmt9!t{8Ks=c?Xf zmPc*uKLl#@@PCxNr?w9%hvMWbM9ZO!|FZC;c1?F#-mQ`}l=2jTftp{)ZEE7hn=)c$M@jEdI#Do#&zDTl&HuHqU$9?*TlkaJ-qHMV zTgxliBiL8O|D+7(?la~18C`glN+c;gzo{#uzl|loC@p3K{WrKfz*1vJJRWf_j-|9+nEF9g)Y+cp)S2H>u}>2;>UKQU$GBN8E%2gtbG|ajy;gi=_4%hp#^`=l7VfCw&T79>+^# zwJC}u)LZ+^L(gX0;y>R4IrXFVDqE=8`yz@5!!OQM>Nz?s?zlt}ol`%xkWPeN>0*6? zhcMMx3SsU1TtjWeZmmV-m#8 z=Sa}==fcZYBX+sQs){xsT%myU{)AhGS_u4Ln5adNh$H} zzF=KJA_ZUa;PC|EoT8?LazRyLSeSuSN}xF2Yx5k{Wy)*5f}U!xYU*}}j`5uy9T57V zwl+AXe}c^`myXix-Lk0YHCx*CP>0|#612KyHxT9ehS7kuY3NJ1j~cR0Et^*Js@d^j zqK+w(_~050`p7kC!v9@Ef$j27!+y5r-}zsoCTag940*Q#FBy8aq)f;PN8%Q)t}Dw= zIF$^SxC9*~P>pGa*LPd<*qAd#2cIyb`74@pKGh=y00X)lcvi zydH8hDH+07DLLFYrbhby{!BY)V@y0+k`{VY#Xr5ju7ksba+xT%jR-55sLI!e1#un|{5(|qW2bUXn2Z(dp5VC#3+K$?>W?Dq zD1|N}C_G_4KzRU z>(bdG98^wfmdewK%7q1wF0`KzuKqN;4pw{qdZ;rKi*2F3PWD`67M>Jf&ojM;QIL&` z0UqHM4p_Wbts}xG;D;rm{i~tAjQ(nwd%I(juQK2H^QUuF-faS_8^oehi8MCezDgao zK6V%;QJhoyvIv-l=v&RlIA1j$A+i`5?7tU5$LJxNJS%-6%a4wl!mC<-1C4u38CPZs z$W>!g%?(1;J|Bt`#gTjnLDgpqh!@ko?%_sl?f%ouxYjSOdSqV0pOw;#I-%#C8IGm) zG=$vDQaR`OU>`eb@zaGD@)&LZn$Hq&=laNg(J}b5!_w63MX!Aq!tD!wb&==V;3YQ#I9E-Slq%cSWA4^TDPK7)^r;^{ zaGtZ*jMMU*=1Es#M@cB%x-0trn5@GScr@<0H3wkvgmc4vpGcEwd^i|d)^jt=9KEiW zG_rop6xQ?zn;L2J%A?1Aa=?BJ<4;YWUQgrOpPsoka3izsY!;2lQpNebfOrj>V}Cz-ln_%`R{2Xq3PmXj5d=< z>kqbxU9L)DoUcvnfkd-|ls(jx+3d>YZOQ=og`45|O7>@+=0Z4+eY$>NXG_7(YMlFb zNPvNjm`+DQn>z?JssP8s`KB&4Ul^B+60qFqVeOJo`W?n~okNWj;B#r38-BMD{O+(% z$KFKIF;)>^ePfpl*pH_VVVc~dRZKH0^75sa2SVy;fUF(&jcDs+Aa*^cm&yQuYuET zsOOyxW2T=$I6-1no_?TlMu$eDadqCiw`OvLv0e?nFw*K-E$h(A5!$4#XI|&mj_-yB zv7tvVt*;lj3bTKHUz2F?Q8{)adnm-KvVo$VK?(SxxhXe!lVLaoML4|~dipdSlbYID zDI7Fbe{a0nmA3-l&SUU3@aH{Iq=_1!7UUT~@Pa;`27TEXJW|$}#|v-wA5Vkk?RH?( zxD>ZW!0x+S^S%OhN$9;=$qfKgO)@_SWD3X$zkwlF<9g08eb-cL^c}Ul_Sz+=LZC}G z5lYf5?H@kO*;!hON!KsklJod}zF~LgX6^b9cju9tDqLrjsV=EKK5C8M-k- z{UF`w;d}?#={4`G838oX@ONb)x31S4Lq2iWm60J73+K%BseLbCY}8v2tp$C*fR*2y ztR}aeT-r=$_%s@fCV>Ce{zCKZh$9SK0+DN~@HUL5cyjjN@6R#;OP6ue4{m?FzdiEW ze}Mwc!EvF2BS&-uq1El-*7bz#(u=iKgV6ns6yrZ2C@I|Blr<5Qddyg2uK31Vu(O@Z8_^nfJELZA9!B5CbgKBbcYBx3g9eMg^>#P#elezvsJO zE~ua>AXuh?E|=D;CoSJM0xYSiZ0+MAW+!V#8PE~qU&CN@+Xv|VMOP1!;vQR8Y5)g8 z1nFW72DOpN`SX{z{{b%}s}2D=$u>X_Bp-`0wK8bU&1V-{Lk)hn;aSgNfJzu2qMVF+ z``7Ai={lz%1&z@gTlxEIKY>L0(XTi-^6HU`{p;8evodA|dLeiZSB3WvLiP3nf6#RP zVSpaB2`bM=@O4{%te)A9cnH%EWuc+*fieGlHR~s*n+lYP_s6aW{{aNno6FEq!0f8J z`caky9IhPJ-|%NylS1tz0TM`W^nhkW0}zDbiakt9o5FAT_1Rv1 z9`ZYdodcTV5h}tUlCmF zJsyXFcH2zU;vL?U)aF;*8k$0wa9VqCZ^GzN1rN(TPuyT`+^y+$lv}v__YGcewXlC) z#qOG_r=W+$y7jw>leXZMq34uBAn2Jk!t@{(KKYc=ToBuc6lkc{y9a`*rc?RrO*2v{ z(o%Yxid_aES3i-Nn+f3sO}~*Kn31NxWdg}ha~*po9Yl#a(Mc|H?LgkUeCg(VWt)Cl z8#MAe4S!Yu0hH1fY-NK#5}MGFhZfECy2ZX9T7qRrnBh54PI>=%;{^N++>4L}8)`vU zer%+X%2rFg-*63jV#2zGw7O?3 zj!4yY;+^kk$(H`dVe0oUm;@7&Df>wYZ>ajumvb$D!L^4e5qdT-vTKitX<#Pqs!Z1IX%%hUw7i?hX#5ti)9>P zFR1*tmI)f>5x`dpV73-=h$0c`8FYcOXdLDu*H z2pfypS?kUbR?S@una?fbSKVC_47kkddwHN3}+fwDk{4I zuR7Xw_jI8cxee4$u0^*G99*~`%Vu!pk+IP8t6d5*BExgpZRksl^4rZUFak(-;%fy- zv$||LAMJb2V8v7#h0Id=%vdd zi*t#id1d~Dxp)3V_p-}937lE1i*Jl)1Pl|q@bO!&OV5TGM{8OUrMb`O&^^|3u$-ea z!;evkA@N73V-wWEIV*6+h}RC7aG;_Szl=uF+&)nD^p6#D+i$%lffzSLdvN{2qq@U{ z)^N{%rIwWrr@QO0gzB8><#Gv0IpMc%J&tB;c_8Pcg2>$RjkOu7b*Vic(bHu z2|VdSfFP4E$#mh;sp^xRsZL)`H*cI0>{)i(Nv71NJ@4R|%qn&H#OZtq7!73rhM|5) zMp=kNyMv^6+vw;BOG=`s@TiuI$??zHq|Jbp>7Z}0HXj?QTf>sH=l3E$)whBr8T?Wv zSO_;SWF1rr2g>nHd0w$r9&l1^PMO49fDhCw%lajSHr7eij810#@?!UZ2f6MZsOe|=(q_6azxkn%cTs=7*vj;xSqE_6YhWN@3 ze7uAVFrSp$)tYdX@rYOTdj+>7w|>UPWA|4ze@^9hqmIpFEnokcjM}+(Cde}a(k2W= z=j}K;Mz}j61*q|6i8nY3F6q4Ig*jY7=HipLHA4#2i1{VjEr<_D*1<#K7;;5P(kD^K zZxFSjH=1?9mH(D?bOW*f=5g}Cl!d(lS z*AeA{qiA>x#|?OF{U9!BHXoGZIR#66lXJEdKF&BVu6s=>QHN#WJy3%UuCL2G?3nIm zTW{1T;~7Bw$(tBAfP_d&8sKg-Sx9v9&>y?e2l0n|%!^};aGl=g)3L~P#+G&54<+g* ze)n4g-Q%abr|dP(IB8q2rY#x}6Md=P$05Ylg};B^@XYO3;Je$?b7ZOs(!+A}KK1kf z3qf1#zTO4Q{wlP)`$1}o{BOBK^h}#6aKmF(aRlLQ*3=TcPYnivd5f3*mC3YrmK)ZG z_t9h8cBhsV-!6ZDwvQ5d|3zIV40{gyh`7Iuj;^O;2@c+%{QdW54CB0tEtu}y3QtH`Y#2+Aw5ttnexH$8Cxr9=H7o^wiqY#vVq(p7 zGRn0=%=3;~&;EPQ!bYjOjM~&S@-$N`=ZqUhyI%fbRU!DH5??Ze&v@AR5LpINOoZU) zI>B=%U%i0UhWNn=njbZ#U{0$Mv@|H!0<;>dsHkYq3esK?yWr)>P&{QR8>)Reh>O>D zJS+`HE!KVPfr0Oa`&eJ)$W;7K3m_l~Q1EowuCu0yD{GJ~_g^|;uH&>{;oUr`cFoWstmhlVyFLl>*SqqD(i!>@3Lwo(K<+%ez(+!>stDi}lv zp8USK2XgkpI8>-YsY*BFyOX%HSS2?8h|q5Zw^#4bh=OO>__L(dk6^W@?iKH2g3zyD zq={@pzA7kzyA&|CcLti|U9W+5m(;?p-A<#XP6D4q{@oZ7Yylh45(rpmU%j|I8(AC6_esa45j14~ z!vcnJaJ^-BL*Q%l5PXwd^nrMA!7YKgaqTHw2HeK%Ct~2zq=FN4tUupR`vV9<-F~(tQp7icn^l$Y@{Lal~W?;fK<|@V;0mgS2(GbrawCvRi71yTV2OF*s0Sqsyn@;qt09Ee!>J^^4l|KffBK zzYVyI_kf^H9i}RJyvk2Ialx=*XWKoYTg?B-tD7IBs_(A*-i)7`>UP&T%Xfnw-fHm4 zQspJacOtmp|5j!24xAOq{H_(}Fxo$+YuMqfqTxhnelB1{a!Jj;F#!ulrst@HDB`#y z3@%CQ7wzpa6mT^1bN#+q6|}WJx{I_+)I-QlT6o#VbH=tM4a*w_RhKfbTO00{(O`J@4pjfi-4web8C zn0iX%zoVTvby&_NzJky|D?Zoj9kXWPw|W8idt!4Bek%*Y&cAwlc8`IeB)A1|jIx(r z?aqWF#6Iw_Fix^?Tm#vl9v8fW4->F$011fu(VJ>2_h=mYy)uDws|Sr8G>~eZ{^ib+ z2q4YuzkJyjUqG*rNL!SrsqvOK1lKupa`)du%T7WKOaYSnr49Qt?uS{eHx-+i#`g;x zJ#ocBuRV~j8ryuXwkKbhZ6B4L30Wy0eP-NYr4Z{_QcybV-t>@zjM!x#ZGO^Ru8t+A ziy*UM*}1hWr>SDws_Do0w({AJ0hUqWs|yQ@!O2J3-9OB~Kjp~80CZ96)GRYP;(=bG z8Z#U)f&x8iL>4FQcQ2!9Of8Nbf{@&-h7%kma?PBiG?*Y$u1$i2t#-%dOfuswNgM-W z02;zr_F!?o3UWfPGeY*r?v{nE{gW(KrGubgzJ?RljS#YKQWI%jN1yq8<)n_GY}{B> zA|B`_svQIdK*jNoO*)C+*EaumCcb9-zygQ*aU*3YAk`pAGX?J-s|VpCc2{CUgHqJk zu-amb`wN=ml2VIR_F8~vb134mF3Z1XmtB9oEH3VVT}{WBi_|CbPD^d3f)T&Js#Wnu zUC@PsHiK6=mOvJ3y_6=7yYlg_{Yv0HU}KZI)%O=$zFdR35zE@(VbyKfSvf;`2I&g1 ztgs{i7f%X_O9w1{59&dAO1{o5d7wF2O5B|0;5rv28FG7h7^av`**BQ_xaa@YOmI>pm5?dqKaT z)lUShiA&EpJ)!zsD(6<=V|Gc$OcY<45WZ1;s$u zhdj%*w#SycFs@zo-;B`;v=4CEp7onG+k&ndXJ~itg~=JUU_?Jw{+0lTMKNUI)0tms zs(;5=|I2#t{BHjPA)OwanQ_7rkd|?x5yAXp*MDt~AlbzfAvnELp!dB!L2=mSM=;E( z*VSQ3n7u3G9Lp{Ze63921Rd)q!ST=m8)>p;A~Cg@<%~YOIa7U7%hRsAw4!Yi8~RK# zsv!t9BgrV3d<`5dk1o1}n~0}ZE{kvct0u@qX{L$^`Mc>^!JT)LB0s&+(E*! zLjq^Nw{u?Gg(kKP=&vevF#)8M5W>aS(i%)0+z+YDYG5k%1s@Zz5eQyVw|aItq8@$*uXO2m z?{stC0<_~7LyP3|B;-(AfuRyPPXv*1(Kmy2~8 ztnr@OvFtCXRnG=a&8%rwfz1T~6}o?`-Ld`i9Y&uZPMKMmNh^(`$0ex>qs`^1<_uLR zY6-axmT(gUvy7g)yvYXeR1_kLvd?oYJ*N?mzRh=-fb&w3nNxLp8og@pGSoc;5~62B za67;u&$_sGce}4KR;mb4P))(@h&o^P(&fqtT?_d2y!nsc*j*VnPN?Y)KH8IelrJH_ zO8C+i^gbGVxpN@)`^zVNkGf4~9%vRy%av#w8X8tM&fVnump^CW<6|lQ8$+Tnc_vL~y;SJW_Tjn=Na%jPvr{hFbG<~KxhQIVZvQ*v)gYM6 zGOOngsc{cRs@g#L%eOUWx9et@+0B{{uZNv~J7EK21O1&_&_~=QsS&!WwMgE7U9Hf0Dr`N*)K9;pDhbx*oy$QS0{T0KPN9G?q=+E2dzav!l zw0KtZm~+0i)kJIkZhW}|*>5{!a8Q4xqjuT#@9g}}D%S&r3$%0UtUOXtp8h{NUj?58 zo8ozr=LeLJf}wmg_D9@_js+H(5@UuNUcCt*lVzh~;(GufBLV~#*)5!d9yl;N;lM~9 zxU=9cM~;n$TwFVnLMKEDRFsKYOvkWibB-0I8d9K~!$)7BS*RwbcuOnBr6o%1^7MLy z%lP|J+IilB9*o3Exugi5A;SkGLN$9m3?(YS@xE|jj0x>Np+_2oF1mseTWI5a&vX|Y zLNjR1bxvH7v|{6DzVDol`n)dM^d)XkX<-l5ep$QR2-zg#e@VPZArmSjm-*EIr-s>o zVh_U%=Ta)|8Aiz@9TikSfT-(``cOu}J3OcKYz9}ti|z%wAcs2w)nycX>zzD_9%yNG z_#(fpVRXN{U_c821*~qA(z*#r5-M^@8ZUC81DFJ^P5a-iRRc!t-#ZYqe9ygPoIyLU zQ@ElvsRcdjomAesYa+(#z8FFw1@BDGi1@SXYvK?2w!eXsvT!01y+dnJ;8G|;P+S9K zcu^6!V?Q;)5Qcsj%lp=3_v+-HV&}Ihk7(VgB4!RQJ%js#vLik5Jz^}K5zD(fBfGLa zJkYBx4_A*A=qeK?#7(s+&6XBAjkm;1(PF_F%=#<_x$XIAhzFoiL(sZfB>f8^A^VU? zb+!vv8=R}1Ge@moJO%Iap*}Ug#2p1A3XlLVRh5wR?+cLvZ(LZ}Wcov0uFrZt*Lh~yyH&Hx4UOXjC9nt^*(bXNQfXpP?~&=# zHo8y?18bZhyDZ{bcBx`TeKlm-_#PzSpIrpl+SFZxp>L2{3uPS&g|}HKBNsaXv6h%A z$#-$Ezn!b|PkxVx^#;VQ#o(_xKlK+@9v_cMm=X(&IKX;B_|vQrFNSPx!u)i#W*+^22B+{(&|{?1{kAilVcC?K0rKA8+4T{nDns^q15OK2w&1mIh0XO7}A`+V(s zH)9KUn8l>25wWe%u(-olB+h`xH1y~NbgRp5)H&-K4OEu9 z6eWKv(eVqh>FC~9m)HoWH!r7%zROQ`#OGVkD4c+>SOJW6mHIt}e@GFTY*mnS344BJ zYgu142{(1O7}go-qE~=@Q;^3KUDk8^EI5c29Z@?+Kz1ZFQvcq1BXQ+JEQGS8 zmm*ld6O`sCZ>Ni`Abl4{k_%f8$_LK>zFm>p`j(hTNa2XNR4t?bFGIzcT6F2ewYJwH zwr}(OTGLTbH&KXj^BZ2<^9REH>#qniXJ5L0evfJSDrLx5TbQB(#f!Api*+CVmcd^e zNSjLCnkl*e(c*-yUiJ~K)pnP(UgWrZr90za3K6=xb+zX;!|fQ)nEOt)` zExFoj;ISLDq-gQYyn2LAI@{~f)+;7@ZZFYjt)~Faq8t1^A5UL-ow@!JfNq)tWxadv z&8rN#`M2@k@PI)&{k^~ncfTsrUzPS4H%z8Otac9MZV`z^1Z+O0k7hE-Y#h;{bH`68 zI;a*ErKh6rpiKFyZr?%|hkp011pJ7N!3!#(R^6-!!zt3Ua4IV}*$~92BXLtkS4S1z)Sb5q&EbkWnS4U@n=`s(NlLF(w!twz9-T>Un>KC z!@w{bN~#wTWHJ_s4^KtrSovl`q>9?Y~ptP#kgF_F$!gRY-u?vL;a^(0a zN{`AnIEM!-dhx4WrDCqVMKOpk+5766&1%VkqoM~$&CHL!FoPQA|0rob7pTIz1O>`( z4hAeU;l3?5imx_h`Zl_t`Ta}IQ*Z6jSu#Fz144=gAqHZ&#B=MGslMx3SMyE>+T;a{ zGjkd*_h=v2fz?Z^{h61VC3}?*hm^8$Wd}i=q_z%kuS#|Q@1`7Z`i66l zTn(BeS?FKgr~c#*^RzgMZ~gkD&2m!=%Q*O`DWKRR*{QS0~bCvBun5uPrb$99fvqHk;d+;6QZo7#LM9xpM_#xtz@bLdR-2d~y z*%-$;n9`=Zum6z0e=GFnqL#W>nog}#m6<3_OM}W@aW(sw#$@DE&h-7uy6Wocx+SwN zW>I-B3QG32^4OBOCCpAqyBZ!HnkwjX#YA@;8hT&Q=h8DLr%3j^ZQR2l6%<_6kT>g! zr+#bbIBfg+Pg0SPN{~bLPU=Pr*^EiOP-=O!i}NjbS;w(04t3?KUM8 zz0-|n)d8SOF_hV-a%RDZ^as1Vp0D~qLxQb9+s4iY7-FM;w;}Y*IT+By`EfSyx+(eY&;{=K|?Z&p%JsRP6F=X!K12@S|J2>-H64;l#by}vccH1W9YnCw{2V@4*%dmx&c zmi@Td3w|0g6Sbk|taw!M8+ z)cu5h`BI;w!-{P=s#_vfNV{j`>%wO&oN{W+U_uO z>YKDuoPiG63jt^9Z8a<&5WTUqNKd04#(2I+b{n}{j+;wk#k0V9Y>45S`Sv6yiyyiF_`Q|-0AFzB?G8!0GkH(Igk z6_4vt#6lp&yegHwzD<4ca|g?h@@qw>a2ud8qXCo&TKN1@=2w!QS@>g94|R zsqbUE#^H7()&D(uf9H)J2)NLqazO<^9FqYUuvLv$e!p_n<+lu50AHPa{QMII%0gWp z+PIBD9|M6znD%*e`N5WqnTN~0x>jd!r$n@oG^)WO3%3XqSBs-VljAn(4x~ZgWL41D z7MoGE{lvzagPW1uS^i;XRzn(^^4^{VDz$6CWYoWdaePQ2@4uQ_(sQl*Lx@E6w#+rP zrUAwdF8+%8mDqk64yo+h_*Ku+`txs>Ae0=yKoP%%vl!4lhzC6^Q0fXJo){;uVs)_Cs#)?*V%?1p$`g`LRVw5atu?)$KeJLso$*93t z%hG~~LS#up(q?J14>PtbZram z_kQnv?sK2z^L(Cz&`u)35NLmmuSPlEwi&w6>M!un;o=SHJgzHDqNm@%H_cq=v|a_U z(EOh9@?kD8M_Kgmu>^_pk(1JKnDYt*4));N&RaIq*~)ryjvE|CtK|q;GMs#e>q}0M z&SnD%>|@TSaZ2}y;+AP_ARS?aC0xLRCH1&f;4Bb(%ZO_DYz&Z+^X_k;L1HhdWXpjF zv17v|9Zz)~sKUw5YlhJ zKWY^?_h4?)CPt+HW;&G)v}K7u+on*&z?%~7dAG&l;^vYFsqRS6`z^V9AXBMk;PutT zDYwKRMk;QJ@b2nYzGOGg3)2tKoX(EZYb^Y(oza9(g547e3<;`vbT&41aly$s{uo08|m<*^8 z=qMo;?ur?q?}&Nb-`8gV8OXk)0W-33MCSpW^?4{?Lh@!{qZ z(x6*YGK}8vVR)7tfGyXzUy}v}O`_(&fUexU?%wX}OfE%-7!hVI`S8+Xyl@hOej&nY zY#)MrVt!yZZ3pyfRRRdRL;J+(G_jqARFb35$jHZyG?AV*1h&nM) zgQwNam$N_GUbPoK!(Zw&w4A|@Q}a0**8gVXX-gSosK9yGgozDj7})B7tAlBAS6PE8 z-BGm`9jRvry~!#(F2eWNdQ8bdbbX zG&@*4b+SC)?RE9o8k^BrW{4Q)eH19Ji&V&4mIQEYaXxd1U__g4tu_VR1#K1oS{U1j zZ!{JJm)$@2)OOHdtd-9M=daZRu&2`plLj5vz+x1#T#;JOw~-YZqGPCmx!KdzjBHRt z7Y;@$lhFn}0AFUb(X!br0oGw2wvLerx)lZ+MBKZ3$iYb$zIpqd2ZAzy4;NlC=a*M@ zPbLutaNHhs`oq$=fpq%#+fy#zrkSpl7UIwQ7~6yn@13 zaW%CQMfxdvJ0oX-6Y{5)CYl~ej9_2!|r*r8=x3J_Cwq2N_`0$PU+rYS_R^` z%mF5?{Xo?x4HkGb+J}QK7eD1*$5i&-kv#c{K}Iq5bSK42}=su_}0GZ^N$0A z-)nCf`7_45b{Gb%@AhCVhAj*(k?y+qwo`&&={acKqD>P~Y7jPoJ%>5q*y)Bm%X5ow zS!6yN1VYM}qUv7uk;wS8f$R_-;W|K(KP~p?%7C011?7Xlnai-=GIx;31P?0#!r@1& zH&o6RQUFH_tZJPP{myGuHcRfr2VV?MHG< zygq~>kJ=VGGSUI1T?ZtG6Ir)bQvt_WLlu=r;JuW1Df;Z}kreWTTPkqA=7LzI$=(u=)fYYfG7z|6Cm2mlw<#?K(hQ&j z!Xq>!T%d+gVp>eFCXLnWfwa^}FG@@iU9AwuWUSu1e?7v@_lha~Tmh9zlYA}fF#GWK$CIx=`S7|_K@oJV*P$b4Ug*`+aZptAcGYUEbJVh> zusLwtyHBvXLjKKV8kU;V9|CB*VUO@&1FO(clkFDp?VlP$8?PNh5WBpI7a~wHP^b&q zmEP~GV&g4uH{usXV<+1`ge1B)V?PE;1c~C3Flc^|Uw(GJ0wG%DVUnL7pl?7Ru5)GJ z2-f{fWgvPv=?KTO{obe8kmTo`?+2&<&|KU*EL4)87vL{yy(u>iyHsb{?J)=R3kGYlMu+ zh6>14Qroat;fKJp*%raBANwGTitOtUhz5@Vx$OqKGK$lSVjV1z2 zm~qgsr<3Cvj3#wkA8z;S@OKeli1Zov z)?=Wcsx_+k?5qrtIxJl#sKPG3i&WLQ(rI952{AONUuYdhbDJXt1mG=D&ip+cnWJ7ZeP&C2I>fvAiG;=-|LIYm0C zHE|R9LqV`&EI~e)3<-j@m~V%}i@mq)Y;~hTK}%(0;qUZ>1Zn5gfvP1Ew3qk)nVS0i z?%nHBpCNO?boY5Dc=3L}PKXqp`3SWPubgDn^gDa!MoRC-F7N%?Fe{a;iHDw1<&gPT zH>nDtLTy12;H!RIhE@6Mbx-HRlw!~l0EW+%KoLi%hwOtZJ2ZIaWkr=Aga;5eU!y=< zOdz`XoR6GeAxCQs2=uAsZo!RJAz_yz-o)SA`(;~hZ{bs=m`W&@NP9<`81D4;hB4~E zc6j5FwOwSxK{%)fmK!bog}`Hhl2F0tkQnE{@YE7!DuR}P^;sU0BI2K__Z=n*g6hi$ zg9B(Pze$gZJ$VGw^mUjYDxP3o3X60pkkdSvE;Ql4TG$$LK%z)<034$pe69@WBDV?F z?`*c{d6VlrH6p{`?&hzF#;y@RX28OgAG#$lral^}rg`;9^uz3n$AZ4M0{)BPB>6qJ zqEQvXHlT(zr|&4p$_P;`_<4owNxB_nNJZ%JAHX9^Zh%C}V2bO>2 z`tx$X?Tz^nMprI*O79J1G4h&vm_!&Tw;*1(Kaz?}dM@1royf zFBX%q-%TNB(^d>cG&j+(5o&}b8yb&(dAscYGJ*K{J}d2wiJPQdO{vBl1J_gFL;j}J ze;RIIJSI+1AVPd5Mp zzR7yqh)>ew2$P1^(hvyXxI*%geaAgphGW&&=aAC>Q9 zU!_Ab1Y~vZFs9S^e%`us8-U8RIR%?^s|bXo(lkZ=SO*B728`&1 zD=dcVhvfsu=I`Hydft29{Cp+^t6rmQ>!%tkjpC2 zT(gWxH*yz!22N%lKsBNfF2XKg?l{De+aJMk*?jjdZI#?1^u2f^_})i%9$jB3cyPIt z!#%(-gD_4GFC3)cfu__0y{Ie-646P$jw5oX+}$hv=Q6+qXo2|FS8d1^kAUNT@?qY0 zi`?L`8!DCj{IaJBCp~~SI%^yM{$D5aKZAe^553(qVv&{y;K@@8W z?ga&)ie!i&pzw|gG)uAtzHF;f*rYXyZOKD(NCa(azKhdwFM@+-kju0{MoNvvofy~3 z2*4Tc3?1n=4WKM8Tvmp_GYNORoVxF)*RqgeiQe#Fdi$UI>dN)~Sjo)5F^<#z0$LO( zP7V-k#=>R9!HUmWQLWIWsuv&bA(Mqc{=;Pq+=Ll`{8ob<{kSgm?4(s3)T+6D;>Jls zFHs5bs`DE@gIbM+CgfrDD3=Ml2jkk@OH44RLLLzfpFv;9 zx85EaD}?lH99VGXkhhkEWJdzfkQ+c(bOoDYV+6wj25K6VMZCre`5lAB~5pE)}Gv?GQFtKiq8*b+7MeXh^TdIHIAT8S2j6X z&QEK9hiUek)2wX%rcc42Ug@`vm9I`|$(PlR3EH`T4o|lMt1p>nQ$8DydiNKwnO-Nv z#8_m4EMEkeuw5&Z1-KOj8yIJY>LLgZT#_-+cVkuq5+d9At?oi>>0#$qDb+VtTyuT_ z0qFU2nw2wcOX;{b!RMg@b&)VSh6Z}jaW{S{etenb?dss*@D|Kv;k66h2G}Q6xJF#Z zfC&CZo=wT8EvY0=e6nzjNW-_|WXfo(TOnuI)L%lLVN}Hh@%as95vn8jx@Z}Q zRXi}Zri{5hAGnb2skC*jwRa&U)=1 z$3-@2;Pss?8s98<9{spqI=Wz2_l^K(@BXj6a}LP|;Hi&^_y1Zg#~9)bdhbpNm2GcX zevAfd*C9sl6YdC81&H;$m#2~K2PB}5uQH4LuBv<)FHd2Un3I$^87kK?+G=Sv(Qx+I zJ0-XBQ`OL%OC0hA*~72PEaq!Sp^yv@Ju_Q;Uk=Dlsp(6wY0rFg6?X0RF>v$WQaS+O z@3`67Ge~Y{hjs?g0XbGR(fZMxlhHFlPlKv-@sI2sg!r$r|FHdeGj)A&i)&t^VCa^( zPJv#L1U;2@2Ef=iN|3t;0{eL68#?hInYxfweR~=lq?L()M!=pKeV8~Yt7x6MZO*F2 zL+#@I6ZIKNwMpMCggzfmJu8LmgFe7^3#?`VSBi!*qZ%hZrNlEg7FLNi)7oa?UNiG7 z?cosGXdeiK#9@YtXGZ|nu1m>)#-Lma_Xy8B)onLC$HYy46F`zZZFW3NA(4 zp_ddBVGB_J2(FDI+orDq0l4m28$jk2xioJFtmk@&(Cu&A#%@Eyz8Y%E({7#6iW`G0 z>u!XMJ_#JzjTLfg)U990C*OxkgX0ByOS|%c$7%VP6vmdJhR+b(YfRz6sz2X6mryyY zJNfC2d=vC#ZtGWD!NFvb*@R1eUkNGFM0NUjgzFNReqaEXx)fp6yw8TR8VdXJiGX_B zAip)3 zaT#`O9QNLp9p1YkD}em>KSj>ID0GIdbLIf}M>jr8>Zgq<9eA)G95**Xmn^7jMmW9} z=+|)oHIryPgN1$*@eb9O5<{+{(q@1;$b_%mphscvljiLGGv4o$)~&+|;Cj)|V2jHk zvv@vk+`QF;smFVQgtA|zuK%n&s@ z6LFBC<1r`j($e}UH%?$i?=be!FYZ^1%uF4TLyP^F{_4bc1MrD_5qy9|4>@QAbH(!3 zKC=n@>bdS2l-oNa3Qe|DnpfyV19N;AATXgr?0LfWJtf)u{lE30QU%#48`!6t@2 zL|*;88-`Il0Q$1uKLe8f1we{xcckPV{l3X$t96^-@%wsUy_=B2l$aAZFnV6fY;zdMK{l)b*NeoVaYlBqlsUt zPwWxY_Vw+(u_y1&sm5H3pZBXi+%0I&ojE>`QebO3eDzsH*6Z_3nY1ll@7`^`cb;|e zf&!>pGP;22<-Gq>z?<(=Q3;*G1>M{U?~crs*(VHl2>habx#BL(JbuSk5`|#@-1xs% z;|H$WjM1}=tWBnho?hnE`%Eh>AdCqiXIm*U&*5+8GybffBpWHGx0zTEIFl zv6J|gGcHh`b*@)+(IP>85yX~9^O-Uaw5~H($|@1Q2Z(cdhcQW{7-xyC=BMicx;FOy z$X_2qwUDDwby=Y$d6vO{I&&Fsnx=x*2{Mn-)c?zN`exZ$K2F95tH|qvuKYH+G)(vVk-~VeP6|M_HTOd z>Fzq!I=66DbqT`cUJWg2eGVRV{Tqj0ciQjiKF7pJsp~DU0r;F*&(PNPcTPR(pkaIB zuo_>6*qY)d?e>L;4YSd!&&O;}_{(yN)rF!vGm3gy1byILNc*X*5xEx=*@Ibf->1mb zqAqEbFZKIb+-(gmz*Wjj%k*5hG8ZCQbd>TM)akfzdiwAS3wmR~F@{-NE1T5QK=QMn z)C9$o9;Vcy!(E25=b-M~DEe>n$`55=jOe(E!N`u*L$;JbTecfyJN!YCet8{Fac->t zMjBRZ#zFVQUz5f|=(_G4vu?kaOQuB(RynQO-A&diq+jMDJ8y8cJIS-6yev$D~T=a34B~RI> zcZD-$tQDAjGLx^>r6G!V6-5#M)q=s8(I?3W?v5&1*8s!kF|6&~bA8`liH6qQzCUUG z-~sdEa#i=W(#1J0nxWKK2<;PE?DAj#`I10R%xG3CTMTh6M7b0I3a~F0o7E7bm6?fm zQ?5t>b{EI};cum(KewC`uy!qcyY3b6b|R9&74WQ$I1ib}bYJL2DwAByeiEF5MJEFh zz_`q1oknms=m4gm-7f=$NKEsmN1f;f_XLXVPhC4~9J z%J_x1gbMDH#M8JTwr8{<-wj0G_F(4dD1?%KEeKJMooq|v-{y(7q1y?RA~z7&by>8; zU_+_lCQfGk7raH51MFP)(cMM!V`1Nl3Q)}`KI+-on?ne;*Eg=D*`CHxO?u89D1Hh@ zFxIT{37Lv?OgikUA%$n)4^ zGa+bIQtzhip07)}yz`?_2sLQHa9=wVVdOqB)%@JEg)rw^tF+ zec&Erz%vEBNvcC_#W|~HUS2&nh#6CjlTiVJC&{{bgwXHZB7w*%;g9CRqq zO+MB!XXA0kwJp{Lq_FOd!!#@-Ui^QZEPWw5?mpUn*&ze!TpF&Y4a=_2f8xJJ5ECad zgE^&wl>9>A*aPg+c|vcv-rxWwHZElI<4`@LZSfCdv9ltB*Va|~EWTioNZ_c~)s3^UW_~JHI!DQ%8@BCAp;#Ex}?z=Kg z=07qW35ydb-}BNe)g&gQe6 z7Q0RuU`*7nx-s=kD7{ z)ue^K$^fy}*LN*IT^AO%LNVF*6D?A6@-n0kF9P*a9WYFtE2Tzbg>p&rYG}c2(7i24 z++Y28YFe;$#3FV&fORfnr;EA*Y++;crH{!}1!AVbBH>_RP2|hpNty1paFhCHmU2{r zp*&cWuE5(9O3;3qa(C(Vj1VvzDTfPBtf%8n54#F2*2F|>_Td<;>y{=HUxEA`6z*yF zGKE*A6_)LE18SfoPM6YAp`OLN_%ngkuwy)IzW2YA=(vN|aLpsKTX^I)d@T8FYDHmJ z5R#==KT7KP?oP)16xu2)JM9FlEzOb@+BQ=s<-{HBCcy``VM@SZ?wTMEP| zxyaTwju3BxY_O`nae_xnGQ|lQ0o><b6hP!_RsC z70iSDJV>^9|I8i;`hg`z zQ1ik*25o3^6`r$eH|wO|M(iS)0zWDmyCk`rj5q%_Ib>9l;eE$o$#eXbJfLu&UBoH83squr5+Ce4eb2U!FSlgNC(B zSb_AL-4_;Dvp-S3FTU_>y?ed}f+bTfC~G;jNN(ei-=#Npz~ z_6xMC&HRjTA-|ckKeehcC(oQ4wA+J;jzU;*60|Jlo5cuQ@E`@l+H)t}L0c0gyw>yhHk}b0>4g8^YoA7-w{psVc0C|0i zV6aB<3bgX_tq6#_+2uL0P=WB?gLkaH=#I$~`As_7zjs%auRDB+h`9Muv`0~h|97j7 z83Q_jV}?2k(ox(MSVZtpArcRzyChT1k)jJ)bxlnPD9g2GgI!7i^SMhD1WD_x4quUk z*G>SaCj(Vr1K55$CHBVCHLHVD!}IqvZD@D6aHUJviOOiVN7+EZSKnC` zheb?>s0OR3_;*c$;}<4_@~Lb95sA!0+8~tk|HJda#=#wpy1W5j-FgHZ5z{)gN~9D1 z#p%PIPP4rIgm2JMwGVzHg2|h+b^RKn=J?u)IfDAc&SifIE@nD{sH*+?JNNEWZz2t; z@_D6?$Q*C%(r8%7M{g`lgTBD1JNTWmq>_9nrm%yCi&}R~`T)PH)@3wNiGopC2Pz+g za+n1nK!p(`v>La_&DUxOZS~7HPF6L!#JkSZF}O`sG3lodgr%hDr0ZeJR=(Lj4i#^Q zHR-ry0wGO>EsOT{OTW@0y$$eH#4zlq0en~EPA_w9_D&9W;t*$)_mfY{l* z@;#*5RyVdQdvz00a1A&vpSzpM$`YVgs;hUd6mO(r^W${XdJ$SZc97f#ePdA*JnJGC z{*u&JKOmnVzt+}=)^J&Q$E&+;wHo_9Vc+8 zCV~baB1=GgJ2FWIUPvj!lO%)9M&G0>;BxMN5z6kjWUyZm5Yd%38!&x|2J zsv3P0fcPWnc=xErKc_IU5>+L73dE{?1HA42xf&_T;jMteomRQJ$TH!?l;UtSrP%4X!Wyke$PXK&6@l zDV=yQD%u23*sPIZmaeBq0ZbDEuhQzF45_>rNE8A=ZH_}6QNf>k^JqW0DgA6TQq9PrN0@8>y3>`{|NJ+OK9n$bF z&OYbtz0Wz{-yg3ZK!u65p68D1zOHK#rKO=tfO``c0)Y_Ry`!W9fnbJ1AZU}=Sl~N> zcQrJ?Uuf<+s)~@(0opb29~_rE#_kXZk=6AlT7g{tV+e#1a#u+~&&PZt6DK2Sn4v3M z%B1(x_JdE(ccOdf@7>k@alx&a_^jah@32pI-i5K=3oCf`@tIcSC!M=Lj+$)KE@rO$ z2O7QAQkMs&js~VueFs)w3htv;Wmf%WKKL~s9m#g1iI5PZ{ofzK(#kD~`L;!$SFE6o zBWAunmudCbkwO3aX>tF1 z^?&T@|Nl$LGm>cW_%oJ-cl~wC&tro+%AIcf-&Yuoq{kYAHGF?O#J>K&uhd_@126Xb zf9`J&2KFaiJ(pJc|JvUj{9p8c-tgccww%mfsMlj*fxoS=XC5rc{TTY>_%93i&%4O{ zTpJod(O&5K`KQ}YpT9l-&v!tBv_hn3zTmXjT#(X&$E0H)>*G%ft9Y{f*USA8CeP1s zon^)&)#C`K7jrPeGYJ3Cw@M zSJx*CmeyeGAw^kF50U6j^MrF0VQE~lv@I&4N#j}OnBWpsW5Bs1MnIqnDu8Gv`|1(> z_xmW%j26W^jq}G1ktNRAXd-X%hOE?aKf6)e>FjayVmF&k<(?Y4i&T)*)Gir~e=SOb zB3N1-yqG3`GdutPzTOimLFxT*kR8S#O30`moHRl0a-#5N7JkcGr>~?EhSD4qO4?q} z_J8H+$52RRSY4v20;GSMgk-gm7Grq_7eWI0ffjkVCr!r=W&VFBK=1~mRu3Ic#g7+_ zv5ApKZo~)1xy5lmOVv06+IsmsQ?Qq0!2A0;_lsEnk1Gqp3>yPV-rfu`T8IyYr76JA zrGvI0e917vXYc#o!XLS#n<(7JpN!Vc{GW9Lq`c4FAQDmws5owpE*3M>&&m~O0Y=Va ztbu(uBS|%kJuHw@jTHlF@Be-;{#Ar9ZUzfLh9Oh9bL>a#9)3+QMl%BvNW(PZnAe+c zR~~|6o?&;zRA^q6{o!e!4%mrtv!-RuZA@yp!@m2m}#AP+x>T32T#-r-Gu=FF%rr9VstL{CsW@>u3GD|6Jj*TF~}N z$h1T&1NtNU9EqSqi0OxeKISvyuV!V;9@o{k`^T7+#f%P2u+E-h~C!5H2`u{Z_N@88Ar>(3u-slgV}AWd5Dno zC}8NpGeRlv-=_Gl?OoS}VADH60fn?dL=a&}IYSE%r1`-}dolYKX3i@UY-e{vyjg;| zoOdpKjby^_dDuW&y|49z|79(IebHb&Y;vrxxbjjEF$kwMop%qN(C$n9yNxjUPuS5x z*vA_3h;i@m^}o~!Hwc=|v!x*AZ^Z86Xcw=RcQC*2D9d9&gCSR3%ls;;Zwo^jJCUi1E<&G)Lm$CUhU;yYVm(9NbK z@cb#V`X}_vVBryzCb|^CQ+I+8wOa(}?u*}{2o0&xf!N4s@zDneamLrTL;t=VDQpaM zPRuz{ND>2Eqm z^^_UKNT0)gZx`OfKTCEV_$hOA!)oc0dL>p$J(v7nfw3cnmhjzSnKC^WqKBac3F;>3 zgoCw-R9Ob|JU5yVm?hy{WRt%bJ8ygr!1 z%He=qW|@(Y1+#`5>#H+O4K7qrwlLXma*mflT}F9hEYNmdAFlG4J}|q7A*c#!wEL9D zYw)T;#Mk>%yaV=E_LS<6Ed{qQ=h$&V?PY{-l9VSwip*~D`WcP1=|Vanmgm9wyZph# zkTG;4=o}G59a8>6Tx!WBC(;Vn^UKU@$VzQ00cOiFwx*4MH5=zB2l^vxeN#DVMpxSE z8KrfeBa-K^0e|~K>L~dva5Fb%mtVtv^IIJ9{Uvf3p~mt-3m>c4L@eC8Rd=YYnRV=6 zwT~H7bb=~1`l82J;QKxw2{s~SBfPFCl%Zll#Maoik1E>jRK%8WzgA&OF5!m2(2I5B ze)Tajv@)DK_~C2-UgP~4Rl+1zfX&^Y77 zNxjK0V5(~w%bdHh*R~5fiB*z_DoIZ#Ml1{!%77@E(3snJa0h=KZFd@xUszQ=+)aeG^Asv=ELsv(hbV?SButY9#~(=TPXP{Ejy=&#+#>3bc*#SiuvrpGa5vaG8{J zyD!?_d2m0^nT`co4KkyeB43gWAj&6Duju(n=Vm$!|IWbXE62E-B5ip5jX`Q+k(Ac| zvo-uP9(kDLx7+)dU=dnhNSkJySi9Yc&{H-CU;zWY_pGJZl`9_5$_GL;jdgq=L3GCA zSOcNy10`tXJzuEfkKEjrR8j*tf{Lc8`-jZ4^4paddqGIpN$fRxD!lJmDmfg8pZIYr z+__;>)YP?;!e=Gbu1}Adg=Kf6a&RG(NVcWUeN`(*K3Ffm)?-aa`ef)7S$N!oy?Y_xiUQXEV3Y8qfSJjI8e=JH9oS2n5YS`YO5dgqXusA&=~A$nWEy zd1roaAOcX?KPX=pRMU^yL7??_(KvVP%(uT}|2^$JJOA3mskPLsL95QqMweo$IWW*+ zvYMbjU99`v-Ip#aeblN7Y;96*h}J#tUBsfqpa?DDoV6jM z6J8Wf!InsUD#>vlRBSsOTQh(lTP!d@$`ITX7SL~VxIswU@^dJ+!fW$^E)Mc|$BJzG zYmKwTC+AuE#jZFZ9iNlE9>eMd`bCsj_%KM98uDzj=6JW8 zLB08$zUXDP-yy49!E9qA|DRP0blHOYm9sEUpM;jC95$#rmNMi!z&YZ&e>94Z8+N5&lJE4iq zlQqt&AiK+*rX__P#=q)a?KOV1=|!}M6BHO#FdHSR&HEkv)Mg3x<-ru}6yOdEJYC}u z%KD(2f78l;p(6_PLZ-|gKAh$LPNeO!EP6LCnHU3C8A{jn|CKq6W28AqM=SjcjqAX^9u}VZGtJ{xR9iz79pOBd#&qH);aj^ zk?$jyX&6>6oX~d)lQ#W&!q*Ms!$|PR2dA{Q*u4YzZMf2V=dq*l`m?VRiqdB;POh>t zwM2z^nkS~uzHT+6vW2PxKLm=Flu$9yn0#G!b1QZr?~y9Of7o?jfNUZifBCyX*u}>u zsU*Zx$EDC98HkbZJ|rmVvC_Tg)4UK{W1TY+$m++y6A>uMolGzjY!L!{O{?DG1V>tR z?bv1ehZ~eUVB+HXf<4WTI&RZD`PkXnX|=!7|LA)`;z_;TzJvk31!uTwEX=Af_*S^F zjI_^=CC`ITJ$iUozbBn-ha0GcY%OOReeWVt#!^J%;FeK!9hJ8IQ0_Dt>h|rXBlE&V z9K1z)Qik-7@&t(I-?JUb-FtG~r6zYY=a1dpn#8HnvxFtwsmbMK% z!!RIW;S^_WotN*(%SN3nl~^^ID63}h-ejPnS#qnBHji;7O@|zym5;t?rzYtjZ)>C! zL*+bl}Vk;-{8n@+pETP>A49cX6gi>G zc|~|%;X*He^Hkp%sO(D9^`5(Xz5I(MH8X-0q{SMj#dsi;p8=vGNPhGXOXvx|IO~C0BI$d%ZHd!30+F)XyprCq z@PTK?&kK669WIwu$-2GQM82+!j-a8%ce&BNZgZL%k6dpTkLyKIdf5(#&6S4`KeYt+wv)Oy_>7 zd%I+yQ*1KYfutUW4xmYj8*YP%MU|}F1Yd~8XT03%t{RL)yX1igk5M@!Q_@o~y+!-y z$D~`jSbYqr!%>rd70;>KQlkol%I|82iM>Wn9D0ieFGq6T2jupn#qiaAS_4iYinhCGvFaZrlgIWV z9~p2Y|8`(iow)y*=5=#RX}o_#qs#5|tr9wgR&U8noHrFiL-7|^4&C1ODHnQV^TZ)G z+~%?AlOFP6Tq4A;<(j!U#l`tV+1HE2unu`@4{I385@O8e? zzxxeSjEOAsRS{;MR;Tk8aR4^~b&ATHy(l_cmg_a=^ME>j5@QXHy#Z{)k z1RaF<;4Bo%2$e_=%0!HtkdoI$5HX0-^uo8th8GqW)t8j!+ro4=qLaE4WPZb*9{fx^ z@JBgW1-K@0>9=sr4(Ds87tTX5aZ{{QZuP1EN|EWK(!5pmy=!q3sUxF-7`J=2{`WZ; zni0fNl-&NVPwoDrJpvr}h;+!$C!GooRb2LR)m)LcUis2?3ME*7LN9uJD>4)k!Oht- zkB`&ayu9$>aD^ZRRp8}fWF#eSHov>O8?UQMWPNfzF@AL9#mXumdpt`?OB-@e#6H(I z`An-=j~W%|#?(XN`!m~7uIbdH2FZ~?TPw=MC)%<0u<;p52Q8j8UCN+pu)1+ynL7^! z+D_a=)2I(MQLBTc!D$}FO9gZ3W(@nV;vqUL7iQxQl_37Qa1Q7{8u2usAweU47y%pL z6zEdu%a9;r@IOeLcvotvFsS>0dh=FHLNaUsF;}1QHw3?g4b8eYCu#7oox%Wfml2YU zW|`EB;k~4&dX(pKQcRtCZzgAMu0Us*yIBlzd34skx4Mm$6E4Km>biamKi?Z!9p9Ve z7pJ1O@|`9(5~t;anPYpFJ&En=fAIFsXB})p!nc?+6}}_Uhm|Ta0lFQZt=Wnl5DELy zl?x2@=;N3BR&vR7Cc4^>O1EcpfzGB8&a&n~Kiv~ubavTSF!N_%x971H zgB8PEdSx=Ta?R1nIbmD4xHwUokueh|L$8K@83amR#TWEchLJ6h8Oijajv2aaHZZkD z=-yOdw0L-r?5~?LYSFynw?O3%uQ|AFP1W5@Oib*|(6DpS($Z4&@j3pmv$I170>^?u z=IH^8x5{3as53Dyg-uX4uh5e4#6u^^0%0Mo7~5vxUUP#hKZ;+=id2 zPO{OCSG?DDQ1b||Q*2xnT~YCiZ8fsRnWr_;r=g_<h ztl&B*!c2p4Lcn>!fn0~C=+Q=KrE=j~{!TEx;OMPJFJ zGNWchIeQX6t=>auSCDC}HZx1G^57?%`O}p;e8Xq!HGB5_t5$M5iN}-fyjc{|zr{aU zHAC89Y)$ws$qlqAY|Dsd#)k^c1YCLrI~uzSoxKUP5+5Hz? zXUz$8@MQQaD?nnICD7?YQ$K~o#J!Zc+*g;QG-qxnrpPh-LK`Z%`R$F^$wZB(a0ok7EsC9Lk zu3fH0V!+ewe7+k)H=ONP;N@^2K_A#c+J5zrf?TRhw*t|LIy>6VmVW&0T>(dyPn(1J zJ{UKz5!&(Ln8i@J)$K}GUmqWFFfUj59XYuSrmMu=Onr14HD;48?d#^y9B`p{Kbh}h zJds5;?}vnn)Y&)WMKiciUNaTz_d#Foy&geM64u*^*^)qC0t zHOI;>yjs@UsL$yrH)9=1_%nX4hbT>X=r1)uCDDS7<19W$i8 zSUd6Ozdk4Sa2TdVX9y&I!=&~eqLC>PLrF)k;M00;BzPo3j;lGa{(c z?M7SC9!wgW{L@X2Ypf1qDAA5Z)zkiF!1)|KD!I1;Q45yH0{&zhfj*yDREE0gzB?PQ$?qQiI>$H)qcU$Hk~_DQ z{p=4Y4VT3@WIfLD@nXXD$WP9x1v#2>Cki-OrfR&pJX`VmU?z7PGAwC?7#DrDeg{K` zHRp-#H0FbV(X%1?Dg_QppOaLN2PB+NvKWv z>2CdW%6w-hiw+D3m2RS*GZ-gLx ze|&nJ2V1ZIRl3MhF^H+NT)vWia_Db6ColY^1yo_bi^W{@Ypy*pNUmcC6fEt04LUH7 zg`V7xjE`4I6|_pA z&md$@Z(A605)cC?y={&pm@PApF)oW!%uLho|A3>xL5Vz&P-#AAM=UI)awl?)g1)_w z*D%Q<%g&x3Gmk|WVZ8~afsLH6n_u6APMF41w5K}qI4rDqFa#XGA?b^w5%a1uE#*dM zbvN4YZ#zta4Qyy84gfL_8=E|XnDk#>0O%!gyO3?4z({lP)O59DdSdxCd^1lpfM7C3^DX~OofN^87pd8?{D4XV z(}~b3k9p(xx%D6nuR4m11JMylqJp3Qe_ke8GVJsGI6}|Ixc7yd(*h;!ggUVmcKq=jaNyI;m#4oPKl_R1 z<+j+^z$Y`kXC5!GKN&54+~IW3B3sh4kOZX~PYa*ux;o!aAZhnMJ%A^*kqGH!(RTH~1Z{9D01BX(I?`hD3`+C>XL zn%oD-zT(tZ*aF| zBNnEyd3qi(U2Pi*t9Il8rRndC;#Zu9M))^w+?Wryzlk{QZGXDES^uY- z)sB;CN1>aOH8mIXmQ3GYYRW$T-LW>^a5UN;K{Qr%!EEkT@W;jQ^L1YbK8D~H);Udh zUnAR>AAmDO3}(q3ucSM_=n#<8e_wuazCYN%DAx4!P|%eG9SdK;xAoUpnb=4(dHbtA z@)nRCh?6w?(pJDVX@7ip_h569${BUSe$79F3Y1znzucRgjxyqH^gr`r3D<471bu$f zgfq(O%;*|o{!4=b|LD2E5Cq~69qrAVA$N1LTQ_S=?F&Q5NJ-0md%dR4b}E04R#2SE ze4{(r3!}cJraiSAoL|9PDlkyZfM)n8fFy+~Ud$}RU{dCXX5s?z{ndbU`u0DSC#pmNV2BX1t~G7H+^V&$N$(8bQnAHMr5)M))&3smMlXl2cp z^--yhW*Q~HBO<`+@_qd$M^*mW$o?8FdyruKsQI}ZMsltWSk$e%aMC{FfWTe1Mu9bT zHP{e?w{R2FUD#EVJO7WFF(SM5D*5mIGC^}%;sf-z-_$VN);ch5S?kkB)BibS$*+~TxZsWGMf4mCvYi>S*q04*Lzq|iC3niba_zB>lLmg2T zx6)6z_0vmEYzMC=1!R7a6bPYWHp?2=K+n&U4~JXR4gF4~u^Dq5Gg)0_-CbR24)aUA zRb|`L4ODFRUdaQr^)@%RpC5NF^ajxapcMuD2UCSOKHN+7&-1*9?PD3SW(#+0EztPu z)|D6&hsd4@ywwZud$ z&_c2zFlxSdzPRX5xO}aWZZXbP!SXhE$<2xJY~xiyw~RZtiS6(%3g{#oj8opeA^FDe zh>-nBNZaw(MC;lnGyaU2IMs(mXpu$!O>E(2*pk&Z!d9d)Rr~7p7g*iuVSJ{wJ42qo zMw5QjIA{B6_VoAgleafI^2NTob9wav$7*nMg`{0<#)m~Ef!+~8P9y5zvjMQi`wN%* z9}&ls4==JfB1L95j4h(9-xznPFsFSyn!`bt1>TsaS%_py;5d)M#-_K>)-2>6-Fav`$=1yi}TrQ*!e;G z-9%)*Mz->#lV*D-)o7Sr@{L1BVY0? zxh1|zh~+IaxD#Vftq|BrX@8qMK*+sQYq5u$zH?z}Jo(}x{KO@7EeZ}Kl5?pnm@ws$ zXFcA;ZOpCo#AA>e8>Cx*atoXPg1=BUsIX zX!YIP_oW64{6gJ+X%52M0{fTgHR%C3*Ns^=U@Q19UH=~|6c0oR#NPZ)<+#;I@T$YX zr1~QCjNXr4%ybySPw>mmEn;FIBd+5jfuL*KPjg`-bXYCSCAch)>7Z;Razh*!hic?r z$Eq9i!C(^S&Iz|mNB0sg+!QdiBg1*>4Znf>sSt)sK}=1(rvFMwWs4&LMo2`|4chB2 z$ggWue{lkWdCc}51AoGy9$q%bGnv&VsEJB2lbX*o0S}fEqX5u^&b-Z7feAxQ%g*Lw&tKKRKV7jrnq04b z2aD=Asm`^}W)e4FWLFZ|B-qlSs<&aoMZz(M#5|NY^u5vImFj3cJgC~J@D;T>qD+~> z!N@FA?>JeVFpvVz^?AvzuGX~!8h>m5(#8|=cC&g9G@6U zSox_{l|cIH(UnKpf3_ESct(|BpNSzJE@2$td>>QZp>FS;tQkr>+ZDwp#EVHth;#Jn zZtMJ#^qa-bllRX-rS7?WJtC#9)zHlK)=cJUNK~R_xQm(tkEDz^1aJ(kt9W^DJwA|P zsUsDkwtbhxFYWvzhk4IfPzDH+qQ$All@~&pwdS}Z;h%QsAqgv=_Z}%qUY;9e=UF}Yr9x?4KHMXg4+)n zo$w;rmiqPw#As?(_=8w~_!^?T!V7VG_h|B;)+Fqdb?xYR_E0Q4FJ2FGNsQ6H6iO1` zb07OCt7)!3Cfh60xk(j&z9K0_aNEtGt5NfAN%k8z4_gbh@`sJ*6GV#29kwR13EeEe zr3613U6RDOaiew3V`0!y%Eh_N&Sn45&}yLv4IA%1a1+MHw)J~YCy=c!xUMAD%ySvB zx?We^@(ulGPG=;EnHKVjCqJU}+ch{8FHZ>OV^mmRe4lEM0>0ZLWe@|IQevleND+1|yW>g!@o_H}-{9OQ@MuGZVf$*6G zczS*OX(GNy-an!G9M;I2>umz-7E*6T7LtD)<;*@Xc8lsCmLFaw_=W{)-Rw_1?FJ8i zh=3_z;!?ytUFm?{!XkX5^On(NKNnKi zkCe-hD}j;Q+h=!mcJ2gTwqV+bH`M=>r8*!+$%BuBHb3F*&IT&7Xa-xG_YAF-$)zPM z=h*1`i4$q5^AvWH{fNSyM+Nq0;UX2fSzBq_Qxn6|QmRq&%*xRoWu|p|y)>>X2co^$&eMAZcX7_G@bXO+4g7LlH{d502>?8C4*uMhw zbFaOd!-xyk>c&_*fEv+Mr0ti>MKgao>st7rmj%m%53{A?Y*4X`DR?=;WDFs<6-|}z zy>^uqoi`in6yf(44_Gk8W7zC>t`aDnMe0D_M} zh)0OyNzP|LN|5$R?a{Ek<++)mK2C|LGy_@iH7h<|VbAcx!;dy|yAFL&=7hLS@%Fq& z9kD0h&6+BI;%<$MXTzAaxa6G^s$#n@C|7>!La zqtwz6s=*)^t2z6W`jv^@9v|1^tXGeVL3FQ@Q!FJi=k7Zf0h5SQfIF~}6!9B0ra z8lJjbR5pf&WGOQ`Ry3eUsp!LW@jRy-qJu;~%u)VFuLZ$x+ zRXQx{*)t5}Za{J2Hxsg=a5KJVnX*Aon;WCp>FVF=?IN%V zaog%5g-S&%_+62|SMH0u@4+&5cX9ZVxo@^WA%lYTFnJCL8JfT;LORHB$~vJO!xh3i z*nIngLCKGoBqSs(U>Y}j_w;bxa#&Cjl6Kb*e?M3#2KA@39hRq6=`kxgK;5ceS)*vap38Iz5+!m9?6Vhi$j-H>y%IVDNAl-0e)?kS2|Gf0Lf-hyGtBdnF9r)jIT z5!BOjF5xh{-lcl}>WAotGV=JlO@IsReik}sR``6NNhNlPijCnut zB8xPxF`#bux~(7hN5{JUIwytpWzGsHf8MfQRD^R+y)ioOL)O2w3w|5>f}Iy;#92jWc5@9W7MG;tadE4N5>Yorn?1bg$6xwOcb z-jF={_9Y`#$h^nq_yIS{e22Zd*mx~6Dq2a=)S;aL5B}Jky zbk=8)pf6pF=z40pDe4gUC5f9wTACKYVgXkd-oj75V*_cC|LGy3QBW<<<+@6O zWSftYFC<5~?4^}mje%h$Tdrc=1jKC_=}w~y%{*3%T;Q*O`Bu4ovH(=hVvNKWt130n zAO|pn`85H`CVfSC&580rPa9>+M{nnnpplfPLuNHybj74*o_4b+F zn_+f}dP)#PZ$KfyLOE0M&XoyrRF}}iAa7IiGtvr zfLyKlHtg_T&%GYBF{11~_hM7=VG@5n(p}dI9KQVhoU;dHJdgc5UW`1B8)RvmG`7wq zUO6|y^q)$TyLx}iN!jL;>;0`5$VSD2P)|ucg>vf@0yVn``)0k+>;EkWd9qh||22=X z6`*F>0Pe6qmGoRE7j+m90%ZKcRGs^)?Sen_umnVBbgDHa?}KM=Zb#6|xh`~EqTe8N zkr_J?e9j|jxPN#phuRI9756SlL!Txj?vL^Wsq|4fe`eU-i?kZdKRc(t+W3`!fhKG- z8?mrCXsXib0(eQd>w?mL?f0;_>@3YpNs6=Io9)2c825W~GS}m=)7x|B$&WU8e058t zroc#b?av!LGY3=ZPA8Q`3YBHDMb;+G@o6LW9~QHJCr&EU+$LUY-$7>O;QSf3sKt8$ z94gEm`XVo032B0sI=lr6&bzk~>>dc#^9MC;Jz0PojBH~SJxI}+Nbd>5TdNFQQ5N`> z3_WFDAzB^Gp4I-K!)7>(^3NnX*a&++$xO?^@-{S{ZYp7$GVsyfv-D4EkwATCWM%!H z^qhFxsO}*>$~O>8gLW$7csvCox!3ob8O}wLpGNWV`xNyL3t}Y=!AboYQbq668@O|? zyzut&(Od>zH9vF{30gMb+qL-o4jkCYc2RZpD6iF`cm9Ohu8}IglgK=cN691Jv+QQ( zaWw7C@d-d^@!6Mp#YRI88r}WN$Fg>y=OG8(o({cLZX_bCH!V$$r#djg#qZjJa8=ZlTzSF=K5m+wjyzkk`8A;8ru-oSldOh9i<;bI%`W{x5+W(NF z)jeA`X`=-`Em^)%pQ5?vTHe(ec={{<f2Y$CXa1k{M zVSw|@2Dx?{xaHtL<$J|rOv<5|b?xk}uxy3C;?lP>=?JbKpxiNJTXYVX^0y%EAm}_cu>x77RPfYuVKi*x$Ca}>~M(jsg z8PHuzO+aOBI(b@TP~y_dTLUBMi?&iRc*Si<2n-D(iR2(D+JU;!w<~@0tE{;z1#VrA zJaNOPmxc`u4fy~GL+AX|BJd&%fa!8KlTGUYVqk?rn-QnQiCZbtf>&%En8yO$j&`h;Xc6S3Viv zQnNS}#GR)wkjB_gtQ#_Y8t2SfTVY{$l;=@{p3^uWuUmQ#%W_SN23Vs1X*Z6PmjtG9 z$42E1()KJSHdai1Gjs}1#+u;1Y?;z29@mwwSqyyIZ$m`}bUe|u`8 zPPZ6o>crO_x6=5yZ_0*}M$D~&riIH{V{aaYi$FbEj@i|Kq|A1e`8HD6{5A&W8xDv$ zqE=t;ydIizKt>hXT zQ(w`6e*QAc_F8E<@6imnj03h1Sr<8_u_4k-Q9ivwGwp|l9|Z#@jS=aHq={M?GzWCz zBuw=AOr_Rd(`CUe>7_+#;>Kd(mEGbe^xHOJUX*HglZk;3T&uHu3-|ga?8u>^o)68| zh`Lta#}`@&b|nCoWsyVzZUZy>(7I8qC*)1kB< z3@4Q7@x6plQi=%$voUantH=I2HB5K=K-pNG@fXVuy6-SQKd;jG8w}XohNZ%8W5b1d zA3^{oV!H@*gVRz|N8wT&>(TV(c~Y5qXup81*t$%)#9wG*xmSuC>Ublh>wqQ~91|DHziNw+g6kUE(%zi;h z!yEm+YjLWxRA|lD#1Ni==RX2)6`a(n-VOKG%3=DK7oaB=ChPXhPv?9xO~l@|(D{}* zze8+TPRF6}J;Rk#a0cQgFrTy%h)o3*6mXKQw#>?ClS)Up67e^mf0U>K<`l~?ICo6* z#cz}S1`f&)iWVWe0ky+l76B*k6F)nEu}Y=)HLto_$LZm(IE;YZPO1vmMHSt<=fHw7 zx7~cD0;nF{CREKaTX+-O`^7(**cJ|GmimfHt=}%FAdGANTqfH1p=f>OqPq9(q0$ja zWPP9GR#CkWW@MV{iUx`^6}DpZ@j>}sVw)VVZJ!8x%)w1%Y3^d<$NkZ(Hfx#BhqfC` z`Aj6lYZdN6#QMi-I`2CBqd(pCPp==iK9>|6#KZ`9Sk!r_hd=4_xU63C=>aDpgKR7i zFc||c&+o1Z)FXf}sxtQxF)^%j_Eb#`AMmpf1A+ajn`g`0#dn;B^Wvi3%z5&mjuB)g z^B-`5#65#={-2?bWS&cG^KuqU9?dxxpYn6M`?-HLjeW9~x5^zR=bByh*bI-`POLTG z*lueHoOoFhy&=y65knhKj?c)mUZjC!4Yx?z1+L^}of?zAN0F#&zB(Ok*tWAf1d#j; zW&6h|)o<4)$LQbSBjvt!x?ttKn7>^&L0cJKhB|$6jjdBe>?wf}$4+#hG8dd)QPB4P zQ*M(l#4w_h?)yTfF_pq>e^bc$m&r&`I@$r+jv?-gm+_I^ZZB(GU(Y?n4--rJc$sq7 zE5_>MTwYK%G^O(*r-U>9`^uN&xCpeyhn?PiSe7+fb`*OdudrOY>AdHF-AU7(?wED? z;%GJ@43CPva}BeWv;P2DcLRU*b4VJ1p(*(Sc;uWdh;(_cH>qj-F+J4qTF}7vm$+8H z#0^h;OLt~$-|4pCTGzcUCLcq=X@$7&`yXNKDgYgr)qn3$6@K&;0^}|ALdxG1?lUCq z67CId7Gs91|MbAi$8$Wdyu!&R&wF?P@@0U9r?JOF0ksk>7sF z>h1%Rd*MWLUf|Uwr+y(hz0VvP67;d!<<}HW9c|Jd{P^~o~3l<&9AsoF)~ zo@*NVs^Xu$e0%!mVZ7ig;yHGAhY7YY8FYlQy$nS2!QK%@&}{<>2NG{SCpL&SgdOVB zZN}s$nm#5-+#x_hUM@Ost+H=kGq?Km@(~VVg;?mxf~fsVNjK}NLv6V`R+Y3_1;K{q zzL?jxhs$FYR||QSA=mcJNv8&aYrjN=$JEP*$2;@3gRib(-qPon66*MNPf+CT6%`e> zLN210vTjRv$U3R!n5A|1{SQYB`?Ex+$}={Bb)cKV&@>p>dfrRT>UFpbT*CO$uNJ;7 zU(>#G*QcdLXUIS^LArLRM${@~(8XyJX7N}!pWWZ2U4ce~3!->k(mGt`io;a?{Tnu( z+hc1nmwF6#&8GMH;>ET_gdCIFzZABe{}_^DT+3ZtX!OOZfrYq{&SGy7s*LZh_Rxzu z2>u$v=incg!8}budShCDY$_){y}#4O_Tx;}%QV=^2@rsl!JyR_J_3$WwF9ZDpPsF? zb&8B={L|x?6OW{B=;P;e-5XBZ2G*3mu6aPv3GcI$p~RNDrX|3SL@WaMIXQcz_3NLE za2@;{YDYr80%qJ}wJ6%kIdJa}*#9sK0O*y8i&Zt}P62oClMlz&N_vqYf3|A5@4oHJ z)$}3tpp%ei3Em@RgGMzS)qw50KI*2et-b1~m7J&mlxoWBbAZ~Uw?yJWU(2%H>SO(# zcNgF9_TBD3i$ahghID!#`~joZyK+NxqMT4Dctm;m$3+ChrsUp&5u|%6{czKx+g-^H zv*A;Rz%06eeTseua*BW;a!$_PuU!i7C%qP5x=WrTHsyj&kKDQT@9#GT zxTV81>`}n|DYSh90O1+G?KhyshbupQE8GyMf;6^>zW{EK_|ymlJLmHWez`ZRSU37UK4=c`kEUp! z!U*202IiL)$tj6yP>fbE@fohH7CnI_(@OS4eZ~l$kp{C(+PO9a|u73O-?-I+C18Jv0H8A{**T{QhP@M|yC4ujg|`fxrGtSbZj9fupU>zO2bsiHku9(Sa_;J{MB}hHF|-rruAq%=aX+sNnOPRO5r=zrq*5 zbu@Ozv`bX$01)bQBKD)g*Wm=0nP9uyYp7;^SLv!t*W*oK0?*Em%WY%@v4)LI}!M`6509MgkuHTEN0qi({j|Q z#uk*K6*?)~n#RQ)enDaCJUaI@p8@~FQS`6V3B~K*I{=uAtM==wWHLYl-ykVO!aE8u zqvt@H%4ypjVA#NIxWw#_!2#En%m6WmaZ8m!u0@pp*-bcd%*^`@h`YOQeSsD9|D)@z z!=li-H&8%SVg!beA%&qO2b2=&F6k0MTDnV+W?<+JK~U+CPH7lAMUfDsQA8v~kh*(3 z-#O>I-|yakj_1)wnb~{owby#r`@VaF%&1RDga6>xI#56c$MfEGnBmh!e4i!h-1K-} zYNp3<;Bx`A;0eCi{X>uAf=@Isk)rr-q%ahbvE#nv4 zRqkV+k~LC|5SJdM2ML_RMOBS7U_h0Nu z%Mq@#;Sg9Vrw(m={P6V41g(!XNg3Oq=EqEz zVqqKCh&b9NUe3@7Ing!|@;n4o)f2Q}eUw?BMwNLc=3WwN=>rwvgbv+7FRgbPoq)Bf zR^T9_kb-4dM4bYxh0lMjg$CYQ?HcD*2N|q-tduJx);AKeg6XbR(RCVmU*!$w4m@$U z;5+J=eD=(d-)83#hzy(u@$B`4p6bwo=gxDu<`x#CWW|3PmBYr*i-ns3R}>&$#vB-1 zemwkXAppD(vVu(}UZ8gaj{zIaXV^*)B0#+YGLj&Z8Cn)}ze(s_(9MPdW%Ki$YUOlz z`W{jF@%oO#d{t0xoi;?EXA_Ve1>Zp(&OCA`+RxTrR1-sf7m)y!e9GKl86yIlkq+$e`Zux8wDWH@3bDop%Q$S z!zp($QcP7gdDVPYK)@Wa*ci>SiL+^N1x<5uO^4z#AY$JU>7x{kTJER!g8)vT_hh5N zvk^Os2im<=u|hd-|1stnaKh1lYqiXYRe_Tr5gKY%zw3-kA1Sp zB5LwMs)zp5OJd1~SUXdOJC4V0ecj7FSbp-%$_vu8umRnA+i2!C-2l-c4BwihpC z{xDjWnUP@l?Hgew3(=mb8S(~9)h0k!t_D}V_N(DTsBsbprHU)*jS5viYjG*l9q{&zn``@s;Q6$AIG6 zbY~0Rs2!#Zvys?+u~=6Mz)& zz7!8?#JZ!{Fla2#JFfiR6N}zWLb5?TkImFYtSq!ypenyAp5e{=%|c~tpIe<%z>-5v z++cashe$q@PvN(e{kD&?bEEJ^^kEp+P<(?;+C<(y##==Y<5mGJd^;q9%Z`kwd!Fci zEjv{H_m^b}2EnU4f=hhmi?y!<)B5#y#3j29roy#a~^}W z4sLB!^K!OLktuyw><<_xP?FYrRQ3v^R(hlJG#JMNI8f7%bp<#>fDV3nl+RIw)q26G zJQKkU-#^~_=*?wZK1BJjxRmT4?CTcj>D4{;e>W(zh8uDZ8DKy7j+J-4b&n>cCl|+u zyz@deE?Vo@+L1q9>FMSD*sVUwIYR6d@5(KAtc>+EE!I1=i}E;4{pU%i2&^3=x0!~n zVkQi#(RvasP>Fcwe}IKCJmIcEyF@vFv198xKPS?WHpH~$H)yU9?i;GwB_pF(2ySwH zo!(;E0kzby#+LLy8}b?Y*b?_vFE#GVVo!qg9}lohYGhZ|KeBp^+$wyW&!t}W!pm9b z7fz7AKs6(sP^N`NLDlp7xEW;kp`C3XD=BOTOhmbU%#YZCkapRQUn?&)^LtrB39X6- z{TNX6Jct9XZio?uhRg>;3X{AJDZ(SX>v_xmAHW@bghAxvR?NOUW6+Y1t(jWEncuQBxjD!o6m)9O?o|#&{=_ST` zioL`dY6li0lL55J_)kPxzL4#jK-hkZMJ?l#vAzV;-7jjN4-B3H;Qn2K3T5m0(WedQ z{s(?3ck$*!8}N7FOn|!QOjj8{ux&nLPZA)kVLn5wG4+qXm!vg*81`2uN8$e4nh#d( zw;ZuH__~JM3}?MK?`5QR_AV@LnOnW%PZz%X1&Y6fUq);_Q#b`G>{ypu%pBRXQ>rIo zPF(#$vniJ`DB&MgKY4d<==nEdxsV0jKO$0TLrH!HU`4j~Md5kj8N6XIK2TX## zE_O(GeV;LlvnVhHE*{mOBm&YHrp+kw-aLhP!OU0wve`7c_kr69?9P(}ku>oC0Mp#? zp&u4+bo_(_8O~xdUy0qYhFLvF47J=7VbK+G71Lh!#nDBBlBnpn=h070jS3Lb$oTkv zj1WR_j(~`$?^fWMS%vS{2}bR!ZNNoB8#+2#1)$JIp#%`2`OL_+w%}G_AowLz%oLOT zcLMOI#gM?;MT{VRa4qGOc#LJ64J}>4x~RLYo~fo{J2MiM+r)Z996eE1|wIwS#=<*A*Dy5)Y>lT7pTZ5}tsug88z4_2Z!y>*I8oxXTij zdbV)zFi*>f@mCRpt|VtkJxr|eDC8v1tbrJ&w*;@bVNdXCCK zCuQZc$_0^0wsIKBP=GV_DsxkN%Fy=w9^(j$9>)C#7np>pO1|d+H04L;Gcs^SMr^>v z6p&7*XeVzQ%u;|T$jd*E&BwGy+R(GrcnRt~^zF~7ocbZYy+Bn|YeNi%dLo6OLwao4 z3*>S!ozK3!@OA-$B6EPO%l{yP@iFvPf*Svm4w)%1t6{y&062Y5>k+2bgY$NGcdLf; z#lM0Tx&R=JyzlcJuudFco!k$6d{yPqy8xh$2BVxQ|DdFIec#SFv z9cX;6ZukYe%xLX$9g@VHGH4&F;)y)HJK>aSyeN(C=oFC&bUbYcd_mPTLKT{#`- z=`;3I6NzHN9{0L{an^TPM-u^uUOc&vFdEz)e5aW?z zqZ-tK$|zZdUFgss79L0KiCKgcZ}PaMi~Yu@>}#l4g`FG$oD-44B_PE(^aVyY<7a;C zzJa;^`}jG)+~~lY=u1!Y{@mtWaBJahp8it?7%DEgVGUy&r}W)5^gRF?*9_mTn6Tn& zOJrCn{r)irG-XeHTJJrDUtT?@tt-_LVm%ct*j4^@X`kF&ZumsBWFM>H?fQi~R$;9d z#|sr;p#gJ$p$@iC&*LLRUmqGgkFlit^`^vn?P~CUCNaMjl-eR9B53yJTD+afu5?A> zJt)&EAlP4DB~t^GYQ)I5M^3{17wnXY7{sy(%$-OJGJebD}=wmh&TbsOKz_201aM5~T zixZc6@Ec$|s#X(z}tqM87z%eHNk)V|@26De2E3B|edSZ0=!1cR<6bC`P`IATjs1i#IsOk z7MfU}vk?okrr_hGI#_2rO4#+sQG&(_`L6o^RS&y1Ch(TdUy z#mM7GGjyW??e|V63bEf`^O}Hyc{GB) zylH)$^D;L}US3X)iZLq(W*yE`_WR_ZJC+7x$a34qQ1koXEuM!B<;^$c_&Ad)-+sHz z=SkG_FZsa&7R#)R| zS6J)}bkT`=Q@gi5Zdf*&Ob`n>`ru&2+Z6ESG1Uzke_UJy_kq1+SIaQ>-b0+ncZJH@ zc^&*?esXy0?mhk$+US3l<_h1`h1s0Pc*tRlPl0d1gt=|;5fpZ!#5N5wM`o*c&A_O^P;qU{zgY zo{`!_-}P;9b>p&jGW~UG)=>3S#UmQPQ@K0UI)eMi4&Ek8s@ zp5Nu%l*s|b^J6g1vyC#-6RIVcJ-{aG?_#44INcT^i%!%7{D6Jd4`%;nAE_W`ANt19 z01o2&v6XN14R8{(QZ*s*^|Iz__5yETrE&eRqEoRQQTr+uJDKQKO%f!P)D6C~tx2d8 z2DWsGpjq3M6FewrY?)^JIF#vR=_ov)hE3UYE0Nk-PU)*=iG!*54F)zc5X*yQMb$RX zCFMDh0?%Gyi6y#4$7j|)-v=IOLX#yx7t2aG6~_wddO4KYJtv+rVgRR?`BU{y0#RD> z=&13wa_ViW>Y*W%3ts>%$V1!@6az?V|8h&?;{eqcNzRQG1Kn|0+Q5KfE5KeiV0zoN znvetgvrd*g^NecV607I~4s0jAUuZHe>tIys%U1O=Q0@%*^9RlLI^^u}eG&FWt0}9% zK|>T<`Y(3k&mw8sHM(4AINLRN`U&S{gx^86;}5lU*ySz>mOSK6Rr^u#Lvn2*ZED_z z+Yw~m7wQg|O{XSTgW|Oj^3vR=_%3Z!zYr9X+V-`Q?eQzTeH>E%0Vz1+iPpu%*Df8Y9-P=B$7LRUz%O z!*_iOw5FD%lX3(sNG8VoN=}@pAoxo>cAO~5Bxo@&+&glhfJpNRZ~Th)i#*mhTg73c zd2x{rq|l3NgOO9{-!EBuohq2U=Cf8GKty7cb(z{oin-v|uO{O);NOn;1sRh#f|kjK zjbaU)O>Is++Kz=M%;K>lvS$y9+Tx+XQRysRQSWe6wYyX`Bq<^U%G<5Eat`dCpH zM`9Z&>VH(lW1OiTPf3!BM4I4u?g@;G)h3(!0CX~>U-;s)fuiIHk_oA#t`MT!DE%w$ z-j5kG5$uzgdfc4PbzOxJyLF65fU5J3#Ko};_*3B%fLW?tR?Qpn>io?G==7amU9lSnc-O@S>Z3Dp3$Y;p`&@o2Y>2b02hOo*;% zHBD${n=bL+mMONH>an9+z9GY1fafF4@R?Zm0p&aCD9N{s2~W(M^7Cbn zt8>~m`7SJfDxsFEYTrbe-CD#oL79mKRV#I^HU>OHvzjX!C3TzOFB6fDK#L<}LVpoC zF7-ZY=Ep(Kgywr2GYJXlS9x|_PP3+P4S&jVn=xGb@@0persDdmZK<2iGgVzaNq*;C zfO2-!JoWtV9Swpf@Zq}2Ehb`wEV`OjZa|2L;ntb&e-$b z={C+XRPhcXMl0S@3s^X5j%d4Vy=kN#zMk~ZHRXk+9*G#qL*Qk{-=6ml4d`Yg(BlcF zuhk9uAL~Ci!OZ; z3ANR)*i-|Ro;p3Qck*=qtn>g8E-+k*^ZlSHi;`p|>0u||BVPd5=X!z+QKD4nH7foL zre!cw1>%z7t%+ec(!%ip%B*rA%S{=ga8>^#JdY7cNJto)>s&{}A?_uV_Z%!oULj}P znA>Sl5#K77M%nGZf<6CQupK-^v@kxhjxtf1zQt^(cLy>@1|drvrQfne{Mq#ydTgL& z4T$m#S|*81gUMH)0+e(B*e(zNK_(ziTw3w%qG?aaNwJl_iuEIz@w^3ASm?31GJdxMa=FX^%eoGNZ5K0P?sO5Z_ z3QE=MxUU(J`W40;z+>nRexBDSN1e`pvfI3S3V2Z}M zRTC)h8psea&y%+1juh>FPIp{Pu_uj-e4%}>-}e)goyrVL+pE$jGZrM-IsLefIgI#m zrAfyt*zZl~*lBxHlW@6dHE9pZq;0PKuk46PlE-0^Igu|-W=&=1q7+r2vpG7_mZ_0v z4Dh0RX;9|MPha1A)>HizJp|a!st@#T7+%lAi&f6Og z#81#E1s&IEU4r)RvhJm&gM-PXTnam?@exbIXF+Nne)_3K#_WD?e}E~ryB!F|*BgdT zNq(LKb)%Tc{RVRYK-4RR=^MS>+B?UXr`*Vn=6PE@(s#bg-57fy(q%(MBmPRZp0uZW zwDO}CCTIQ?T`7zSOmNHVtWtCxWGry$kY-z{@ZE%zpQ^m&f9{-X3@_0xBgy#}{s@`$ zYZTxJZ}P0&_PT*9NIY}*<-R9bkDj3eMGw0xcUP2F6;M%bEV4giG*Y^)Q0FkKbpc=m zn!7u#z6UYexM+cVJzxN|4pJDbEMnp%U1T*|uKy%E3g!2sSpm#iHF*#*Z>cEwy>8;3 zgFr(10|(*0n>{M*2-=%0pvmW=kGBFLA$V_o?FZ1nucO#<_yXpO4~fNgJNX|XY1i+^ zPKqZ&Q83n7<|2|I0x+Ja=&C<1-(2jtR8QA27jDpf>5W^xTv$&Y+ytEvGMy)}yAmaN z6F#9STNpJku{7f}+P$}#@Mm#$DL{S!oEVy6@;|5PjS&kFctLfZk(9Cfot!5s&?C^h z4NW1(NalHPCoF+d%8(RzOP2e7@}Q@sVR4y$~Ao|m#0Vlf`1Wwd-mDA7$pPN$Fj=aOyGL=@sE}r z!6APx3;KX#GXVm1EMvDpmcHNNqscDYT5-Ri+sg`Lr-}Ay1u!H`98B)lGc4i~G1#QT z*lv5zPL@3|a#?>E$BjGP)sg-FTAe#61BQWzi@9K&$7= zm~qy72^v8DYSO6Z`1@v-genT+q>=wvD-KV7c23}Rf8j;hW3q~#Q7z16GKrcpOO2Pj zgWd(`2qVU*C2R=PS8YA}*munVuL$?_YD!Cj40DXTAH*GfK4TGo_oAI-;PQ0wQuX`^ z%Iw2ik`@wq=El_lkw5hn>+(F1^WLC>psxD%+8dZkx?o^sa7J#*sM2|U&}<>;`EkV1 z3fszZZs!20zmBm$!#F8n%$^-lj;@-nC$$%;MDra%%6Gud>JUksxzr|x_7dpv6d%s+ znjQ4qMxMN|jKMH*_Xuq)!t)b-eSJ5cAxmLd1Y+I3yfU{V?7e?JzgrWqAUf+let-R2 zS*4)eDIoZofT4m32yb4I={e_)^N~|ZQ+IJ~hFm3c!!+j;;7JqvO9aj9MM2VIAXg=F z6`x2d0C2>gFU~58E{BoDcwX}o_OICUWoSh)A2$s%GMcTFIlSQhH|(j#oGo0D96z)0e;b1*dtDJ<7a zu>yRPkB7wj(Q9scHyjw{5Klh!XlI1^7L#rMsnuN1yte0jbHF)Dg&nmTm~(wig~@Z@ zdwTd}^?j_j&_}L>`eH9eInDEfoNDwgxNmI+%m+S$X&x1@=k>Xqsx&pt0T`miNeID7 z6lVqB`j8zZDOJH9Pza5(ZxYfdu5%!{5hKV4V0LH;xJ`X|*#S~Z`%i@z-NgMEDm2N- z*z*~CDS3>aq;xs#CwwK9hP|G>*1qO%lJH~_I3ZXVrx>aIyOvrgtbCX25#;Jt5{Gkt zN;FGaa2eaor`SfEOEMUlXV|Y0jvySl80WDyWnR(~#U>?@%E%`+-H2cI2?0f5s0EP! zDahgZssISCn;omVF6gXYO5{7&{=tEF%8luWi6e$+#sZ_dC)Fx$rHU4q94~Fh0<~S1 z-FvHV4ol@a;s7%#MAp4m*tU>CAS92}(P?nlo%wZ>hSy;ufg$PFJO(K^h3Jas+Z%50 zQ)daW2jc4t12>^xvXF;<)Ze5&OB2Wtfn!Uwtdcux4A+pY<*-zGV$y%qO}cvXdY|~? zPON6o7J5tOp_LkcZurEYE!d5&SHMe+4E|Tt6}nv`sE&> ze6Omc&noX3=nsa0`?EFqWeklU>N>s8<9^kpdyrQUR|k;qZc&yB7GN%`zxpJUJeHQv z(_Q#;OP+U%_NU-?6Y4PyPQ7;76;5IhjAV`CDX(vfolEL-csKI<+pWpLj2OHq`@g zlu{N#k}Vje0yQCl`-5zK!?c0>K%s$uE}QBgL(v0G&%OC?;fkG0 z!hXZsVom!~fDqXLK5m#m5cOZDiVAb8X2ZU@v@!zYMWJkSjsmoc!G{mZ@U&hX`4^zK zw|`%3$*7cd#}?kQ-SBY64h*TFVhz==g7|tnXbSi~e+E;nJl|MLuOIA{3s(tm!WNyA z7EYj6Cux+Flv&lfC0cTT5YRw2OY`Hcf%(gT1{9|w6(A>MusHrFx-r#HD?@@Ny2_7j zVR*@$E4f?77ja}z#sN-3ArmNi3ddg_Nw8wh0KVXZ)D)PW`OJH1RGvmT+NC_*xW(%Y z;M{i?J6C6P<}fB-@x1eSaK-^>M4uGB0y{Y@ z(({InBKW7(=j%AZ&EvA7H+__rtOk+EkE0dQdh4+X2SXO6!(GzP7(*^AU>@o=g6RM5 z#tovEsKW&F;cM`**8^*81wFYqBpL33SqfH3y3U_o7}KW3T@icpycrYrP0iZo2B>B9 zn~D!4Jt+DIqV<52D>{lilKVT*2ApsOo#qWhfhf`!bd;8$$`=}Z>KG6?k(ivFj@#Kd z?-sqbzTSHkLbKMm7|;SlOa>;vuhrtjbI!IX^^-M&k{+q+4OPxR9V^Kdu)F)(AO%n9#!dL@F2zv ztwKL#N>_w?(lec3U+_l=CKJm3dv3~oL)=9Z|t zn5`B5Sif3NAGI7OM7(cgJS>OmELTu)<}#=+Rrx~6_8_{Z^yWX?3ChDx0US^;J=g?R zFYBdc6-i)O9s*}uha3wxH#aZa(6FvRP@||_L|nT;?b9yb6hcVJb7$P?^5R#=I=FiU z#^ql5Qc7=eAZRJqj4&sD5(a|ey^mX+JxYeb#QgaE1BtsWLqOCMm0(;7tibm{Rip%x z>u8*SkPsL$2({JMZ{PTl$f%nd5Hz4n%x%CbZ5$8!djHCIr3sn(9 zG8j*Cp~tkGi{P$b11M#>3Qse$^=>L=%{F>ht6!7$&<96VXOQ+FaEhk>ZYuyHa~oG3 z;MrP9%k8_}gz~gKm%L3&I86TZ5qf8nyO0Za4Wb>Hku>EfgFBp?J-Q; zze$t#01_^pX;TEOWO42x*41A>f5eSxV=_a60-CIbRBa`m$${0)V0r*&npi+HDE3?l%@c2ch}ML1N4P7%zG1) znN7fVcOC3}GJtI}X*X`p*ma7y_1v5E|M0V!Tqqjll4QWLQk$8Q6ZY9S-l6XLj}Z;FDOA z;QU@rlS(dl^piN)2W7^^z~EKw1fDKQYhHe={~H*?B}C9aeUcvM_I|S2-A0BW*Yl&{ zhNqKQlNsn&0r+66XieTzXQ=0w@SbGi16z}b^kB@htJA?LUJ)#{GWV``jr$$;6s|kyQP~aRb7@X6&bYdg18uAg9QrD(Sv=wpy z8P}pQ;#n9AQsmhQI7QT2nfC5)Nn0e)0$GBN{88r666y~t4pYtAe=_PP5{=jJPu4p9 ztN56uNNJvbxf>de2G1wh#?eWAsHu&K;O&Yfx;Ftll~Mr@JcH(jS2MoX>*`lk=qlZ< zm0!bgB51yRN5k!9d{wGAL1-GRuP@|XItI?Oav2t6fHyS9u;zgRYTPMeXm*~ zkW!|pmxqH?PSMQw6ATWaBYbKch-?G@de48GtC_e?z&SUJ;#&upiQxF%wMUFlx)qv> z8-|3hh6m-#VDcx?06)2F>c1B*QjI@2e4qx*IW4=x@R5%(qkEt#QU+%EX5}0{72P>F z6mIO`KtgW@&!|0q*mdy1wV%%BcaY;%HlE6+6@qnjk7ZCVO6tL1-f}V&c_Uh^h=*2z zVC;rB3d3oLy>jjW{as3Dnm=&5e9IU*>)9YAdaS?91bG)7j;pj7C;E+DW^VFY%iMfe zRjpcxsFN&1YV#)kG(EGsu=z1Yp@vVz5ViO9Eug22sQwS9;F^_H|}Z@E$15%lP|+TeN%DS7h`Qce|tMULHAfr&XkI)V`?GCEtT%gGeCd_q_j3A&U_f6hoRV2#WM9&;gAH+H+fUz7dpXm;oYbAZxU z3Xlo`D*ZL$A%WzyN+CUnn`JPJrMl#BVrW7w_pJb5(`H=YX}09A?X42 z=XVCNBhNOZ+!WZK1;tYgIWff992LhmUEY}iq~-!Kv#$; zD74^_EK(EBTWzGo?BIAC@tvs-wB&e&04%$z{qWyQESMk4-g{jd z9cjJ4zaL{XEw8DWxEs1JbVpG~AHc{jhLQ}3Tw`uLm&b=tePBh%Z8;`l+!p|~J!9e9g4d@}W>@!q;A@JzpCRk?N=U%qUcy4!f@95BX$YBRH;V>w zguL(ex$m#q+GuVep*pIk0k$kJ zGV0FvF&ZquT%svk;(=J;h5y0)sI4KcreKvZvoFJWB2yvmj>Gq zHQ-xOQgew)y9G+Z`&Z@y{<~(K42I>1xC8NwcOR7v`VP|LO+LB`gO+Atez+O<8_|^w z6@Rx(Ixe^j;rDPwTGc6NBkEx77A%OcGZAJbwyu+oi=Vs}hKlfml@)W5l}IMleTgtN z=F3nYIx(NBlNZTVz+bn>w-Dn(d$mkjr=NLe)ngkZB-^>Xh5wXTHl)}qpJFJOg8L7F zupyQOPCwxz<8BU|eM|bhpKrat7Hq|&^nSq%wT!{0*`Mtt^rfKzubb$%(kb9S-W{_3 ztpk`-$?g$i5(%GYc+3xu!NH~O<;&++w!?FFyPSBS^78ulR;jr#w_ddu+yqnoQcAta zO~jLghU}9AZND8>7bOS(aYD|i1kni|Jl4QqMXgo0{IBFobl>FM-yfC zfX|lk-`pBWk8Mx)D&OUBh9w^`n;Dg>8JI62CZm|b$k%Px7YL4P2FtEUDR+CiO(*nU ztMS@j8|-VxF6#!ox>=HnGxeGy%{=+cux$e>Y1ZWV9n6q74Y}3>_%g4k-UAfe%xzC{ zH%Z~T`OTF~bZ82bGx2t!*fRhmwiG9DBOuUGPX^!3vMkt!w;Q$;(JlYouEJcBWo8ey zo$>b5ADkq^#@Am{o5NZZ0~9rYO^tWnYgRVmK`7{;gc-}UXw(ETT_TNZn~4zy#XYt? zcIdO2JQ`?l9S7G-i)1k<+bc^YXh6fIz}IsJG-3aJ;|3E%Vdd{0F3sFE)Tidgid?R@ zpC)4b74!2ZD~Cuv*F|d@j2?^u8MWS|;xcoWE&(tkk6K>o699YZ=zE+v@pW2B8ZeZG zJscz#Pdp5n!YU&AuF2J6bGkB@F$PGy99!cx&!qJ}RiSr*d3LO>1z?KB_U{4ZKLAo+ z17fuN9wAh9NkYuytA;P=N^Y3NN>fSu15U>z(DTSZ@(kq9&j2W&>_1VA1eHzT2_XMD z#4&U_+Iuu?T~N@a+}Wzbw29t+YG&^Viq=Ip)Mk&K>>q|HF@ zM*RupwD6WHIuf+d+eyb`Y7iiL(NMqtQzscl44-)lN!IuQ&Qc<#hB?IE&lHy-$-_k+ zvQ%YvN?!p889iuy9|cxrfn0w<`5W?>*M(bus3&`!Yn*BQDsOS5m20o}zbyhWYho zjGaKEa_Z~XZyv_)oUK^ahYS{Hz90JsFtUvZVhOK~n>dZC%c6%yE?dNjSmTw>0(1N4 z^sh;gIxp_nCNUZ^B0Elh+{;S?`V z!nD$YVC~?6gBXGt@TUb=Ka#|l0i30etqEws!1x8zvTU!oLl`kG>LOs;9=_&B(w#5~ zrUylg$UQN6Pf%Y=AAOZg;F-S~pp9INH0zUGtjY~Shr{~r9Ro^M>X#h9F_O9BaHWh0 zwW~KtnMdXhVaO(=aba$qJ1EtML3(XvLgbS6*uE{Uf2hesB6LFblcGKZ#Be?n z&Qag!ky`xIlY1%@5iTS1`A?7TzqDUmtgn0n@QuIu1idmrr=>(h9L9U{xl&BxKydQ; z9JJE$wR!{%TQfY_;GKvXp!qB@?P$j^jpf_yDS7}#KmRJf*9n{D2f*nYLt!R0mZ&Hfdf!gdIwDUp6Qh+kkD6~_#ThmKnLHj$iq7kO{sq$nD(j=9+KoSnjffCK| zA+osgmgCj4P{66ycW=Xt*TUQc0b7*=Bf%6O{?TzDirUm5P8KHKG1mvvs%_>cCv~!t zsNA~jo7*Yy1RdO=w^SPI`GM~N-W6!T`nq7^7gj&rPcv4iqJ7ri`vHK;Q9Mb42gF1P zew?uoNd|gz;VZ~5?v&oX`P!Hls_L6RVDh+~%QP62Z-Uo9UF_v>CEj&}d#Fk+W zfU66k`6f(zq|8oO^ND>+lq3w7e>z;gNc*p9gCWCUj5Sl+ObN+-odvo2oDcjb@E{M*ah=;+%*T8?5_Tj5RlI>SKhRu@8$p_pVEvSw>!KMny8#fg zsSw?f=^O6Y&pMUJzs*(roq0juc?HKS^wCZxzlUHdEY5xf98W^=H`zQGEQ?1V@i<^9 z>9e%#zFdAoqz-A~D7;2q_NJ(W9BKz?!Z-@mz!5-@eQu$TCx@0|XzgA|vcxoF6^2N+8+V{<=PvMhO)r3!Y z;5X#L>RvU&Yvg1YH8@`9tq>7bgGWL)i5RtEn9r^P3$DLxB)Zl|4-1&BLCdcFLgLF9R#m=&HDo*O2dhD!neWm5P+}qnP|Af^$%sL-t6u8G;gYTmvzmrWW;hCj$2t^Gv zGFDb!QzuWnC%d-9XM)~|9I%Dg+ygEZI0( zkOG?2w~TPOQn0%qRk_?En?KQ`k%byKfV+j2amfx6!rf4j4u=!Sn<%Q_I$`z_w86-M zzLQ+auC_ zh>=j)HO_v>2fb4x*{7(1dG@(?uQNfgS)ei-zjxo|KH?+g*POlk06$of27_|{m1={z zph46X=<*YSeIXSvjj}g)`PriTf~^BOb@ca-0$E*~1PJ(8Glx^C zhA#p(-X4=XAe!~RC}2Q;e~^%ukGpVtWN4i~TZEXj22v!7?g?$F5Hb@SGa%C{2$SZ1 zHGQLq9_WSdo1QboVULcKzD66EZ>aqL?GO%*fjq?ZEUd^%I-)+_Fj|rYI;$cZN151x zQe?8pUsGSfJ-7$o&x$0s;>N+JF`&M6s=8Xi;vhO%44SajtQcBov$AZ3Yf5+Os{Pon zIp&mA8G$)rZCMjY30n9TDxs5SZtKa(p^PM|8a;x-tgbW(J2$sn z>I{Meuh_e!f=G_xKFLyB(9yIH@mL&N7Q&`t~=scdnAe&k4VsHtU#0E_3|{c_PiDpRnPpaDGN)L!GiU7%2UJZJ3S^uCj{C zHq>C?ax6k;aG9V(hdgKvoMy56K8CtGY`6T{R>tsLNNJqb7soiP zKIZFQX}KTHas3OnK7vn?fY=Sse8>)y&||=rL`&n6e$Q^%$gH%ste0q7sEfF)d**q3 ziJmi5YI7^jMmDU&;=_--yNBS`Aac<)fWzBD6k@)|)l4_k8E^dcX;uF>ymSn3X@~K| zRAQxJ(mpdZxmlN=VE*6CoI(u1r)@FvSW?0qDVeYN`7TLa)5F^#SeCrL&vzR(KJ5RL zTJd%l5$-Z$j9R^OfDfOXUFIb>0UY1) zr>Tpg2J9IFFBX)dgao1ptF-$g%KaSuNofz7rKWZ(HS6-^1jzmzJ?H;DdM~BnB zj_*V&BoPTcFK~i`Sldez%H*%VV8-PF%A~ON=_K5@CWXehB!$&VQZ=W^vTG{DK8E~M zIaj83&pDP%#XPj50ZJbXg74iZ*)7TVr~wwnshcABU#lF0$kpX^$Pz2%aJ9W?65jKo z2I{-S-}mps^=0GShJ;)wEXDG&^QZ?|TeO2MdVj$6|d{5A5=h3Mo-8uIbP$-x?fAZ&$bUsfeo4zZ%hGtiP)HA9IO^k)R6-GMz z^tj8N4=&yoOl{*4ZGH=S!k|dB1C`FMQIXpiv3yDpv2|hy=N}u~ur)1u;6Gu$CSDn{ zEKOjsG$w2mUL2ktJ>a_jA(lvr`^66sIcXVGTK{{gQxIUYb!Fdco>yOOyuD0xx}Mc> zuw$XnZWq8y9jl#%uX{tkST`?JjT9%Lo6aYYW%KD%D!EQhoV%fEBm%Talr$)KJjHU2 zlefWxnNkTS%(){E=Xd zH}dI?yu*TAvd}1V|A;+_4QI_Zpy#az#bTD`m|6W`0>UOxUX zG$SYbTp-R|ayO0gnDIbcNcIt}(e||E8-YLY&QtcGph|l84gAMB?jw$qr+4a$z`9R0C$OwH6T@m!;H4R?SbCqJn6! z6_+==B%UORJfhr;!dhv#L=6zINhIm3;I&hWa4(FAEPt;zdAsuRTnkqVDao9K0Fr?o zLYWWZJn%M|-$%ceuD??d|J4{3Y0M74BAZ`uoqy)baQ_tTtpT6&`R)0z;z?m1;bap- zu(pyF|65Dd&Mffp9lfKKun~Vzo|-DIz!4j;pBUOW~6Qt<M32R;3M_4saT37HiKACG z7Wa^Lq*gpSivDE{PqS1<;!-r~U$3?QakJXv_s!!e#)d|kep|~w%V=Hrza^2R9uqiS zE!>Pl+Jp}^=1a}&IbM}6mz(g5Hmxy${y>dCowoJo?L9_G(&l8jHQz5DM;3A0R=#iG zb273Dam@Q%Z8WBobq#QhYOwJ$r)TbHZ1dyngxh|i_jEr$yxn1`5A{`&Jq{W<%-950 zfhn6;N+i$qLGhIQ=KrkJ&0v>&zR-Gf1&==>8tgoWOvfC^0!sM_#`QS_} z;hw?Z%eZJ6c(!xFw?i9J*rEo~*VlOSzMH`JT_?QU z|K@f}7My~D0=>kh(<50{QQt(a_#l_KY?b?RYd4{pJJ}m18&YQ3wgyiEDT2Y=4qfHllq>}~ zkh_Snc+&S|BbNzf*QB5&>)&p+STnZ{hjC>w5Bc#FKfTB*yOf9? zFq2x}72VOWA0&q-;F*_u0=m~Ycr`78HK|4u}<*^GRxH2>0Zgr7&-1_S8 zJQ3JKwo%QiNU>^O+=1=4|%1!Ix+ddcXqX0E{^Uk|Wo0UHhm+z#!Ux6aa)uU}jYS<|-I2qhZ~X zL<}W)f9&2)Ip!Tybz9rbBw(p|gOi7cey6@?Q%k)+i{fDG)9(lAo+amhI60jUaOc~* zQ-8pqFmO2e&6~=x)OYg% zGsJCxF+y?S?Tmdic#%U1yv)^V2Pjf1H+fBBLd40)(W0NfXPQcU47{@=#9an1{WhxLfnD3bt}))W)u4!z;8)_e*u79t4>MV9AVc15sqhKHy}O zEdYx&Dj}!rdGO}iBzPhhJWSY_?KNj>Mf7K2S(Nis8R)y28W+5>zzeD>019IPj77uT z^mOk_@KTfWOiu(UG5)&W^>&tQu}D;|`9CAn9&rLKaJn%PcrwmOmO91)(ahW9!UZvw{&jS2q%+6k}lQfwGC=@AaPXxCC?5lF=>UHl%~PZ}+x zv9cmvZu=&UKz|2B;_JOh3C2KC!O4Y>#>6m(K-@+dvJ;gGWR24g3R7ajVfJ)*xYpx9 zc#8GP@1vnj=9(Kj(&7er4|7qLuCuir}In}1#rFyvl%z-7;?h{p@}%-^#VnYDF$v3MggTeBp$k3y1S%HN@_O$>z$cv z=DU3GIs5GMi?!}m^~vOywA4q@VXDxTw$7b;lchS_5PeCX&?!g9kGWU@Vnu#c%ACC1 zt{S=|^4J{e1go?+ud}X&e>YjpD@*Z23{YBKKDc2rD8Mo;eW4EZkPILb^%xUa&%uHy zOhVP77-cLzYJ$?d%58co1iyls@=>}V^S=YnOMlw@L5C?KSUkHfG@r8X(_DqmlhM;f zs@L9V<{VVW8F-m3KpJVA%k^(b^tU3tVDXd;bZG7~;I6GO0x^-gYpfU+lHlISG1`++ zX~)a=HZm7GzZMsjNz$It_X)u_OoR4`ZTKoQUyg-&BhaQMl@r-O!BUCj?~hl{zEj57 z$joL^Sz^K)p_I^qcE6hm=UPPf7CO6P@Kqqx&E{u=aZhdGGGu(F0T{V6c9vp~PjT}Q zWvPrvIhQ$~Zf&baVHV16DRaHZh_Nq}p`EOYrT9yjd&|FP-Yq}6}-Yo>TTn&#Ed!bs_(|5go(gn`mXW+w8DHe3UC_pYuZ^$+{T9%nnKm0})p~=yFNIQjheqKY9fWw3s+e88C34A*xqw0abr3Nm8Lu5o$_N6)aC#hM zqUhqYf!~9(892}S=vNeh5dN3la^ra&pa9RlT-gOcT{!u&i=3~{M3=pgk>sVQzLnQn961}7T?Uz0Hx8NxpFz25NY{XXpDS7lP~4(mBlN) zO|8?~^im1fMAeilF&gso_1#Eq#vU!G)@*G1nTXO2!XD%!0UhfnMC!|i zoXJ5WlEoE4g~Ms+EBSs9Q-!OgVG&y z7<5UouU2^>l$2|5ytMAu67pHEocO?#MsgWgP0ZHE4I)wGEJf+lhH3lrv@RpW&!VP} zE{83M=JSpo2J@w@qLdPR1=>IYCQx~qC7sX+VVeP$7%>2<-8{t#UC1d9EWdc-XqD2c zf^b5Yqf^L3m0ahxZosJ-ut+ta1$Brd2jt`q;3|y42i%+Bo?!Xx?A>nkuNdC^y}82= z=a8pXo}wL%$PU~w$=Kr_LQIMX8uZl#(&%hdZqgzfp>rF~IW z%;}Du5Sq9Z@QyGLm(T*KZQ9V7*)$)k#P{&P%R)`-S?J%3> zC}t9)GYwTS!(Rmq02!=2ULIk_X$V*}pcG?u$8#v$VC6(q3xnO_F4(Vmy8~kZcqeEz z)YObMyW%=|BqCO85Kgs`-;rUZ`Q!hL2K(Ok5cg`OYymAG+u(HuBnMe)D7n%}B(JUV zt+~eb-y`Zx(@s|~_eeI>j0(hbl&5^Y?osK%^g`P{AJn0KJNCu+ zYrXcO@ETSW!dnbdn{n_cs4D+FxGnZuk@6bTT3MR>7!$0L&k6e4afry1GM9-p46F~|tVs3il;`#AXMYPH6D zbO9~rbsG>p5`rw01Yr0a3?uVxe9fWMFUdl){LK0lqXD*|=L}Fs-3Wjh6GT>Pe)whA z>I6!4RBWio(SJ6Xs}bbD$bB6NB$asqF*mB4qTRc5!wD}(hWn=lI5LV9B3;zw5c(g& z3ZuPbIPpvdseR~`wvjPGKM|l?KbgaIg^T4?$AewyPc9xzl7iYq-7)&;JGVw~vcVue zqD-u#1U(sPEOK9|MSL1Fo=ke16tijE%!%6_5$o1eHRKU)Xv18#B*J9i-0$lEKV?_w zy&Eaix?a5nJO2t^BR#)+Eq~>Aqym=z%J&RV4<3(^$l_>_>$CMY}X z3Ys(b94#@Vz2p1FOH4`PX97uSg*!Ym=nup1t)KHWT%6^M^gjjl&k@Na_!MzOpYF9D zA~ShZgh#p6SNvC0wuCyv1c<52%gg)sI0O0-4{7&zZLAH?;~B%iW}tgCyM6h}BL4wG z>4AitFJj;y7st3+mB!g*7|rSa_2Sq+WnE*tjmUSXM%DR3StmQ-b``kZesk%7GkFs+ zfxrbD@X>}EKVIu^D3O7OHV(#sMRD+oZIhfi2!u96wr0Z(FsBsLsM@8Rl#)7t$E|+I zBp*Ebgt*Dd ze^u3c9nKH%w#-lkVjRRLv<$SpRWZDrFbrvH1KE>U=`m1-cJ|E(nB^`o zvMl`9vKMifloyJG&|UHowLyjWjc!|fR@AYAOD;oOOi>}h(TYKgn{<;OZ5R$Ce@@Qh z{sX^2(J^?dU*%)DdLb^g_X-SFG=w79h7K!X5q-V~4zZ#$B=cQF6D<`?K2h5&)bSM( zKb@aAA+*7}s+|R&mtQNn_%c$8_oJL1W|6}%C3PDnwNIC_QkF`YH-<0oR~ri1Ga#9q zbPmu3g>-jPN?h>fOuRun^?BOaR{f^hd#*oVW}sr|_!pjo_^E3GqgzNaXJ6M}yT@e@ z)08Cz?d62{yh*}e#RZ?4*2>&6ru`Xb-kgB2OMF15A04_noTKY9OA8x)QprPaCQ0nC zx;a1b#LHRPTER`Hj-9)o{y3;4m=rNGO)W!DF1aI-UJ3ak73fiX^J zkq>gEZAiBY5bYVMFZxM$xWPSb(#y+-&r-_GOV^~CQQ`hzFl=%^dfN4hoopB9X{$~l zJjSRnL{gDk%+L0sQZZ6iT4yguMe2DwZcaiR$oDrPt`nm^uCG#XyGrNEnRz7)He)kx zGOHuf^4*h#tYg$C-EwMEaic?OuviRjm0Z!3dZMDF3)b9-R++UIbuFt3>p;mNQ6qv4 z?596*dtA!sNL1&Z2|#~iRKo*QjWb*cJ%^`2^pz_F`j}&o4o(e#L-9a!uBweS@nr%l z3^);M+|NzZ0gN4_yx$}Ou_+0GvLNa3VrFDEl8K&9f9Cn+p(DY^Y809ExVVw=39J_4 zyREc>Xi+IR`<5@rNt`E7e{thE)W6upgg=aGq!my2FN2;~E;`j1;&`!$y&+c0USpXxj5nz}B*Y~TDeaH9*kfhx5Wq;CbB#p!6 zB{W1R6i7J=ktp#NY!V~E&o3J zgXq^aaZ9gnU_3wme#f%oa*w|kUab5<@_k#P`fP!s5Iv<%vp|p$r7W|2#y(ABs>h;M z9AZf=M01)G7cpRRQX>&E!4&^le(7RJPudZXD``TR#{NykGs0kE_)kj}%?JeZMIrDp zTF&XpWrKEU#q&P=va&8LE2DKU~T;6k|-{&z368Cbc`iNMQ7FBK42T8`}ej z`*^xYOM@Sz%N6}v1e_7nhjq!r(ub2fvDMh%_Cl>1>qYbo97z84KjM8df%7uPjM$d$ zjM;&wcKfydJstfjSUa@ci&5+*8K2cFf!}wi+6l=7;sSx|*T`|f5i)d5lvt8_gylF| zeMHeh@!FRy8FGL;bL<5zU7JY9aog{wEf9GP zlOXVvROO$$dYoFSvH_>-Qb%_x%KW*M4;=y84Mtp}4P|9)zBo^zRcMj#`Mcfigw*v_ z(`vYaoJ6NkM4<=#IgjEp3q}N(iGQHL3f;|{}CWI)-W_v;xl#9IJG8p)8|E~Q=u_xW6Jh; z+>f4fcOy#lz-fQoDXsTw7VL2yD`9yC&&4ba&&jSgzw2v@TI0=d-sn)bnH@W#_X|%+ zi*Ky|b&_+blGME|ko;uCN*NLlH8F=9pb1u`XBAvfwBGVnhdq)8b@=BPk+KvmhF|lt z*1u+>Tcga`R!O?JBpnx&xO4TVk+4w`d571u45Q`9q)_|@R{PWbD1xaPAh*2$Oicxl z{pv%q5eWa<90*!DfnzisNcsLA&YgyV_NjT0$fy#pnB%`kRB$&pH$?~?rKDazza9`4 zt;N=<{M=d*DD-!|U2*p0W8gjfLxeixr5`D^1nfP{5z*6O9Au(Sbs;E=T0qEszGL&i zAQttz|1L#uAH0f@koKJELX22lPZf>Nup;|jLuS!5FX2t1=tM)wmKgiJe#3Y`%!^XR zRop*XIVa5i4JO11IlNt!mJ=hGW?ozYMvgC&$&|+{^^UwZ zCtF#3T?fV*ziU16^Jk@T-175>XdTH4x$Ti?uWk3@{lg6UD3Nb+s|M!Tl{F~AC``XgQ&l;%rw#d5y^BGWsdTkq~JCu#s}du`XQ=H z_~~&`K=qD8>{<*eDe{O~el7(iktWNLh4sgD@Nju`1jnITqJ*f=#rA2fY#Cf9Z#y>T z;mL@L+(rey$7T-rtq~xUF7~YmWUYf58YzKHSs&GDQ^ZNCU?(FBw%;?dV2C+ju*}t)d z;YmI7ki(5*1Zpr3-ikPQqn%5{(8@J-NoJsuMz#K{Aj*W&F{n=rN4R^X#4eNIr`kL17I%V$uO~sN&?^K47(ae0F4kl-1 zWMRm-zxE^haC;KWam;J0zt-6JQIEW_T3(ddsN08ln~SasLR}cNv|$)I>U>1j$h$?& zZC+I$<4@lv?Cx~+g)W|q=rMf#BUu;zSm<;6ZrowfM63Q1lkW!=OgVheZ9F5h=$^rM zb?>xMgkOg4BEh2@t--l5>j)OGlc7;NS)VSpe$m-+N7%iNYcUKotGvbeg~~8;b-THV z^hv%gbsZqZDT3d}tH$nh) z3@?y2(t!D10!TQJ0!4@clAh7as3S8@IzO5g7TV2}$9E6zYwD?@ zCiw|mr4Vr*--Sl1I0I>xou$99xJBxVmUEtR1L=wT4r%09iSh! zCM7MPy4#Wdq+>LW1&1Y#61FHWhV`;uNq?HUevoy?xqPP$W~wx=mBPLwNyS2~)!NMG z*|@y^b#%!OM=nxZq*t9GiHx!zPRA@oJC0`INIEZMLLyIOSXP*K>fc-!{;ke}aRyQ1-o`+yVjD z11ixT{b2WkjXz$Aakx1X7911LgS~UO%WUG@ee+*^n#IyrAz(b~r@~97b`(`Dx!o7S z%P-5u(QM^5a*Qd-A5*eU71YTmtGdz|hAhH;#O;ABl2-N2LA1cLIJLr}&ADx0@Tnxe z6{!vl4yM*>EQ>y`b+@ARp2 z%bN$wQ^B`hvZ1XgYrH(SZM!DF71hRVRh=6W(|Q#RAVGA3jD&1E!%W@sNHvaL!WZs} zXWNtPIzdN$*?ZO4*d%Y0S;j2ud{w>R*p|LZE28Mpuxn$T;9vh6dq%sWY`E&$u$D6o zV{bTa9CCXF2QyXUL;A%V$k3`_p~I!Zg|U)CUiZjiWE?U398yzmwGd2Ry7&7AzLT;z zCoPhoxFeTj`qXI4OU^Y+#odV+n1e0Xn&tKFl%x$dAoGg+C$X~_e;J9|RM)TVa5x2L zCm5RK63zP)wtEtxr+*xYA9l5ioqi@I?c(C%!p~Od(%P%KE4+DpNW{TAEn^uTEvFv6 zwq>BZZWKG<`AD72sKuC^CDu7<kH8>uld#7q2Jr_0r z_5H}&{_8xUlU7fG!yqpP6?-an`f=KB&ku=nFnTDa=~SfKo`$D8j0c`mjxC(bKVRd6 z_naS_See4ioSp$?L$Iqm2E8TzG?>5UA}6r}0s9ME=hYo&HycM{kU@=~K))}i2l1jC zIuaPB{4D`sNVHrIGnr#xF+>_NL0ZA2k{PJ(cgh>q1K}9b2x!Fg7$fu{AUu{$7FcHg z0cR5$@JSlYTc~{f{ql%^_EJu%NCiQ?!OFU>4k({+M@L;xy(umw_JHO?-j)^~c6*!S zO-%QfDQ}RWBZyPpuDXtLzHWB|?&@8^I&}2bcbr?y zj-&@18+%i0Sor~-Ao4*I@;aDu6O~5%{wA`ch9_9b#E}(ps2Wy)loTZPy8pH4G$-;! zw3XXY^8C8+V0FeiH3}RJQBjeUJTP#w5B~1%MUfqlLW4p*Mbf2VpEq0RVEBa7b1T-4 z`PbL-lK3kN&+>8Wt6&>>6}yw42zR5oBmx^jy;>z&X79Xm6E-(H7PXjmrrg$&PDP$I zU%loLfY_y>!NX`!RsJM)(DkzRdl=Mc@r4~69SwlbdA{m@*XDiEP#U|?XlWtbpE>U& zs2fn@=>N|Bqqkr9R<5WJ^Lbgzg9jz;k!QAKdnFq|R5 z==^NrK1qDUgH_@wUm#x~w4vWgTO5e3I=CXTi$pi$SMrARo)~m0037g zE)LFLKfrnUBza^?7a-~YpQx+sV5iWl9rbJ6y>o%N}xulH{+&eAkg7|l8_L1K&vi;O8t_>C1QRzxg-9t zW$f29ya%hDA4uV!;0h29H?hS0PS`o!D9_dXq>1NS{Vz-tS^#>82e^AhL&KaGS`@I^ z)4rH>>ui^_J+F@#eE!9e>qHfvxy%{pI0Fx0Gf#F>*Y~O7t$9?U8`?oTUY zvJn;R&D7%)^{n;k4?Nb$CVuR-?&))t>8Oww8aT>3j@s%qY%{+|avNAs&+R@NA ze+bAwz!T|hN@kQ_360+F@ylZKI$Drm)G7avkeS)yIQ%ov7w0AP16T? zM@;8&_{RP&ge{gZj4?}%H2#6z^N7xMW|b>u-;iL>oJqNF%zXb3ZT5%D)buL(!!hCEnBj-O6k7wks%-`Mkp1uyvkQF&(_U;4oI}9Y6 z;Bu_F#kcHepMhG9L&?j|-h}sjl3Gm`uUCmHXV!oD&+2_gqYR&!v}cm*7jp+0k%?%rwaG0ExafiIJz(l z#&hpTX{hntqA>n_*#31il^dsn)KSyuTTCdUBS_Wa(BR;p!Ib;B&nYS2ZZbl>>*wDs zb#?B)@@$u2N>iRFa#bI5y}1st85kEEBq_46k>tqfWMR+_p;h%YYO?1>29e{c#~#`Y zH1#QIX#CoCn6!#15$gpt(2m_9(`!EH#hOh{*5q-uu1}Eh=bN)Dyt>6k%N=MM8cXz0 z{yjsEr6WmqTH%(0f&w!WlMNA|=5SF|P;hc~a*DjXxR~AD*%@VHWc0KL9DFv50WQ~q znWC*LN8fkK+!yS%v6ue69BPn{lQd^c%){Gl5Rn?eYtg;egDH|ODEqA<1_*^flY#>xuzA)u{=X%m+a*BMrC|t}{S77!5B5(u2CL zu6sY&HZ?U37yb8Dx12UD)6EX~Z7w;9KvaG5PSE0eC+%OHxv!OQGZF^EVqpsST3AF4 z5s^k&UQ}#wh>Q%12$yw|?R=fs3~Gao$F=0?b5bP*l~$}UJjKLRRR_k3C@r!U5%UhO z)*bS1G*%NvwHa)d&LvP@ z7Jq6S*$D_ceoaB=?JD-io36Gq!Ut}Ad5$llLoL{JbikTWv)g-o^i?8y_^Eo%l>E1! zn4&Pco?$!_J)?6^eVtcj-+r1s4zZQm2)(-4>7Ux!;-x#y!wCIbu3cVov|81+ zy=}L4$;!x>LZiq(z*6Itj{xxPo+9xHn^^CgO_OaF>uO0y8N*{mev}eBX(AS@*YZT} zSRXW2xo!aIvgI6^oTclVb5RBH73`3m0@2g!6zEM4Azt@BO z7I#SG9%{~a7MRXgl%8;504`($8R(R*GSF%7grgjK0cD`96S!V6aXl7Llv70r;S#!m2_MJEFwPGCly=I;6TE=$in#i)jYA1tF7trY z7``Enb&^%E@|9l^Iix?2o;U}e+?|Q=^aVX75c6f*}r>iaXO3r~- zK2-eZGq`l$FRx^-^#`y2`Hswza02ww8yA{gCyvw!_%b0j!ck3t(y-qa$eHH{oIX9= zy)*JX=H9>Z|JBdRb0fv0O(=@w*rIVf*!PQ=8uf?9qZ$pECZwtHaPKn@4T__k6PNFp z=?4vM4u#^Qg@0j57E^gLynhrpx{ED&W4n~{ym81jOm_+~{2A-8B2d45g&aFT8cl>h zjPwxNuk3Ab;_-)pz_S~7@`tnR;OHQS)EVT|H_0${jh=`%McB5^@%EffF>I=NxPc|} zwSD_pFyXLoZZ2+5m637Ks#jE1d?*uZn7EPHw&!JU;LBeYA+@am3MX0 zVxL$dF%k0BsQEqANH41_XNhgg=`Wex^O@SMIv@{GRKk_{?Tfk)7pZ8T7Psi|Ep9+K zQqSG2P_I0vS*7HpZksce^v6718(au=X7=`k6XoO;RcnmEtB`jE#;}=Di#$~m9V%2B zRs4P}r5Jo|1jPw$Is7lWbY^1LqF^e>29`LAo*+_g8MQ_+eqS>J0g+W|t8N3eS*Jrq zhBvj8bX^eeClZu0*UzoL`?~FrFIh&oj~ijkhJ)#S874qyQuWhjbPa~`G4M@&T(`|r zf_KcR))9@3F^$;d?2Tsi@e=iE0sh!p%TC~TY>}c5nsv6Z=tfH_C}FS5f|kf!!-YK8 zF!Eo8;`GUErU;THp<9lm(2+ruKb*9r%8O~~Yfz52{{4y44y1E6Pxi!waQo zuv5dnW5i*Og>2bF^-S zs0an*-zGY)d})%AGQ9=A(9#|t0knMtT9tcH`ndqD0;**WU{zF}fYMuVL>`R8<_N(L zcUTXGVEN$7{%N*IeQW83-}}rDLaC}5=NUi;9k25G4KsUsGVoik?XiL=Q-X$$7v8Zi zpt9)}lIZ?WNVY&FfWYn3cn(fhR3_Quf%D>jSOqE69@D!06Juq zSx`Yqtb2Xxh-Kqxg-^*=s;l@dSf)|(o2leE0pSi-Hwc1v8U}!d@m3GZ1Ky9n*F!+1 zZNfwjPJ1~wh_Z?g2?>#HBpM$aWDo*a*m0Y=ij`OQH+PqNdq8{qNyE{x+GFkmq$tPJelcq6J%y_JE}8SOy?T6 zynNi&E&A|Kn&onSRr7P>spjt1SL1U332VNFqs-p1D`JB$wpF^x{R1LQzyg4Kp@{v- z0aHEN?}CvVy0E?M$QyJjSs#&huaB3U?>mrh`KufD&4fwpvG#Zude~b^g{o}*bnMYx z@8*lWx)zQv9a*GjrX48*@u~!Q1b}q39T#gIf=?SyYQ!TfW!|qWt=T%+_1|MG;DQ-! zrBJYcLopjJ)>OY!HXp7vl*bom|Lu0<$#pTyT5(V4|tz z0;Qcv(vja~0JriLB-O>|7PgFlKjP~FuQn;HMp|O0o4_^B#G=ekCXxNg%A$yk9X)uC=c%L?wk?Ph#OU(pB_Jd!&$8|k<>4!Iu_V=0P~ukiqOVT~Rf&_tZsBYZ#c`56c0c_BOfC)}%+3emef!>kDCf&6z1e z$lYdioVK*|^e#q~!BuO%(*~QffhA@^82uY4TeZo*2^niE<)cYph9f|e@>RRnU?v7~ zWW1J9e*=%X>d*6!AKf!P1AoVy;@8f6M}2?waOvlWM7iVs`{p^vII-AZcb+jEOwKad z$QBYoWIdI{acK$Wx0QM`Rrd#&Ykspv^RLed1en;;;&`IGhP zVbx+H@Ka9D@3>aTb5m=;7l9dud#$+f``rApUn#J5rnhu-5j<9z=X<+^v#$OP?hT!2 z2zh9jD0EnAts3J9L>fu_6PW!D!aSa?y4ZY$7#_K9NI4=lWZo+2`&lYd$3y#l4%%1- zBonA}-;KN*HeyH8n%_>5dedjAxy`cECD_|CsND}!KWpWz8&kJH-# zqU|6FgwXv1GR=pD57J=N`(r_ot(CjjX=kYYU9M9KzG0J-MRI2lVy1`SpKn13*I-rq z_#&}e#a0#S%u)bI3z?NKQ_3b5K8{%3DHxWQNR+~xdy)g*mW!h2=zW|Kxj#8veI3ty zLTZSXXq5TzsKR$NzeeKAkmxr#(GY;pL;mkmm@>Ga$7eS3yGi^DIg;T&J$kp3F+83M zW)t`t_<6TSUn>&H8fFquSHcvmcRq#Ugc7GvI$@kV)TgubIJ~sFHb8;9^0I4;m2d}1 z2`{wWHOtfgS9HAtAdJ2%puK(9XR_~}DB3|y1!6*%~ahF;3K+PW^5 zxy){&hj}92@r$>OrvQl>3);(jrt&jfqnX^+@AE!h{(X`5^Jv!6PvC%hsqLRZ4TYB7 zkKMkZbM~Mo*JPF8rF`ByIoV8WZYn>(T!Cu=7%HX%&v|)X*F*<^-UdH2l_#1ZU3G=$b!BT&$ z%+h<#z>WIu=7lmy6b9;w#;xYV3a(5`5KP}4{&F&nz}CbUd_^^50V4h)C?O$%uqXt~ zO0>aLlN(5>PwYX$#O*h5kFqP%mi=o4VUI$Y@L@p zMBG^=CbqJkA~%yiQx#YJxHeE6!j!a{wySkKK7D8mmi$cr2U3=O`PBP@=b={yu_Sl@G3m zv&t|0z5P`Zy0?C20DSj95(%u;-Zc>09)*3*fJ|b4vB#TkM7rEzTEBk1M$@_6hFleA ztbfGgTpIv{UYd51l+k!I>_7iLi0Yro2Ql!reDy}2aZFzhV;V<6c#j%!d7O=7dnM0bKiWLoXyN6l;UiOw|-4k-R=cq z>U$~;p^tDpIPoy`SS4UXiH#da%OcDZld$BeP*bcFNb2>s8A7Xe+$2wVG76c#oG&km zTzHbz8kF3Ss{hQEO|Ks0_0jG}+kLamP?Fap5G&R4U}bxceeH5TD-Ii6e}{(suj|>? zU7dTWnN=+m;pXdbZJncDxABN1CtPKVuSOkatkPAu_+zaBD&si!Sb<>gTF!0BcR zG8by}Ct|dRfyo@VP6%$wjTd4FRfk0<5%}8&d|k&pu1C6wH#EGwZdZW(s|skgYPx=R z-~#mZ+W|1p**xj5FbP91G+FeSviHt?z>ZYWU|zGN(6zr8^NdO=bh`**NfXF%*nUp| z3E%cp#iBYZg6aHY6Pf3Nxi@QwAs6oRZc3OkXbxJWja{YDRrvObc7OTYN}sZqhUTL4P*pMdU?%GHY_@!9 zyQ>sou&ilqc=7TyHR0c=l#`RRf?`3R^K*j2>-H3}^O#oRscdG7P(5BiuU-VEmrtTV z0H5U_2UN|9AcWH5&+qR}hvn5x5Ou!+3=t>kH_W=^fW(;!USauM5g&8V9vQXUEO555 zX>7bL+fwd)kfBs~d{8p^l}#cL!@1xOw=S{y?btYG0w-&I+HJp z{?en3;yLleq-F|vm#+HW+geW-#SF=!Ml8FF|K&CM6+PA`$&KoaJyVQ##Pu;aw=7P%#>sK*$={h#=g^2IiWrdP z$ZuNT7aSfO4vPEJ36l<$aT0X(a}gLHQ~&wIy{4H+qhQA9DT^%qDJqlej$71Z{L z8k)PE7uaT!;(lc;rwpTxvdv~9Z{YR)q*Sa(PpTAq-8!%fG1 zC%SR1`HRRn+_+DmGT$(i`D&Bnh&GfW^-hSId{WfG*Xqc-O;?G+oni-Rz@QHfIm*3X z8RdC~A~-uwW*$4W{|rP4*n#V~zG=fBiIs(=JalsI=^jAV{yc#sC9&zukH5cl-h$*K z-ukv4&>wkz0tJj&CA&-Tc?Kmru{ zZ*a-q{1sG&SuanG>~R~8kCeubMvjh-TG#p!M>KW#W>%(r4yK#Zn~^Zm@I#Nvwd*5= z?*FJQRPL{`-VJ+A+;fSf64|%tR5TrVH(hLA>X#y~()jA#wPbmZCD$;p8Syk6(kxvI z&ti0&4BkEB6<=$jBil;1-^@{c=E3NG`+4>VaMJeH&Hxki6fPYQaCOu995>N4@SS)K z1MxW*yojH7eg3{s!U3YtsG{|H5jBxcwapK>DdJrxarOu!)2Bc5>v3LYs+guBVv?rm zuGDtIA`ikP5C|<-bB=aPS)-m<(x+E&4HZlZpdGs{NPfEjZ>fGet2TG8=K;grndEA3 z&uGSn(l@3Jetn?HF$XRF#df0V5D;xChkkT>n>65%$Nl}h4a|%tfx~luB0r4Sb>3vM zP?hXI2NWUq0M{zkwBH+_Ra)*Z}m%VSvKz@U@iusMXc4_74`kb7~T#)yZ1QA9zMo%>i;BIB4#6@d~96O!2kY8TE-8gbyqc zd%yo=c@~CYf=+73C}c|yqL3}GNik!QkI06j=3vzSOoUnN7yqV+BAjBNjFB)CzjQZR zGYkmPQt|sL=dXUh-6lT9GOmrKV0e73IxtlRL3JYRt*=j5f7W%Kr|02-IBay4fYDLQ z6m3@iW>!{~a$L7oiRRtkSv~t>yViiu(?#dkfG9l7-|c!30aV$_>ZrWdliSD%QzpU_ zAPy{5=_PDRJ!%-Zw;!B9ZEkqH+{86kY)p2(>^vS_CBOG+w(RbJ<<~N25turxIzHN> zVUdR(pZf;l;|g@;HJ;+Ck3CLa;C}7N5@k;Nu){9sq@GGe@z~NXW=){Ae`IBBicb{r zGn!~XQB|0HXE+sa1ooHB0^?(7R6OtMotmM?F2#b`O|yP<)Y_b|GZL_m?nn3ItXq zfu(SjeB*+L9&dqjLDuc{4)DV*zzAvCfPXN30bD zqZ>R3&QF>fEf{_`r)x$)&>ep+Wlg!|vmSuBU682a_+EQiLS&*E1GXV^uHI zZ%OVq2){5*J9z#)T^Y_yu1g_Dp}wDa)dh@-s9)<-0q3i zOP=Q^C#HR2ws3eg&^F380e}HO1doR_6j_^JPBWN28!Q17w&+s3-?NGK;}S6W zQ-Iuv*Q>Rr$$d>=RY}NWb2|?t#mhY|@m-986d^!wfA&ROJm7OC{-|Y_o+?MUiC%On z`7Xqil^Z7@L4ur}L%&)Ae*eqKzSG{QPSYn7)6vB%r&(#l2DhU?N<1|JjLm=AVi=dMFUjPcCUUoUo<;cFzp6M4H+ zI9Shl*_(wy$wN$LtGmdnT*Q!VB20og!X1iY*LN}n&TmL;%4p=c2OB&O=;Vogyn1v$ zyJMflpJ8|r`ni`=lP8_q^qmC2(=sz4Jw#AoGymjiqH7?eZ!GCyYT zNN&1h68-uY|0fl2=iM8Cv~@cGk-n+~5?E1~z*~@oAI!X>|uc<;x_MFW0^MxbThlRv(eQ_X=&{ z)+H>+pi2Rf4Y|E=ya%)8E#Cpi;%NF-mO22x!NqBn!=70!NqWYC+Wo5bFBK?6D%4vu zqFPQ_`7hM$6gL;;yl)4Yo6fJjb`mb1sa7hR=4nDE*2VC1-FJ6terk{`%-+%$^Brm} zOrKZ|+B<#uo(;!aYvKIkpkNhi(R3xaCJ3KZr((GwnuvGTR5F}}>~tzVe1n7+Ce{;Q zy6OrbCW0ReLuf+TAZHT%An)T`T2?sA2$&BwPs%rQ-b>Tg&z`4@JXm6Lj5uL3ozcTW zI)mWQjnhvUdWPwj#1kff;4?o9eWllgH{AFE z3w?V1*;8obJ<2iew?pQkyv9qr8_W0xwHPcxH8>F~9It9cSvKf&*nLcH3bX@JO@AKu zCw?kpx-%?`*Nv#*MQkvnrM(IqFosv`-QiLgh!mc!7(I-q_3`U`3FF^!L`-s5R@y1P zetl)X$?t`tjmjYuT8x&EO#SY9u}>k;$|k7=1Hx05kbFTs&=Aa+({`mq$u}fHEXnN^ zVdD3T!dd+jW&gD&ikW0U;B;HAE@y@t_K6nUcHxak@BzM4?2OsKOqbTSL{}OKCa5sX ztK9-#bEeq-MKUdly@2S5O~*N)nWn6-VL%#C&`pu#|4_B{@~FY)zq$@h#gE~3DXKf4 z4Jkf1Js_camXVJRk>NH?``4;_;b#+MBO&lamW)rzZrp+UlAWc6)AI_Z22TV3YdG+0 zyDb1ec7t*jU-ca@;{G{l=&%fIFGoKI(S#nhz;h#Vi9UVG@c`22vvG9Z*yQ+cR8)Zwk;0V|LHWaoabW@2C^8tsRGIK$hYFpI<&ao>+oypS)~wxg7jO z>W!}acCf_v2M|scR%QJFxB>>T8y|bBvd!CL9qI-_q!wxSi-9=PA5A@W0Y5Y5(o&Y_ zyBdAj)?-`I*X+ioRf>DO zX41zV`jS`2cR73u=sGmnf^I25{?PQKl`gWHWW>HG#)+{W4b_{$k&$TcGY&JcShbX4 zOEB%l{1XAyw-S+nvbiYg&%}Ym+kpnRQa+SLRw?-43RHWT(vL1;!SViK!NPE_ct+N^ zTx7M>gzEH(k)$ZY`618KY&|7j&Xt`qyslDIp%i`;fp|>AkyTr%iQNY+49Mmpx6VRou+cN4}AybGo9hKI!+3_1?)&inMoUt##94q#2w z{xHHdP7Jc1$UOFr?ZbYgAh5S-$(aop5*( zw-qCziEwj#@(&IBaMnIav9jfzCChu3LpHK4#$cv)VVR+4_IEt4_~h_cBP`qygCRdr z1IBjW+~5mG=%H7pY1#FT458TRf3Vb0@Ln1-A_I=BlDkn_(3`=N%0UKkb?e!beY@SW z&c!|&*Dbl*+4YL4iitu$i$ipQ1`in?&6jlUp=B1^~? zxer8hav)&Vptq`i5-LcEP60dJs87KC%-DLS5i8& zZkhZqy52IZ%B}DE7DQ>JLmC#{AS}9(?(R=8B?nXgT8tD*eB&FWTzV>zB z&s$&IhaUUGTI-BC=YNdx8!6(>wyH5WWm7wz$umkMFPuAVD${qa_;Mk{v>wW+qA!QA z{AVb)fYCDv~ZJPFOXBM%rrSEIWMc)SXT1I>sopbzoX1+303#hcwputSc#?bUX)uoC3x zk@z+G?<~DG3WBC>4N2yd_9DzTkZUKtMhwg5#YEbd`P`a% zE559VXSx&DDJr#Jtp?XYX%!7Xqq{OeYCus==fYMTbY(utk!&e)y>NJfiGDD$r0`O9ACHuSHbZ%L$q?HNCynuz`o4>CnbWq^`8v^%` zV>{9k)Fbq}$qFK=d?A{uXZ6F6_Pi6u_{%X{D64-?loo%%Mp3}5ZsuR*s{$kx%m;Bl zT9VVhKWGOK@PjkWJotRoLNEsmIwGXdy$>lmZOmjbeG&=|00{M1Dt?zL{<{=P#ySW2 z*RPZq13_qV$^~W7>)6d%KWx(Q73gzxw8tpXQ}BnO*O6jqi8PX{hl^51nsuBkijzH^ z8H|s1C7llkR^_ToU)E`FUc6wxjN09P7|F|MF1<><^K<-)foP}0Fhkm<)1tW`&86qu z^Ns<=SZ)w@W9;`C7L)!QXUQ`0y<)9C9yMO2bw}A|^$D zmlkL_r$9%GSPdZ?24D3vE+>U7iqFJb3J+ztr9qc;*Bz~Xs?Y$V6vJ--^fjELs->$H z;DTFXk>)s+iq9N`D20iq4l6K6{XMXE6`bu~zqzsQEhg9oRbU2>9a9#ggo+6n&OGD< zJil;n=AxKN^xBB{yS8s{kW0R&J*HvB3y6ka9aG`ZW22c8=c{-zJNi$ zI@0Q^5~FN2|4kS=p-0Mv`BQtkdB0WBnUl-I1d33n!lG!-({Mb?}K^O zOM}Ih@rn{55%E7Efc{+gW=;8k8WhBEoK!VVw!0t9*Yo9hZbThTPDEkRAdPJ$2)V5ZT{B_;21<^KwMiB=ylH&u%i9B* zw(W_CfV;nqoe}okbsHZqj&wZBDZwhe33T^I)D!juF7V9DW(`>aLm7V;yi+t*x^)R zM=o!5BTgYffddwzL#3S@=NYMpt1jfW^5l$6*cBQpWT&co|NCeU1#(;s!xwS>(mZy~ zAm$T8vZ6IDEFZcQ4x4Q?Wh(w`3e(sV;mu6=yS{!yUXkiTfS*NNG;=5B)a%5kdEFV2 zM`iHZ)+?H=S*j(xH1n+e2p=ZOll5N7JOx(P{6=nY*((}U)2=VvRZD8D+{4cm|9u&M^^)9scfW zMy+FVf0IushlD3^vG~}UUcf_z8fsG3@dLy#^@U-JV%zzG@z_`veu`4O3qDXqeP)?l zf%r8U7csC`h%1CD*>%t15ONE{@}0EbwG&36kyZjc7BNs>0bWpParD4Q(UIk@{3AKw znc*s#h|aqopAJ;bL{f?0mL+mfN!gCXC&0vciL0QRr`BQPkG9Qgx|4mwD?X;gn#;5+<)M$CJX zt-eFCF!ik*PAN~DvT#BkX@YTuyn`2!Zi#nFk%QPE$dcWiQ=K+LS5U3Xf{EvR@H_Hk z=UMvQBhj}y-;fUs8wf<>Q&SmXLQH8KEbSt^xqk1L-BmTqmAX4^UqzVz`N%ZAQ#G-D zC8RlVStr$crqJ;4dxT1_z_}pLQKVb#{X?i)&P9c7w`VBPQgIDYy=u@wa)*E(vmV*I zM=f$yQbO{^(jF3>p1%o>A`e_O^*6%?Dcwn}*ZvqcurASZ{?rwKVLAlNh4`mmpsPQ; z|L&EV1JA_Sc1CLy70pvK`P}cM;yobGJeq>h5)`QHof+r_Kl{ux{+q2fy1;mi9Uq5#X#DcUX8&)AbiEV~?XJacomk>ib)tHZII3X6I}Z!F17;(%1yY zyS44{iI3my6Yb!0)XH9?MU?a}ZcpERcXHnF!jn1t@J^;JhiIY6SpT}w!vL*|%f7Bk z_rAk+YKjFRV!)9ULTFKnb3-D<`+1VpUF#K2(rr$+11p?Ul%h;A+5x~_7+xJLxEvq- z?9l81?lUtmRfm<AOON((@1*XpAi>TPDEyg#mCJqgB7b+G4q-z5z|ITpYAwh*WU z{(;mW$Au5D@XE{N>@CuH-U<0a4MdZGwFDlUV4;^1o-Q?b1-5SxkmcYEVXv}#N= zrFvy5CDAH94Olwg>zVBViW$wi5W(p0|hP*Nt9%ao7@T38|N`Gv4XD|dA?`T zzrgg+ImJs86vAt5u@(b8UN`LBN#m4Y3aHBpZ+<@@JE}V`WJB!mdiN6}=D<)FnW~pB zd-Bx6gUFp5D|9~{W^|Qo(|eM(c6=+^9X(J+gy1%$B-D{Elku<+aAF~$y~Ob<=J~r) zEpJHh*2sklhS~4P+|jDl zZvREWbG;T~K8R3t7ZcA&5pFz_z=>#m6%oOStK$hb&`%-uA~w&rEu1V_8Ntx%470u$ zMd+)c_-tl_zcY4s)CuHkj$1?RA!`O!3N{!JwEF%*TT{mfqQss8^x+NAjLW{lkpBR5t z|5Y;UgQR~X7jIB#UF=Lu{pf`KsaDU^6S8@08L|vZE8-gTsr8q|>FMcT6kQYUtV>B0b0d z?;gANxmDYGIH4Js53yTL5|0VSKI?KnsC@B>mdD5E#)*;yM zWh*BaAlXg$p=x3xr-bf!J_UW>fqUbpq2I;KYxItJ>HI>W^RHst)3Pja6~2^Vdf^TxO3cAJHffH$^2SC6M#5h1Od@pO&b!C|uGwIt-xRGMZh{V`A z1A{!G4dbcxB#;6f9fm6MIc&*Lbiy+}3s360J_jP?qIufXw6*Q93|_*-KH5-0I)-Pz zxUoQ$IbW327PHKnpe4~deQ=kJk&v?7tFBqh#EH}^`h-I^?c!NkTa$BI2{L4iOfEZ} z>*AErAZGMhXE57t3q1x6&-7sFPpmf`kpcQ{vG2`U4KJn%{#hje*s_DMnWjEUpX~X; zg6ePr=`m(I=n4Mh3)<#u)WyL7eCw$_5sa9%z~F$h?f!B}Ke-Jk)9el_{7+Sk`VFlC zj}J9xXOWcgj)=S9g*;4^P9Cv4`GEnfy?(rHDP~h#ek1npV3i1+J!9*m&KDVklSN5{ z#t#?B-_|GVnVdoM|K5Y1zA<2UaPAXA`#r(}TWg5$%lrD3W`!DE6F{nj11k34!wh|= zl1|+J+C>%>p>1asl%W9vi9~jjv^xdR(~g^Mw@nr$Mbra)9yN} zU#y>8&P=*YE`cIU9wG-($DMjL2@_&3ULL%P0<@p@t zncMf`EHg)uPCHUky2JBy17jfK)oS1_E165KbMBK<<*0e^B4m3Un7g)t?J^w*RbxOg z08g8q7gag+;05*(&=$6V19vr3TE_TMx@c4W{%+XKg<*%G3zP#n90Q!F-K$Zn_)-NH zQ%;zml>sw8OSeR$Y`9wt19pP1U0s&hxGc-dpH-b3V^Gu5|Dn%6weA&Cu+AK$)`!)l zu2DtcW!b)BELPHJVEQpky9u9pXgYU_g9QVUr>{a_4j^~AeJeX%^c#iAUl-0u%pJwq zwWHQe6skyS5X{J6!Pl_#1TyH^UTGe%x~S@LQl?{+5iqg&9<}YZ8&m*CbhmGtEJ_B0 zrULk$5zPl^g_VF1es;Z?FahN1W6a+4Hx|DV`<@P~a+$0mfNDxOC)kfU0NyMq@T9(9 zsnV>o?Jmn$pEf)TonDQVv|ORoH(oNPC;Y;bFr%{E@GILz7^QB+7z?HrB3ZuTjVL^K z1=1@TdMIR($|9t5oC z(ZVs(r|aJU%n~UXHttR|Z%{|}Jyp4ToG=E4at`Fk!(Xw5((1~~vY&Le_00uIfti|h znWy%;X$&kB(&F%gJ@k;eaprlWr$+V8Ybq9P&Ks=5kr+aDB$JkO-0iU;)VONwWt96} z%>5_Q$`X)P<(W1JI~X}y!uv2AODuiXJTDjP8h%OD+W?nXA3RcNMZx*klJ{0m7z{O zg%T15H|W0Z2k-6_z~@IqTnA=^L+Zj?@J8HyAZ@@E{RA8-d>|Lq1NI=Fa-7HMYLL!A z*SL#xEzlwxsSKzUa(-cc2KMD-usSO;x30m?y@zE{KcG`0zl~Z4yI6Sx{~Pl;d)Or; z{seH>?O(zq+X0>fZPxd(4-g+NW4N9-9pUr!Uzpdd`n2}(W2CVM4pGLkjs3URQLE!|MwvVQp5mH0OKvsZb|)B`0i(4XllkMiI!bF zaK`+UB57Q#EJ{;o#EkU(ogYk+Lb8A@D_PjOP@!?JlP8X%OI3#fr>`PoRSf4GAP2Oz zg6+_!hyj3FUHZhr4OCtaL68P8OAxqp#8Z=E{^Yu+a+MRXU;Sye8%VKh5AV2%K)1q7 z#p#euCmh=B=|>Cy?_Mqw*e2Iz7+!sr*2=J@O|om=b}YZdASRhJJS`q|g**0Gkr-8( z^=`p-Bvp65_V&L5re0!JruTI3?hI>0l80wrl8k-9n|{smUVQ)63KmoOe=}i&5xoo4 z15JJ_>sRM`BN`?$zB8#6)7LLHQY=Hl3>hJw&_IDy1Fl~1K3E%5 zD{H%Tz`m3F$rpOdGa^E?NgB(Gug;?TYt?I)ID>WPmel{c@2B9D<#qXNrREe>czu2# zzZe?|Vl2>h7X)t)HW!uGevYT#f|ulqT>`|7nAcT`#v}Q&^p+oq;<_cOb(Am@LSKwZ zrnuQWecdL#y1U-_tBt>&rr+v3XlZDm&`+H|=1V3o+fm<(MuckIHLx}vC8QdD7@u+W`m80ya%xuze-O|T=jD~&TgxS@{f-eaTdukbTxK~b zfH%DC3LH?JU`!jI;35KU*jY6YZtMeF@D+nb3)IVk5)pDDWR8ELUmu`Qs_;jD8_@G< zs*nDydpA76_%B6@F_s~b-1H`@KehsNNxE~K;rD`Vi)UWevLWb8NXLF>G3Q$!tS;I;@g8O%| zxjvE!#om}Uh&&_&Zs|x_%S72YgcrEz$U%XNO7x*^U8L{m^=7gjbJ+~6aPNBJXf;6c zA_SYVjSHhUE=5^#osrLsnDjTkBd~w2_^KsknTbN^buiyTXq^W_*+;x+1nF4Q1&8^~ zUmWQM4q0Y?jPs=p3E#*{SwwJQqS@>S+{)sieS3f(EwYyEsKpy;900A)#y3lTM!k{X znnRvRx!~wnbf7kD&o9l5+Jy8^?;ds#Hf3+j7s5&8FFkU6pRSV|N?LE4G!rDS$IRbz zBjw!6p#?R0h!Qg1BWUp<)+#zG7d1TeCP{61{s0+#9~3)N^&m}CTI_zP?o%~k0|Hq1 zGQ!EzOib`w?#=(?PAaVB53)iCrxa4eLyNo7KkOky+S=ByG0npaJ(6oEw;8N&NxgR% zv@SnF)+2yp=^pQR-KVbgbhsD$zWT{r!TI(1AaJ;&{^xf9S?v_`-qJ_3qQCgr(!{dHh7}d1j!55_o~!$$ zd>v9j#gjl8k>SQed|R6qN**TLs8WlPL&rM^p>QmeCVD?e&(T3o75<%F$nk|@c8&;o z*C{ZMjzJ1cOK}iarg2Wx(EDj4*6r6w>#!&^MU|XeT0z~y$lK?&Mv*I?oxwcmKYv{D zR_s2=px}AXpI@s{CZd_(!`4@yo)2UUWs?)mFyiqdJ;NLnpNJ(ujsd@^1_FE*p?npu z>Wu)jIl&JVTLMMQF{Nvh&yufSQGx5*%+AMA`45B^5-B(D07 z2m0> z(iL8BnqLf^QW$yq*z5wM=HOXMhjoCdK1Q)$yqIxpm?g{RccQIUvkP6IJa4=uOBL-X z{viKSa_b>8sz}dxdyv%gM-Au46_3{&97jRiW9Gq_15rOd)R+RVm7-r`iOm$xbNpJJMSqM_g< zAWSDr7CBc(6;2Q@FKnqkcyD+s`d3k66`ilsjD4iM$I zcjZTZrjVdBYojXsHQjlhA?7WsKDL9m!-ByL2PhVE&h5`btYlHXXSiZj9dO^S7`=qY zM24OOMM@gSO}`%EaJK+kg}0Q6NJFe!5StlzaX7_A&qb~6Wk+vb297D3HNucExDl^v zh(G7Y?Y-_G2=Cq;fr|%rFPA-QrCii(R1jaN3!drD5tXamhh^Kb>G)v2_m5u;EQU}D zS6@O$d~yia0=ZjCAiTH&3gAlqAH||^2wJjHS{26o$gziT;`QWi9_T|N?FSc>A3HGz zM3la>Yll87c2r)v=y4uY`#JG`MjZExo%E|0{y}%oA$g|IPx8weYCZGYeo@joBQ*}b z?lFxWeBVwp-mszAD@GyQ5Y|ETUx(y`-VolQFQj#D!Ccesr?Mwl!+*eW8uHM6yB5$KdSwRT#}@H%y%xWi^KI z_I>*lhmDDYANp=y)^QKxhDAgz+KTw+S|}*m9V&s_+%PE{$zX<4nu2mhdo_$P(-*+M zH!HZ*jNS^E)xvybGxj=Tw&*`=c;Y(3ytwPPA3X+n#t-t4Lfn=Stz<2%Df8>Gf}gTn zu#ow)?kN=^FY7lqCJjl^gtnKQ0 zMCNU5oI`9EQUHA1lKk7{{V!f5=dI%7r)0({BG<=X7=MZDl5O0MMZKvQ`8W1X3tNwl zkQ_Prjz+}qG_fC?aO0I}mL5UsK5E9}Uho!OssP7?Ixx%zzdpqy!a}Ij)P_6>u~D`C z_Su+MYb3vA6^(=20omh}mU!K}*5C!ZI6Ssl3@rtB)|Nb~=I0&7QN|WT%kx8D+^&Pg zQ2zM0viB>ztI^qoGC?(*;xT{1iWRaJ?{eeyd-On?2Nmtbj0cEt}}e z74(}Emop>PaO9|TH&2luiD+H3aJ}vGZL08NCjLC9q7dw(-*>H5(oK}!08C>Bw?nwm z#1c%U3gtqnigf7~vWhft$6C7?OyET(WfXUsOkmXw_oUq&A!+WU3Mb6ICDM)dj5NuB zeORn|`RCbCSH%AB*CYKK7$f65=B0il8`^NDI1l=pMGqZ&c$MRtgzBS3)b&L^$MM04 zho?&Yoa51geXZWv@I3z9^ZQ7aHW%9!4n>MpYFBs7U=IQJbUywyQ*giT3}UmSK7~Tb1;| zJyEIqr|>l7j39Q;d{Qg`GoBVeMaErCxC>6>Nd$Djyo3h$ z*HVEAe&XMnoD?wZA0cvRh%`t10logJL(v8Z-T&S?ce1QmID2Y!~9>c&r{pA8_#yE0WQbo+8=Kz z*37KxZQllZF!bnBxtNJV>zvZ zx(o&y>;Of5bJWq_qywVxX@F}X`&6t4x&s@4t7GqLLC7+oAx>9mbL_2jb%5hVdlSY0 zqg==F(P=E62{#Li;@dbAm+IF4COt4xXyx~|UX<}CoeG}xK6jp$rWuom){_fwfrwPP z7#fc_2(@9lbdFJkE0@WimoJ|b2OWf+AYkelfz4si%GeZX36`IuV!tI1aD1UJ!1L&=R$>>mSa>G8Lr_ajSeB(6UBs?%F|I{n`onY_ z7gnA>cX?U0b7gLRIUn=wi_fpxsKUK^cUA@plYe4{{)98wW%NC;i8RKQJGn9E!JW=1 z(W;_HXfY@_5*oUTyW--bw#C)aNV^e2W=I^fqXt>KnvZQ(m;^+^Vx_Z=3nRto1ArX8 zT^Pfh3?hIstKEL)3k~M%et%u*G;3HhZh+^W0iM`Z$yx;kPx{Gvdw27vslClMVKP%P{Z7RBZgw+dGX?&>7CWh=$>II5M;eA-dC+p4i;by}{;zxnk#wW4 z9Zm#HjXQ!+1w+s+mSC2qGH|~s@5I=V+=(<#%)W?!;n3of?<^O(W$eQvBbvIj!XvJZ zAaa08qC%E~^u-G4xjMD({)_^Jwq@pQ3N5VOE_yCpg*L8$tx-p>K$MRh!&LLQS3($E z3#T4DDlx)T+rjV#r0z5Sm`%_j5OZq^#^FK1&c_Oza|WY7I06Q_K=7fxe-vRWK8rZ^nCSI`ah{+an5L##eiwuCz=_InDb% zh#abvW|B;*s5IiadTF8NgH9tfqMB682%#6ZU>`>232lB+oPsJv^&#~#_bJl0R?B1&LXLSPYqHW%!|{nIT-M8S zx3;l%>z)wdckd!CZwa852-Jd~g6u{Kh6gxv!vam@%1J5^u%n(KE}Z;rHt5IRB6%k5 zo2lHo>0>{o?&If-rpSp*Jg9{#rQWvVlAQf1^&2m*H;-ISHKmC+~#?UD#uBi+4kx?$rS@`V{66< zyVD12_*;~}h5rLK8Vn1B5`56)0ihN8O_wVNQ;c=H-aN-i|I(-H6N1aUgkYu8-XQR3 zZhQ`oq-+!4fN3)5nZQx&=2Nj=h9v3G`bF(1mkIZxr{z>G6X?^dsC=EvY8-so9dFoC}u{T~Via)-U-t2rw^s!UGsW+2PWX z2c-oun%jjD9OHCi;72uU6cKcB)wG&6K!aj;9{$P#qp(Q6=vm=!Nlry!ls^P3%5Y<= z?i_Fo5LWRwdxl+Ln!#w}y|}&DuK}ON3{wzmlev$*k!kp@GqSqWiDHm-jxI*|Iy^-R zwgVxxe+qw_^?t4WgXNK!?<=n^TJ#DXssBD2CqQW=YH`Q(uOo}9k0tw?;}ohCUx&E6 zJheo;?mu#bC(m12noWU@08>RZ-?n!}iRM~|s{+SP1d4ZJGV`?tPk=16M@^#J5ad<#QdIhO*TV2R!KmM z8V6#e`B*X)+ekLb3#P4pBvMCoR7patBTv>ax$P0ujc@_aLqA#*v zV7q_oj8?`GC0k%RXfmCNSm8P)47;PV@oDP`pnZ1wrsUr9xh$euTv=uadQ)O&tFy;N zTv~T<50tEjtetE)8tG1}zCgG+vi2Rd8D|`x7?Ytl#@b&>ZKAn))YNl5ESE3v{wA3T z%Kc(>I+ggNMvT^4ImXH(hAhP#b8iBcwp=vWyRx7D@YrB&E_R8n4Iy?bV_lsOd6^n+^F$$q!N=X-%;C6{ z&QGOWqlqB*BacAD?xPLr>buequocp5cBB(RR9U_sy)2@$Z*oove|z^IcJEsN^JfN>x|2RU@DN|2)hK$SjbO_)&lne<~+#a6i<6@(^Oga5Gv9 zV-Xw&!aU!KAf0cz)m-a{*>wa+1+QUU@9lBX#m&w?lTWPTF(9Affb9`kxOGbPSU{UL zo{p^EI(mD`CVvnaks+&K`=wG9hSE|=$eUAlC@ov818r!<&vpf3AA*Zj^6t~T15dwh z;%8V0kWRU&bBrURad~u3W(`zNzM=+uGwdJ(A7mE)WvP(QFGhOy|0uroLctZ|zu)QX zvur(?#1u%yj}J@qul=u%@XzE)vR4*OcN(t)q4v?SiM&DwxMZI#-pdIG>VJ$39{uxN zxe6Q`so~b}ZK9+sO%vvp9m(haU^tRp+ca(_`^x**%Ui#GSAT00HEp74Mo-HmULFtS zg#{Vm8aFABL~u`hAi@Ac2MLs-v%~d0ib`%;i&G@D-z5|4rSBnf*S{BgDY4V++Hp$1 zMH-J9C5x}qjg$I{cR{I#`?%aA?Fng-l?gL-f5wW8KH^p)x(r#EH{?uxz(^ng9^LatmP z=Z{J*t#9OQnBCbTu!TuDWSy*bjs+=<)>guzoKk!u6W3A3e}nz{zfY!zTfeO)EN5mt zwCLpPLNd4$P(B()LRdK#AC!$3YYI6|3r}ROK#Mfvm-O-GSFzJh*3~y}F%aK>o?E3A zDN7DuxLafLMk@wZ@wIxt(jGbHbMxbEe5^ExL&fG&Y9aT>TB`Ai=PSfAgP$#YF=X0mH4lhkG*1>l!j}SNIa!NkrG_&wX@kMdwP;q4B!iK2y z^Y#%U%N0^bj=4tU;lI84Oh41e268#E6?o3Gg!t7-qx3p~LsF6pK1H?Y-Nm$CLMU|< z;nH#rsu3B>62{B@kg@2nZN?F#O(jM09hfDA7F%&-jhhpW%gG|3rGNc@Tv>Lw;KTtz zmDaozd`X;mZFiDijxyLV7sSlU*UhJa2)){M&%07?LYmE$%~*V;)r)W~B;RbjK~bM5 zw&5o0){(=Aj1mUN&DZ^cFg+RV-5S!)p*lGIj$y}qu|rdA=~%V|Cpt8aPGZR>mFU*Z zv^#6j+GNTm_$lWRvHi2|Fe$F14t0^ZRf$~`n7qp#4g<*{ydgE=#>PQ^7x!;tXNIbG zJ`y6oKULVMmK6wlV1*Ni-m?M;14gq8pZ~o-mEgwFVUrL{!V@0rXi~A6#dbr{II9ti z_WOOv(?i-%k~Mxc*FQt(l{_@5;-MjEc4Rz)M?>s>o{bpla!`S0GDnclrz4I*n++nS z;caV5JtR92QLzS6y80|UPFo1Gdlh9o9jwfq9XW0fE8dadu$pfI#kiQ+!`$`nx7oz0 zqg9yeHxsK8bSax3*2SZYtMH{hyGNIb@cT=T$ThTD1zEC;c(cJkTQt_Hu@sFgVX_WT z`F)fghS_c4uY3gHRdU-t;1@jyHz9|Zj>i9a6S4;eo|3dY46I<762j0`U1FH8@qZ;x zr-e-%c%ND$UP?CbdJ2UB1#?1w3-V#W9cvO%LK_w>k6U3;L+5|Li0YDbS_ZgnYBuwlZbYRZPVn#wUio)Ka|FvPr}~n!@)pxrMu5 z9aBXs%p7z$eaVi3xgv71^o(>dP&ffM3G?eILG+0g)P^d2^8*#=o&KMBWVA4WXm8@6 z*{BA4E6SG+G)_u%VK(1jX|h}l;%S9cFSxbbT+Ac^iO`{e{Bc7Bb9Zh^i-Cqeo9qe2 zu=xns&w&Tvq~`;~*&{}ZgS1>L)?(WGmyW53Q;(@~{YQKz8W|K9)oc^&{uVF(WJM3( z0OiNG$jJl7dCCi_2!Gsay`j0}-$-V0xh!4}oowKzf&UR0g#V&{Eq2`-(9w;Kdp5r65gT~4{ERQ#E-rF)lC_w9wisdU%@ZY91nzFo z@pHP!RL?Bh7D+$Q`9~b!Fmu!$q?9&!zC?&&OUy|)ESejOHvceO4fH>|0T-5RDYf!C@ z9R!B~+zM5*b<@szq3tJ`s2dl_M$l+KeCho=$ki)~>DhTY$h2_bAZszNL8tBadbSbU zxWKYF+{Bh5Uk%K8sH?QmGf(2joq@2M64U@p;eZPps-$OY-jV+|jpaW-NXavp{hR9U zffA#JhSnG@QCGaAY+^KX!`$(Z=19CbBpYDMh?OLUB_}vi4-Y5Rze~DvVe#UN zKU)1`)#*l5+USu6&BB^ME|PW|mf&Z08o+Ph-{wBo%TNZ(M>+wE& zZfBeu){$rH?Ry#uEvx>YWHPb1M5;56ng^ryG!(ky-0{4=5>I1@sC7ZJ7nd{PtH@DP z{-~|as#TNNn@091P;T8|wCAp}G5`BO|NDqM{Rct-mIej(d>wrwI4Q{7AbO`z)`v9&EXt&=`MgUzD#5M z0lG(|9*2j~8Djck>qsR+Z|CE?O{`6MBI(o5_{zM0B6x(`;x58aff<3kX7%4g#Q%J$ ztmtPBu~|8G)T&Mle)s(znfj%)5i>f4=U)u1kJvm2um@g6Nx2n~`7f=L`Mbh-%o{au z5UekHQDczw>*S{m08sondOb%79oTrhe^$U)Fnmk;v#Li%4UTi~UafS^#N@?srH-C2C{Qr1%MT7~Y z1#qx~FomBvYo$!5Jv%Y)l(xU#iha#QaYA{yx!=huP~dZL!6$qIeL?_zRut|G}P##l6e z32)H%POc-dhSptMWMC(+Q9Nd`zg>z4eCv&7q8?e>ay-Hpf_8uADfRLVc zRYuk|Z|39g!oUf(V8au#cZ4bu+Wosd%zyKgg!Hhrz6V}FdOv`ovQAN-Be^P`)*Rs|3M0P=@(4~k{U`;Atamvh7>^2OB&r8_ z?C_CjS{PDPpp*|Uju{sb5<9>iv}KK7I2Wswll|Syv^nrgC~Aw4aIW`rLz4p%tvw{n zbC@u$v1O&5VJt0uVU0&caVdU0hS4LQAJ=3Vf3P1$7i0zpp2?UK(iUrz_44+Ub{22; z-G$tU^>-o;6Vhssp8wAd3a~YVQ5=?9s*eFukPZM`rxcB!YTTT`cbo9Ptxtb{Uk{9> z&jFjRz7RNk$5fI0vl|dDmk6EgSUak(kjoiVZ5oU3@Cm?jEi> zPcmyl8*ReGO3$5TlEkWQZd;1F{MH4CTMT69H1{(hin74?HEEF*8fIpj*c#Ub&W`*a zswvC1_`la&G%yCFlFDCt?2J$n^j~OTd$QJ|W3>3fDbvI6<)#07=g}5{ul(e1_5*HN zZ=TaZtP-&ZGhfDSi0qB(X!)3mHtb(aG;B zwp18)zNGlU^XWl%oigIN=i3Jo+PC2efg>(gtKo0r;Rh-ri-`I!UenwxuZL&0{E4t3 zz;us}6h;`i4c8M9e+!zzyV3lP{t)iGQ|m8FtUs$0x#edKjQL*&q#7JqIOr8w#KYPD zQLefTC0t9(n#+CsQqwPNa z$JE`O?-OaU1b`kJEWW(fZZM1LJWuw}*BHcn0%{$3b}6fM#XbV*G#!8hpQt(%PeczC zkcy7yi{Dz^Xz6=|+Rm?4#DflwszFDUsg(xRtmu&ONxglk2}ami4XMMh@dLX2=bB(k zWh?&lCgBUQh_^hPBzQJ#i=lnEC)pqnUW9rov-!Ax(G`^1^+Wk`W9;4Y{xmZee?<#l zU%s2aq_2v#bc_ZkSH0rz2OKCGAo(6n#qM<#o{RO#fxayOCc7P<9DgFFTik>8 zV**Z++bN3Z1pR-8S+uYs=z;hoJkDmTfQa8Z^i-}5E>0Sd3)O$mn2IH|ZXMcv`biD4 z9m&8hIR-$uo0!cYt?o4T^~pUA9yfA;n0Fg98)wpMRkH8Cu^?bG3H7~Rk6_l0<{TpS zSSAPeb^8JMai1XkG9YQb4fM!VfJA81Kn7m(E|k}Ar2bw?2vgPAWKBhaf6?c%TZDkP zd&X_Rs5yc*Y%|N2d$B(VHvX_1d3h+x>@$El@MP>3L76L8eNrQ}wywzw_K-~<=6W7L zUy%MU=PHJt7y-sFX@}H+JUyi~kW%@#Ob)B8(k=F{G-$q5@ zHmJfm2!dTP2RzZluDLFs!P0qG)T`8yh8!=1i`Q(@#q_&ZMS|@o6BWvDHflN&E^@A4 zq%G*^Yn8tD*;};9Q-9LBmXsy_IOa4K&5E05PUWCFYo5ot0T-2oz0(v?fH^)|GhWAVFcoHJ%l$rF_jGQ3oi;HNSt4VT1lW1(H(Uh3%2cj z%+`y68`UWU0$cDjSF#B{)yW5smA&Lq^p3L7`%{2WX^qy6TnFtJk^q;tdHLcD2Q%xz zSiY|weVnT<=0sO`{#c2qh`&YLp}1bl#Pjd@UX8>{7{o~@iJ6!%I2*G7;f77s*g|h$ zbC_;0m(^TYE|fr4N7;YzueTQ+8O9cFm1BdfIXyBg{XdgB;g?Up$wo?u;z5@JFeyINe>%l^NJyw#Inq*FrlIH*@WGlB3GW%G(Nd%# zk0Y2fr)Z%5@L{gy-<`=g8Z7z%$cY}^_Q8|3Y9K(QC?|y;eV_WNJ5TzNYdlzy)>&SU z?4|tI3($StPbGT;6+Zx4{Pq*)Z>FKl?DY8O6M>yJygsg1a06UyX953H3V-mt>YL#A ztOLH?>;>kq3@qtWV`{5ACVCJ00Kk%W=Q#Ew22h4uKcTN(x+11{c8P9!9&d#*T!&zv zZ$MRq2(Vhv&T%-Miz9c~P!@>GS8BWe%Cx)f?_BOKzN>W;h(E;|4t{>n`y!0p9s3%! z$Vy|#aiOinwfLWv2^$M$)L33dQx5&Y@t^xj2J>k@$o?TQutrDPeX&d{029O4(QdFM zS?Xxk?amMUbh1cl^C?*rusjE!i~^;ooeT@IRt-hi+xv!f`u7!cWFn;vpkM2|$NS$AT9Nt@m_B|zxNLj?#Ih~*W`;g@P)@M)Kz=6JC^V^moWebP;yi4kH@oEt|<)kx)M>or(ME5A~ zhh;Sr5w_^1dtXt!J3F{jt~Yr=nbBG;81_B*oQ=`=P^1>i%RFmX`%i?H2L(hqaCjVb zWRnn;$Q?QZYEf;w1vvVbfefY-JI&q@Ls>!+&&xNm$Kto2-Y3BQO#h*>ovY_tng7BA zBz#gLu68aA;|mwiE!o7cM|OHAAN->^!fBqj$VC=Ov8KohLpY6s*Uw(J6=)JqS) z4wk14S+yqHbH5h-wYj>VBNwd@EbpBsPfBjb|A9h?(IZX-?mZ82iK#fQG);;$Hah*K zW^g{NP;3UKVnKF}raQE6TEdkHn3>KEo_CbOmx*-t??S(@S~DKG{)P}~@R9VRjGCl+!kv&X zbbNpmj1ti6qy=+ewqXk7a7HmCkW(nqXaaS2eaeJU*ywt;8^v9!FG(f1$}5~SO54pRO;@L zz3fv%kNAEVnY5lzPl}OEz66%rD33l=SH-6b%Q+ zI@1K6kyD#Mk%7B7GjDkJWhZq~o&W8*UIi(B)IG+|xqC`Z4nxOPX@9QMyH9CAy?HmF zw5CnXxTXeeWB*q~jgtcaPo4LMpf0b%4azF;kK?d^oma*lsqT>XjFZ;V=v5V z`*C6W3xPHVH)6|55)^_c6ojAjy%)IZVg7=1w9FrBxv}Qi^3*MD>Cx=a1Py5#2VKmA zRpjmT1eM>lvnN@!J?Mnt4lpy}$ftVqPJ#M~7x>AUtHT(SRrDS&5|&m&D?m zYn;obeU?cWGaXsSmyx1$l<^8$bJ?ea_Y{{=5Jo;|Ty&2rR{T7~)nDXlnyK^Tm@>4Q zE2G<$k>rEb#GinhA~H14mo_+7HC3H}6A3Wss*$=dQxZTfjX*NX&=v%r+dId#{q%Uh z7WC^u-3|6lB)xApwbFF87=XE`L53t495#^YC7dpKcaxDjmYcYHT1g&S^P#Di#vm`L z-M(+hGPD*xd3rOz!=o$3c!Ga3RF#6-2-ue4SUzR2hSKnDm~vjxq`Q(SceOc)D!-me(NJA>O#M_ zc2jrafG1`mNm%!-PMwM4#ixvRI)#KzXw!jGbhx?EZMdU+#IZ~re55?b%6N~z(0iCP zkruklZSCzHSNVKzBa791D>7h@|7Bl`?um{oYBtYtZ0qP*1El@LlrDag``5}~Lh_OF zO)W{O+SK}MD}p)aP~Rg;>+~R#5!zq*mm&*5c_X$ZmeuB&cppRhsrZ`8>7Vqq8)BW4 z(!t7016=rfCU@kO^2|;sOp1LK%J|CSeWi0RrNd?mfHNcQYQ5y!DdI7!Paj$7q>}7- z@ndEsa-g9LZdi}Qk&=*|LMyq+K* zy?p;5cym;QPSP|<{r|$sooRoe81sWA@77=cdhtwN2w-&s>_5%Bu2M)^(yct*1J5H1 z@Kis^rtce`ad8Hv-xi&69g2p{efW&oanw&GY7jbpOJbP?@3D%+_j?=04PhExlM2#{ z7_apAP9rt&W?Idf3q#Y4yO$Z$YJHwaj3;{!O&mmc1!kUp5DvAeiVAfY{#5hFX8au> ze*N^4;wH#wI>GL10zbdNM)H|or5(I zO@&P7Hl|NY7a)=2jHeG1`2XH2$o`s!Pgf2{mE7;nFTKx=4Ny%}BTX9vJ^0O?MEB*8nxG;O_%y$@X!p0fa z{m87HH|A`}$0orNP1W^^nrU6*+UGAQ`CM@&*h(#T||Xqk>E8?*fp>l zvhJ6MdalDCdzyysxE9l;kfnwOVA0Ob(3gZ)bHap>kC3wo^^;#S07`KdS`PkAIh_O< z$13Z8?P9+;8br;A`CU*~Nn(#_6{kJ}?@3GuYN4b*g%E4R-W!*VlP}Z~ie!K8G4M$7 z+C2N$j<8zXvXtr*1{Iez&jO@_#L_wG=pDinCFE9z_9&=UqCetRcHHR-oPE1Vlv9MRq)Uu7LdO@_2~-Kt1Mh*W=k-J>H9s zZ_K#-FXlXmq>mL&?VfdpMll46pO#9i-{HvvV4jP0% zBYpeIU)L_Pj?|T4Jjm2#i1{QrB)yr_k&O;}|MZRrPQZ(&oDUy>Aqa)HlMzD1Hy)uRk zuSQ9`?Zzt-O9fu^b+WMT>1UD`P30kbu;-KaJE`*{A^2;TRJ$)ka5lE5`wYV-hp8{p2y0!#MV2lhu}8hz2UPw58a9~!#?)QQLE z$`gR&Cd)t~FFw_}4nkw4LYYANA$@%$Pqt9Kc;CDEa6lbVyFC-nh^RKqD2#|u^1?YE+$GY zTMn*O^>gUVWk-?3)NvPFcWd5H(2_-$#cJN~B)4DBP3#4BB)^nmfIbHeo>h<%`?ixrl%7FoHkL9h(!2|#giSIO>TWdJY08Jt-pfZ%^F!0#g zYt#dfTKh&6MA-pS7lV+WL8uJ5xWD?6M0-!_ZCp<*V;}H0UIBn*G#C*7Wmf=3d~K)m zu#FLJnVok{FRZkIq`=uqV!g~fLfwu<_2V9&agyDT!p2}s>na?*v?}ZqQuK!2@cR})-E7KjY?Ly_>x#ZvR1QF`KJ@eJ&UtV$>O0;~dSJSX0^3u8H>a~O_lb?%oQ~l>t zv?WwU!nJZG3G0VW%N?_oZ2X_`+i-Ki`;MKqu9q{PC}9WcU8V643YsLdzZuFL}gs%8WmA8E0{Zs}R#^ErTvAV~Pi z&E8(MKYCda1V|2l#Bc<#0J~FCS7S5Z#KDcZ-_S+nQM2LTgD(&CqL!-X z+$}=l+m)m!S6IAh{n0N%&cR#>u(5SOw;=4r{Z?r@m;gj?zAywUv1NN-fnU|WkMFG3 zcD`MmdGT|zl*1@ZwWG{^@Wo7((zrklSY`wK@;k2h)zb1O;v}JgnR4=a+Ts64o`y(( z?2*aByJP8wdBkN$D2etP=_)cq(>CZmM8}s8lfNyGty|%bEDHK2a>3rj7=KWrPlWVI zhAx8E#Yy8+FtsrcCa z%macpxM&uP;i7%7kF%K#NI}Ejkp_=eL6!lsYcS#b4Jd!wv(tgbMwSnK(*b^))5d5F zXrR=F?bG-dcX6SwWB}_Mv-zgRY1D4hpIA>K@JWXgKieZ=-U&5o^7#HP!(w+g)E|mv)=^SjLo23mr@c<^5S~hf0Ri7j=zYEJ26mz?&vkdCn_nnjOmSoQReWKu=X+4SV$+r5#S9rB*67G+Qs~*ugo|@vFRmWzp zO*>-UADQwcwFXRX1J%lO0Vn(V5E)YCqYe@SM~HgpA{d>hGuJ94?Yxbo#_d;df{>($ z!j!?v772E}PvyDyWWu>s8?s=7#Vjj^g+nC-SGx&@`O80jCiCaM0OwnYf-TUz&=U-` zp2b)v@_b91V*{{n09Qvwd*XcE0ZNQPoGM$<%x@5*i z!V5JTDQ<^x@AMj)oF#Pvim?8m&1%PM_JP zk>s*^e&Ay?kxh=J6@O(np%VJ8CBK(YJaNUIjSty68V?4=mrEBn94Q465I@AS@_DQ0m84> z{Ea}K{|WG8g#vke?jgP{Mu12L?_(pl^Frral^pLPwUYwf;HrkSPlknl_SxZn1;aZN z(S3)p?!nlUEZNT@r(%*6!WJEMucn^W;VO36)nh)uSC^uvcrQ(~($luubJ2R#B>N$Ruv_Z_pSQW?nQvev=CjEtoU3}X2n;#xLZHO;C~-BrfL@Dj>gZhO4i(S39gDT&^2 zX$w3pbLhC$!-B=#?#K7(1eY^Dh~SUSzV1z|y~~8nt-Jd9=bCRk(0`?|zy)#;79W&R z-~Wc`{({JRq{K8Vpi%kbAJLG-vkF4X2C2tv-malR2cH~f$m?@G#hTxQODa;KZ{o&A zziqeINU89AhA%*_DK}#lx+dr69{Vx~XfTflg25fO)5C@5;$lDB zD0r{-@{A!~HG!>1;QY(uYX{+srKnf@mFYkM`=CLe?tty&vMJNZQ?KMaKuiz?Go^3# z372jT10yL1J~fFH^hS#~0C$uT z9#&#{m2nOljqeR4@I_1$rFVUz%jnUn(Xan>PQ))e{Mv<}?)@uLVvOkz4wtjU1m|9^ zpE4G1w|y08#Lv_)%odn#D1Cw5bc6M-#!gC}n39iD5D~cIP^_EY2N4lYhJ?Rao_<`hEK7`b=?&I(j_Qr-vd6(etv)CP;ncR%D$5CS*v}%tl@Ml%8TmY6Z^E-> zMBKXHM?9L(p((j9=>ELW=*$}idvTrhMwbCL&dp<-%Qs;4Os6y6hfwvmOaq7<$VVvekXtsp8?1r>p|Rq542DPkimGa5-=6R z>{j|950;%uACXr;Zk2Y;IN`c2DkW!91-|FP$P{(9K&mC zptG6_#2Tl%M#0Q!f7;`bGdM_~oj_}6J&ZBK7Re-YFb|c=RSY`a=EU7#H?8#WH>|LI>MdW?(lsFCX0$-YVruA*qcD4Xb4e}V&;0B>};o<1HtS< z&>0(OL}-$+YgI^qQgs|C$;U-U(^$9}on#7oz<|!*2Qa;-i4c5-#>O5{uC<2|9DNJt z+yHIH_)GXIs9uga4J5cIZe~_u-Td`N{C?0VY8!0Eqw1`NBKD4=Ia2X0D+Upms15;k z;uP7QAu=x0OFv`uU|9=0ZZ^0zbcJr_-`)mWhpPzc=o9 zWB&DhkO&};Fd|=pgYF3QqXpKc?Lf6GAZpqLY=FN7)(}s_N=u!M%&!no5ON z*ss*oFSlKqB|E>9q97^VSNsrQw8?z|#v<>O?>Pc_%ld>>Fs9_xs#*9W9cX8NkWb$T z%vX1^P-`MSYy$Q*WP;8`B4^74c}OSV5OWY;yhLeJ=k^?Wf|?JKu7O52MHtsqlUAtZ zU^z8NTvFw6XMxq?Eb!eXd+;ja+qZ9N(LVtVMETjXU==@0+VV>vyw(I*H}YJbi$Np; zV*|&QCp4sQD-@o62oTH1P{h_OxDXPOK|U3F0}XmX_G|C1D1H_<;>~4k42p_(EX%2! z?MJbwd#XhZe_-rDKuzrkvKT?4HuYJHu{IBCby|cC3xCTV zKoT5*+T7z}h7>-VlFgF(fwOC1srGmR)JAz8y58ejrYnL7Aya`C+p-PUYE1Ot>;ng= z7VYnN^GEmzH1L&MZ?&;bR&B_UrH_nxk&5w3Mte~moevsE!>p#a4Z&w zi!TA5Nr7^%jJ!hP12W>@T!6(8D)(;mV89<^D-?iX7+NqIoqPxLWdvA2iCIO8(kYJM zRGYy*%Y4!dGbS!>d8W_bXp`BpG0+7k7~mp#VAmDMrweEUX~72x05*WHD_QzEdoO*G zF-gq~8UeGM8#}V{T)J49H)fFj83hr+NCR=x=PdLnyf_rhJ=%fV-_Jkg*v!(vpBX>d zM~9Z+sA9XsJ>{t7|*KXBjC8SVr#n>7ldT>a!#W4PAkyuK<{ZPam4p7QDDP7?h8%K=}#`Ouq1u!rr{VjJLY?c$3v~LMNzvcF>(* zjZYpk4!q6t*yzdsyiJJ+Un?2Dz@|2aCMtctJuk)Kn$II_af1Wdd1dJ&jO+4{Tesfr zHCl^VuGxe08V{w{k;_sz*uZr$h!&_5rj4+|u?v^0<{B^0Pc#7RL(rg2RE|>a46g-q zSP0SFG{sYde~c60`*^J(w;}4NfpXjl>}mcT1aqU{4K<*)ArxojEzmZ(EkjTSLGT&^ z{-|lxI{aR{jVB;TNeX}g+|ha$8&fRxC!TY_O^7T!{Tzg4L8891$OFK5umi6;+>(5B z+gXuYw>`1BoilIZMHs=s4R8i$>0<1i%hu{T-$xwvG`P-w3$Gk4Wi&1hiI1s>LRHwh zlZij#QsJU`KRDheJ>TfkmWfDoe4BG{Ynzdnzs=U=djR@5_nvoxkGJdxjm=HPU&9yRSR+ zo_7*#az^pIYaTifd-xRX9(Hh#L7iG#lYv6`06XxgBDkA2*+jX&T+4C)kocAw-gSFWk@G~jTb}w&5E7kN^uw) zl1~3J$dxh=Bsd~S$ZZ@ERWV6|E+rhb%lMRDhhJZURY1ytS=dgB_wU5vib1i{->8uE zD#^A6uD^`omw<~o6Fo5{y&ULj+E*4_PyUs%IrEr+b`Cm%%QO#u^-Gsa^x(doDQ^3J zrA=A7nBXe;lhQT$g*eM>Rk2C7x2p_KSs@Nc+cdXHxJa9%locRy=)vJIG~GyFbcv^2 zy4D&zL)S_35xv_R5A6Q^+qN)(8A+QcKx|A^PC(H72ox&JXKL&+2r7gFz|T2P0&X)i zFyuaQFbYqBMs5MvEm{b?kB5in(~BN_x5n{j&Vx%pzGlKKFCDnT@)dxfElIWt!kHu3 zf!_g>N;Qf~*by)j%@4nSJ#SdkZz)T+Slq+qqqxeEzQvMB(j`mAx&9oIhsyg-hX^Ua zYf>9BOzmfQnk;7U%@1n-wC1e}u9zV;cnb|w|5_uz_KUh7LTR8GNOCs<*l?=cB;?={ zP@D4+m@EJ(N5Hu<3Q*IIt}cW|t320Cxrr3J_9tl&%d!v3fiA>%;FLT1W2MijgGht_ z<}Q$(KLmEu-vK>V1rTc<0=1A?kd*@G^6&%>y#in^s0zgN9ga!sfXMGl6FBgSeJilR z6iLe6ny-&wd8K2lu&5P$2ZV8TKruY%mG@udN*yM;=r2EPPS3duwN8AdU+UYU9)(bk&TMiG6|8P0(_(eJx!;| zd8LKKN{L7c|k@PCQt4$ z^vA{3=^T}*5i`nc5WgnpmLA4{=rI)Iylbs7ytinH+?x-I(#LwPAAW`Ie?~7ebjOX+ zo@T3QUSU9{uDG*^AV*qCoPfhn@Y=?x7^M!Fru-~1gtPeIh@2Xx1Ip-6@c18^bYd|s z7i*Tk=Kx-sa+xBt=t%?w1V!qJE0rc29Bsn^%COo!3t?ocOFbz75E%q#I65J<}xG1J}OsDgJc3dR`U4 z^;Ooh+Msjt70789fb_ck%&mUF-(-{8a;!yt%wn5KbS(MB-5N8Y{%Z5qJFPX#*n>q3LP`SpjxvQPWvL1(yz zFXugxv+M@_x+>(+>~s<4r{vStV)_hr=4{*QH5O!C8q!tDe@A;n;Tnp&r$dbf0amT* zGBR&eiphoS?ExdZ=!eU08*f3O;=5L=wW&_~u|gfC^?^2PtN72vw?pi6a&sTEvldB8 z?v;6{7pOw?OEsvX7)K4^9Mw&2)Uw7Zb&7i~8757~{n}ERDEkV~I$8JWVr*FC`j(g7 z++A$&_(c5tFTSaJDy+!=5xU}%d@sj4Cn6X+`yzjzN`A(;Xt&!sYzy(5C2s~SF1WH$ zyi%$2FMXSPMvP@}e_gn#jFX>6gGLeervIY+1*F_$|%M~ z;bqYq@t-msSB$0t*;1vB7H`(g~=?(MkpQGa@g>8Mg*wT4ZZ+)`Mk!<^)RJ#dfgTz-H?@m5`GD&ptp)SY{)T! zR>(g79+6`Rk7$4vKCfyB_-EBE^?{=Ikn zJW*Q5Tx$GtV*Gj+EEv4*ufK#~(pyExv90q%5^LVA*+V8E6}z&99TN1Q%2TuQ+p{X^ z5{GQg5LNkJB15Kc8SR@uGwgi6uttd`uS|_I*QNRv%urE%HAW?Pi+-qiCsgQ!e%?^Y|BBhEqKL5|^M0{Kjei_Rw z5=z^77zh3>^8QA;b+l?SxVF^RZm2^*{{VErxiI9!Mb6Hr;ZuN7VTFkPaGrINwH5y{ z(Z}hFrCa3ZT905T`)jByp*~F3ggYT(5OiFu#5lGS$^Wjuo5(U!Srs{%48n9~j-jd9 zT1-HEh=!F#6CrVQ-WkE|gGIhGjI*>l`8)cToc3BDsF4leM2f%$(3axs95d8nX zhBb~eZY=-KYi}UoUmM3)XPu&K6`RwU<>zE$)Fh{=k=bCYw#@BXYm@BwR+f-=YIB`i zGao*NHMQ8(KB+q*f$8_XqaZG5-aV#;z1dVO#ax+73lIxKTs|o~poIRt_upIFpt1a9flLu45>a6_tRZv6kfAy7vbAtfA9++UZ1^w0J# zDzf-&*U>{#=_8{L#Yna3G-)i!Ytoltw*1eaR#&f{9H)r+%zGeFdkj*lSeLV`cvZfE zHpM^eFHYi+<`Pm>W~`RtZm}gL4woX-^EGs(4!#J_c;9R|^9R!O&pU19Fcc1EC#BlG zUGfe&jd(bNWt|ezh-Ag|Dq-OxyM6hZmaSK2&Vw0W*Ig-XQp+dBN0;fF>Np|Vm%2%d zI;>$hvx;>VXyk(^nMMb0v&HJORt^x#ob9Q8VOf8l>xwWGrh6>Yn;IO? zY#ROZZfny~YzCKDJWX=)%t~)t@}q$@=2YGyFS{gceH8S#18FgM=e7$UDsNZea&UW# z$4gX}1e2=DTh)}r$U0@0=2`F0Xsc1}ASk7;xCUljcOF6t#gnokD-&`I=!J>-=lyqV zRrx}=9?Cy4MARS+wEq*%e8n8mHBa3$bdJtLr^#B8CN8isjTI6eN)0cIVTQFZ3^7jd zFco$;$`8v~^IKZo^Y z7;Oqlkvh82*E{O$hIe2lWn81J8kojib=wpPvhJ4e%LD;*Qb-$}d z;tx}cC$0^Xc41-H7L&){s!MAQVzlk{!}PXpZC;(8jgU|`WdF30C8mmvjgQ|tIkzlw z*7R){-uAQHW+-B`XA+J(2&g{S9;>MGEsNaUfc#WZ-u`@|A)8Vim4N?h;@NPe^~YH$ zWo=FMq!0x3gWT5P+s*zzht&-~G{Q0r&1vb9YVr@-g#lVn=bF?8t@=R0Tk);^GwNt3 zGC!ko609?4Ll_#r2e;Lu_P%@mh*W+e?l3OLVdjl4WB3nOq1tl0{q?a;%G1-&=#>sE z8l_(0gKMSV`}(U`jNMF(9fYa2HGgzS*$6$zu{n>RoMv5nVd{42?Zeeq;fya!Kg(qH zjvSkWo(3ic&v;2buf^mYJuR}7;G0-Am&CD-3&P%%{g%k}!i+T<^4P26004Gb$++6)af18W%kc|*jRyIHR{;!Vxt{n9lHv>35%Ro&o_^(E+ywS`)bsc4*$BV;b34b zZ|S~)&3?JzfEnpD8kWbzTM&b zy#co8rWD#z6x6ETVoB6AezhnFiJjr%hYPg&e~W=~IP?8DFV%rq4u1N4s(W(IH*4!9 z7dpssgEo&&u>x`OZuZTB6VXo@*YH7{ve;V8KlX-1fmm7H*c6qfENUS(%o$y0R?IP* z9y5>~cecKxebJ8>Gn=8xep(UUY0g=1WT`(^dt79QYcZ)* z{YpjfuwiH^vi9>@Uu0iz_g>N2Hi;L{nwpiSl^#Wo_7TqLUCO$Tb5BB8M7Y21Muzna z^gj3T8J?YBxIWxo%X?w^vh&>DVR=3ocUp6JHsLPu>XhdXvP_g#n9>X>WUbzzsecyE zVNzPKV3>O6prXJK6Pj-R>ZDHL*0t!&opJUw>?z3&kIyx@ryv#M;sh1q-!GLg{D#jX zEw+dmWDpaX%=`>X^*!DOC7)wF+)g|ugcLJ4xlBK~SXLiz+EK^V$+BG2+5>+qPzyg) z`NCPKDWlV@O<%L?w__^wRK6l%WbghxX4AM8Yl zg`t5%4ULuW|2UK1Kcmq^pJ}(6v`y1_;#r+jP0xT*5JQdrNw?-<8MCgxphX^Zf}9{$ zztOraF@kio!zD+)LzYt~6JG=D&Ze~! zUoj>uGBhQ~m=29l0-TOC+G)T01V#i|Ke#MMh0uPVS4^Q|@4->Dp|d$yud7T{O*he> zEOeEWl$0i;R@m&Vb&Y3`J_K7D^J7`+w5B#1EDdj-kSc91iuj~@#`bM?l9T_sZA#I# zL8d_Qi4{lf^AM_$=a3#|ZyQTXd28p@-igk)r`|0y>uY&=)}=|Qu>f6n<~)z8>eFOc zUl7BLdsF;(!Gr8;ylxE7YHSM!bN+K+180IFIh{Bl)lI zw(!d~7R{g>u;=j2$VuJJtTrktek1v#H>j$+D<^AI(Xr{q9ZeRf*S|Gh z`gTDT<d!f>o`R~2`*LS%zhL?ft zq&tRM*5b-PN^zPSmk*XP)arDmLqBa#AGTe%rCc9x`KY1kd{0eE%9?sq&_iD7Ali_Y zrdzC6-EwBPoiav5O}%yjPuv`-i%gBn$SCh33?ORH(;s@$m0=$YWmTSXy~m?Dk6o*D&_A5mJt>|2WHwSZGo!21%;Us0|4qlhf05^C#E4SU3V!7w!v9U*G zrNVo8lvh<4x@uUX1+bGm;BQj31Dc-&E?ckJGjqW>=4*iF19EHp@bGY4T3Qikv3>{8 zi&{L(IJPzbixO_*P&JqVRL^3fNXy~n-F%u?O&twQ$ZdEB7R1l*eRJFpPn4U`=h`CI&ob~XCAN! zE^)35P#wzwDYa(@w6`1HJx_TEL127<5}6HnP>8;b3ywJ7PZcS;#(OAGCz1An`po%CaYegwCdNzvXjVp za3`eiDVJq@C6r3D%?+FfxtpK5Z<6|rUqU??`~sLFkvPD_Gc*|j(-O* zYU}gS%jcND8d@cji?chvQ?TbL>c-=;JXu`>fTMmP zs=-#y=8ItyKWkudm)5^hWL#4gqJ}lS>iEEPz!7N`42qszdSCD34Lc;ACG3GUL2~>c zWbbX{KuB;%~Ce zsq4=MP!F%xFpm_XxPPdX6faMnuv&NUxvAfg^5D{~ww{#)gZ7rj!a>E#uArGNi4hb~ zS`yR-9+e|YNb%Yp7+v+&189AD?xi_+UIrlC414#viTpEN`s0l%@}gi6V(B>9>gpDC zMw~_sm^naIh|*G13v>Itm8v&)Rik95H(?^e@zJ=ufvv=Z2JT1O+8KIE(}Fnhq8wk* zgV-t5wgxODTCb29)rVVkfAt0Ag zX$G1Gw|K2*PIb$LvwZoCn-;aeMItpNrM>+hR0WM@0gaG}^zFX8+B^TM7}EN}$iO4q z4DNHiSPCAJ5?o?BLo+dPXtsb;y4dpYbUp|OW zHLL;es}Hyr&gTI3xI19%j{(|)u@4G05OJAx-z-K1H87H2=?Pp#<=$M<(#}Pn1)GBN z=&2{|<2%1|mw(`?hB$rzfZ4aR6WB?*?{4<(X+;9QI+11SK)P2?RqxT+;rh&4d|KT1 zL&|~P-f=a)`NC3?%N%xL>cmp*TRr6W&b(y2&!{>|v{@8N)b3ng<8=_V^#k3DqaF~0 zSmZ#oif7dT7hN@4=VWn*Qa}|k7XD6Ow%!}qoDNh0%5Oas3jO4?seZIGJH69v1unc; zHh+Ta;LZqQF>a`Yg#WP^nR0wD2kO_FU0IhSYp@WvLW7TT`ced6BZwO`4AC(HDX?_l zIDR18j z^2~3y+q~VbuT+Tiw>TAEO;H)>-u=>T{+kORWx=HjD4}==Edf63nHmh@Rrxf2rI9>^ zc_Iv*8l54)N?M*Rt)zC@o>Bv`sS&~aC?uT|0LzCFu#tFblIE!~Q@3@R%ymW zV7h<%|JY>qbh!3hmR9uG7Nd%l2A<-e1@yx;CnDnc9m+@gyPkW=b4k4FN1Nl&iA^-vWe=!!x>a{mn?}XPr50mw&U{F^)4csvo=!VpQNU@#^K4W_Aszlf zjvS0RE9B?r%g2`mwX~S*X7M$j*qpE!)z66=3bK*HJaTe!UIU{4PtagSu#Y@zfp=!C z%z~ai^EwTL*Utx~{R^MxrYx5(k_}(=FT&S9Ruf1Adi}(@TWrNW8|s{_^E_;t=kkeR zCVk`E8}RH*^YM=bBweBls+iMV!w-0lGS8n`?4V{oUOQ7RFzgzkftZD_H_Um_>3Y*-I%x6I(@F!>vT@hiCx6ZLMIA331AT849gND%d zr;&%o?Pz0w1_tin+yg|GM=}e-us4fQF7>-qH@W|K_KBaT``Le^ufMkhy!u9@l+5xS zzbXmM6T6Yh;yoynY*4$ z3;uNvL(~0M|GfuLJ~NS^F_Xf0@w+J8YCb4XM0UsB~)#@D4y4~H&;ym!+lWPiqmM~Hi8`G3Fym$4) z6R~QjiBg;e6dlwsh}zs327(GmUp1YRblZSPcw*Ca_kR!dX1J}YSm9kv*{g&*Uj@wr(z51ty`V8wc=7Y2E8?xMvNUi4@&gMbR!x~1_23yn>g{aMN zlN5A;Xs1Whe8fPCJsbVtAFmay@_5ZU?hr86>Ve$aVv%ffI-Y3A-_DotUux&C>p#YY z-y$8TsnIFrQ1)S{;20o*naQY!FtyU>uB+bk7Fk@ z7jA?BUYXLN`_!( zw=^fvHBV2%S^jOX5SRL{_d|nHxx3lHcQyo4(&$x03f(Vj8vfFB=aA1l<7%EPVS>rQs=Px(@;UoAk&vFbmdb1@fF? z1}_6k9Q4a)U-+Jb$WQT8wkP?2@AC!{bOoZm>Jgli4=uteX1vckZji4nH!l~}upqiu z_JJx&W3H{B(@kFFBh)zE+^89x0Ha4!H{V2tk5wvQX~g#uz4SuytQCoLb$tJ!@n=GX zm$LeZL!9?cYQX~MeoM9u_CR5JamHl4|m|@+4^2bYED2(N;B`r`EIiX z5U8m;8rJ~>Tx_#oIIRy%k4@xgQY-tWa?kG-akkjr)R&R}Kj5q0a^Dqc2y{xiP8~Fk zFzGvFD}q1obC=s?yiqI2*V=ThrF6&nQu{qH;2RlLv>~ORFfggq-aj{Y`feA_(Y)+A z>$F+*CMrC3a96`!J9uEjHS+4pw9?bNCsS2x`dogFLGu`#WpLZOkD)S(IvG|(lX8w_TX#9)%c46jq zPY5}~EbY47i3u?wPGa2FzAHe9OoV|c`Uws>4}3oRoTwHk)`j+Ue;M|rjWVC}w>jEy z$aaF$q6%2i+dDWM%}E`M`3$x7_V&&}$O-a39oQ%xE1B+q9ez&K>{R^r%)CO|WxZqr zRaGfsqVN;t^o4;BSJ%P#a>ysEgZUr8s{QBX6&8e3wr)h5M7Hkva5l8)sG|)^g&E-$ zLXMmGMLFTK-sV_gwJUH&j|8qvi8IwU23GS;dc^o7N8f>A5Q}=zN5Ik0S!lQfYI$0< zfOu>Y?mqusf6!Cb8?otsCfNUn>W0YwaVL=yt~F~sKA!g@4L37fDh!LBqlav3*YA7~ zL$THXE#aH{1lAU}H|NQP{MYns0D*x@0i~3$GZuh8fN^CpoD=pKl}})1x@j|Vt_{cP zBgmUR=17nS$HgS)W6#>y6@;CK&%4b%F4<4WmlrI z&0=GJk_~z(*e(#y6ng~(GUUfZ%(Tz)mI-EJ{&&2{rHQ6nj}yZT4WXwrdR$a>=;VQ{ zgJHptmpz++d3?~)=KrD)cZcd@5hZ<(jGHMk-Lw%DR&xRuq^ROdC}!SUgx;4i-`RJC zE-fv^r=@M#IG*BN$*tR6g@nv+*VfExWx(O2QG;tEoXwZ{eWjG3G$3PgTw|edCfk(yj=} z+S?s?icAW9emT7bC-+c#eX8_sxwtxmo*qqA=`_!mNH&yaJ^bo zRD_2~%=U5gqtZRCl`@n%P(2K5nWhu{P!ze?dwPe0Uy+zgCwC8!(;R^iqYZ+v5{;P6 zVkx-y&9I4~9)Usar#wLWUwkbKj>0zji5P=K=BGAPty_Cg57UhmMSPTiL2pIYpwKOPIIk(r3!GXeHQmNK-6CK8bpPua&Z_I-*X6lGtsleK58iGJ_9<;kb|{`2dc&e7?(?)!dU%X@ph zUYA$eT~$Tht*Z{g!W9<8hK_10d3%X*ivrn?O(nrKMhCiOUtfQGW8!rG$J2|0xQh(w zL{G?j!o#=9i}m=hcO4Q3gei~>#sTAb5{jJfNVr4ENl8$_HiQE|q3gm11hKb zGreW;uBIpYyPPY^ssZBJtwxj#UOalHTogI^amyaVJ8+xdTX~0s#cuo+HJpwy7bcaq zvB^5Sq}eZW$F&mYZg#otv~2crQu-WIa?$Lh_<7oFay03E$0lr6?chf-QPJ_GyW-0y zCNKG&8@%f{@E3rv#8v8Frk*}MICQ(O!n-P@RiTkn46zFBhs1 zIw^2Ul|iA@Z~}b4{Hy=13#W&LfbV-~6aJTxVXA>^V+>SK;D)yxFx=k`t(g`MQT=_q z406ZY_V%5&gc1+Btmq))?{1LkPC&9hYD0#Av{G5klvHV!rkSe2q2>ao=PtgAL)|4O zoKF&jx@np;+7pR7RG+esz4!Q@KNtTLt~`GHLb0o^&O7D#zb;qwmDo23^>%C)nv=Dd zO%#`sI#eEKp3paAcK%4Flc)zy@*b7npEMtANZTdm+sa7Mz&Xfe%Wf!X^x^gw8E?Q~ zE@?Bh9+7)R?S!U<`S3^YH5={@K9W15E`dr*glgW5n%8nIB+^P7ma5b#B%U zE=$SCOs#<+U77CW2FZT@z27SRn=YID3g`anZr-hG`G|cEvdpC=gDxOdO;vQ_<^$%O!%a{48)%mzy zYcHKv&GuBnjglu>XV363`H$s?kk(%0zv*6MPixE%TiwHf*Ydh0g38jKurmUgkz3cU zU9*LBS}^wRU8CotR1@dX?#ReU87vlCv2>|C9I4EiZcX9rP1r@18GwSz%nbM7;Lm;k z<0ni9S18+D7BaJ zJ4}^+)hjr5`!gYFMRaw5vHSUUrWsX+v{=g>DJPhw6ocA2?ww%Ii}G&jNpDhEl6on} z6WLwETW7_(Wv=|Tk1AEBvW}CLmqu#aiDXgdeVLN(8b7-y$WOa#>5P_DYS}h8>gVZR z!J*zZ-krOiq}}sSUf?Fr8ATW0&z{6HU!1E_=Su+y;)Wf^@r51{;Q@8R;n={-FYdE& z!c1hxI~z7|MCS@Cuxe9wX=y<>GwDo`?C%9s&dan8j_7win1-OPvM;Xs&Fbs|smj%g;kr%iV1w9>OlQwQJY@#p`4xJq= zZRP=R+Z6Zv{*H@ zzT*NXQL|6p&*GkwKsntP5 zOHWz1OE*UBGj7f$I(eD74k;QtRJfKUg>)}$*B1$v(S_*kx#`KNkKQ|##m|%sW05D-asyGt!I}bs(H$u0DQ;geWPdGoH9MksmsXy&i4QG<5`@v8$XZ z?_{AtbE}?RKc{(8GdsaiV8ruAQX41Q1KfcCEnj zLN<|PZ6AYXY9LF;RPwaw+70T@5C=sgDWa&jnXkP>^%cE|KJ%x`P`GP}u?5mm zvFB7Oqyf~DI5IR?8{URgC!sc?>|Qps$ssU)5l)=wf3u(n_PeLptxj8iSz1`N zxkseIr{9zzd%k-0b;fnFixOUjb3v}{7&E~!^qgm2(rqUi^Eayp+_l*9B9^3dFF5kQ z?KtP&AzFBOxkuR^JiYxw750wUI7mdi1+|rtxcd#Ov8cN^tCqnR8h(NRc4K(eu5fMbHFI2UQJo(7CxeXI-dE-LB{q2Pz1u zD$|9Bpj_Mr`ac=rs-KQQP3WaN!~@B*G4;FFRjK|<2G>M6u~jZEF8OkOfyDDgQO`_> zYwY;eg1xUF?0USf4c)Fa0MZUELoe^l1LL)6tIoM||LiGWI*s(rwxO*9z}==H#0TgR zB-}dq@39`wF2~N&i|!^Ug5kYoQ*>NZR5B3f>aw$ELfH8KnV)FW^g+YasDcK(GB8gs zOk^yC(4#7N;{ z&d;)L_h~TA!lms5m2@(&CB=cp=iPSjkqlpN-=zY_u93Vu-Xk%#ag}q>R@?&w=(Ulq zT-y^*%8-kS=Rxa3#h*FCn8V%_dwA%H!j*5{<>4GddrkUWTL9O8QSk6_a0)c2a(JMV z4eb~L_jkxFI(h=gB7$DG@~yqCsHpVa&wk+4;iYrFE9nuveV;mO zp&9ZNxuXvudO*W~nSh!tdYhd(Bfe(45TBioe=cCOqNFYWF?OohcON5V`@i?H438&({18%-(3cayCv&k?xmZ%kMXI3ruMBr-$CDp` zlKs(}e2E~%#Kp?{-u;IDpO#~_6gUQ&89xRJLh=Loo9VoW!s+4(1PTCDo7!siQRX*1 ztfPv3FipL3q=x07kYEb%KJ8*M`5K+e+bn%E^d<23Uqd>rJQLt})Ruqxjn*TY1oOD- z-yPffPt_fuv&3c-Q;+5Bt>qQ%**Pu>i0w;6Ex*SFn~Zlq+wd=n01`&&1UCNjP;Jmj zPItt3A?)YBY)iamrVrYWHzzBD+!oRcvvpTcsXOlw?APE0`edJAP8VO3%S&3_m9~vK zOyJ9s5uN;3MbIJefe}1F3>{iZ1MWAqe|1R6*1T?2sb!poEv_yWJf>X37HZS2nQ><> zJ4u3APK?z5d}w!<7c0;e_TR|a6uGz04|V43*#jAeKD*pj1q@I6B=bfEo+zsNTlEGd=Hym}0 zYaQi3#(=Iu4n^+NgFs9k3UF5|Eb?lG3v2f`NaJK8QTyxKknc)GEIaQ)EJf z$AhF6(bcV6TX0*LA{_yHn^SiAq>_)1(vkj0M^_`g9Ufo479;$_10q)15*|Rl?R`>j z12zMtyX+9?CW{5U`mtu-=kfy6L6~JE?&_Opn*4nm1iCPv8lKrmT)X|&L{BG_1MWiQ zElhM1$F@e_+W?DWH|&wam_3?5qKb7ZhzVOMNw9KPjLgmFOk7}rK3aW`=cerK0N=J2OM<#D0=WAhju^O$a@eEH`ep!%{S!qHncd zlSk9<{2TDO%|=&0{&%we9OjLT^~Fd-*6=8UHyOg0t(s+tIdp1P7J4Y)2*&RiAI31h z;w*@wPe#94bwZb*5ke2wea&A|?62c0(cQ(qBkt4DGh6&3Ge>qdGQ?~ClXPFCOiY4b z5oW^@meIickkij+|NJQt^$e?Qsz=2P&#DvC{hoqQ!(=T3|j3>=4F4TXtR&r{<4 z;Ut+J(>PKnp$(5=N&}yAzxTTH)S`1N_Fmc3D#`x$uY%rD@@|D@lFun{i9j5zexpiv z{oJoxNP#W@N(?y^WBUioe0{tBuK_SyxgV-IY_(7#gki>+XX}shO{%kR*c%SqS9;2@ z^=%NiXNu&eEicTD(nw4%&APey$A^6jR31vhmW>{MXEXWJLf_tIN2QgM=Ci%%uVuGD zI(T_r8pO49rpvR@(6A7(ht&<%ecsPu1MPsPc8g$mhW(czfBX|eOV30=1^nV0f1ae6 zeF%J$BDr_t@5jj3CqAS>TxaEg!TSGaQV*~w)W8gU4~>C*?g86EyDj0#Z%6C*N8xxO zr%*Y-@rTC!*E!TOhM2%(^`bJEFo)(#GdYqsp!T=HFe&i1(JU$)e`tz)8|DNIGdFp0 z@9(?w>zS`B315?3-FWEt^XjWn1DiL)f%cd0gWoAi-z*w=9oB3LiK@Ha9RJ0kV2C-e zw|3J5@!#J4yCISxg)^6&JhBKOo6qoJcI~+CcH~ zJ!V%~W381mc!F^M*E;V99u#}@zNSAw3cuQGC!mf0uK`3Q-Y>4qNR6$wG&h$5jlVp| z(%7i6>BWm#F)=a1KDF>#ed~&xfl8(Ne=sNcuffDLTnLXn$ve+*Ya5f;ujea<8C6&{ z%jx?HVGtwa=e|qSFde1-s|OQG0bQ}*?loq7d|d1J@$SJmV6tM2&0T|`q@4xq;6q5k zzu043uUb9=B=!@5jcfoinaOaeRL;aj2pjVdU67lPg*71}p6jqp7zeCnPv6-?&~w*( zR$CtnQ)Xf&SMvi>{bO%{CAVk@^Re36+NMFJu5mj6m>z>ZAh!}9#ND}fWvwy+K!kt< z--3q2hpYLbe)LyxzKw8m{a5Xg@Hh})p@u<$!#H3?^A0>^Uxt|bA58r(7tk_6NDY8f zAChnH()<8Y4oF9P3qOAvU|ZuM=0y0`f)|Il{=SE7}(WM&MuLm+O12+shxW~jSq+IQfZ_<4w#wy9k%MRb4=)MDe)k*0(!Ih&d>$e`XK zqC1dhI>3!JU%@rNWmaw~@NJj#nX|KSsbcVbCC0~U<=(JgCwYb_s=Y->s6rEsr;=Pd zS95c6;_A-v>+9(e5zhMM)lV-|69Lxe23m>7)jrQ&(U-#~<{0Xkz4x0g8dV--WgH_u z06Y(7;B0MkO`1~HTOaj~%~|RI7X(nLGJMdV&!R z|K(xcC%mNK=PN7(nMjb(YKs2|0*=~>-7Z%)^U0bQz8?3(b64;fZ49?Ym*Oalh!_$g z8|x!bvDoWdVv5bfcQmIgk%imYC)5sljOl{Pg{F?RBX=E7s6Qm`NP51R)$DnWA?Qnu zCa}zH@=PV%-)H}*V(n8s^UV~#k=)(ekVweYFlO`bXDul@fPn(44c!sQ-De>JZlTnZ zX{BTqT-V`7x>G9)(C0-s_U+uPGws=jpq|mVjL0o*-emT0TlXcW8SC~=wNvDb?YfJ)Zsx^`>3in7ekSOv#VSz$At90Z5APZOcCpu#_B!YME* zhv%;=11^6t{Ee48wS>3H4``PRgy@B0s&fj(h9XSa0E}_9O~qzV3B;n93CsBFS`f!P zH8&Cq{(exeTeFiA)>X5h!=U;s^;`+ulXK~H>QxzocqrI!&M`%o0?zJ zD(J*~h?lb=xLZ7}A{h#q&EjhjU8uZxrhFo4t%j$r6IV6+RZoF7WE-oQHp+nw=e`7@ z1#zlXkl?#^s&{)2hBf+SG-We116cJfbnisFE;Z5NP{|x{f0{U7{)1Yh3-~FiNs@cI zP&WAuNwCI%mD;f-4A$J6!qis~;{F;IwQE~LF(5qNtxjTV33gmbz0_goPwk2 zgDMh3YAdQ;)#+UgoWMFOA+SCDu1t-}46TAzR~*}55H|&pKveJq*878&tR=XfMfd2@ zquC4;g^m38$q6LRHqfQn{<&FOb{vvjPddjmo_scH`rpCi2-4qFe5q$V1EjlVE{^>< zVJ*{C*zFUL^mmfwgsRX9I&UBo5C}`Fi~IGhF~X1NcP38whYSAI`vv42IvLF*=mXQI zG(bL^Y~LaVN%-H0z=VDoe>xw)Y34JvOdSfWqY6QRw<^=x8?;Q$x2DIm_%FrSKDa$E z!_G)CGQodw_Pd^OHrUb4_Fd&Ntg5t)4zyh9ARKUD#|~VZ>2!})wQ9uOyCXN-6yAaa z5p=0^UN|J?ZReS5j8|#vXm6Cf>qu2m(K#@@WmEuFB(uAKZQ^LS;MAp8jq$2}#qySm z*3_cVPd87apYH%yFL1pZvYlT)E!2h9L6ocgiqkP1!G1~v-%`xNEje_th@sL5i|==~ z3<)N~*RkcF-TjJ_#UpHtx`5a@_M2tqy~AVy2lAF^y%mJJBR2SQv_(f2T)?W3*i z{j$5iwBEoR73=WNd6lf@d4~)!xHs<<6^(zn(>Klw z|9eLoR56GQsjdSJQ2XCIV(eM@K+_*?Sbh3$l-uz?>6lPQoQ!Q9)3y(1_=2Eh@(2Ot zRV^WlHT*%@2Ftx1|BV+659RXaj(AHJ7br-&zlQ(l@M{$k30Ka*K{1y@Z~s>jSU-d- zr@Ci$AchP}_wL8p@2-nQoO)~Ev%VAv*Lrr;9s>xs7!PoisKeK%{$Dte$WX%? z!+5D-EcRu28~3e8-1!RRdleLPo12i-Pw2G$OQ^ z%M;G{sg2VIN=0h>gC0$g|5s+&X=8$6O0M;o&cK=dQ*@X3J6B94WXdm@O}smhDJKUX z=;H?EXQjXQ#H_I`!pezrKYhI-LFC8Sn9XGw8^(^qK^QkSK9Ftq^Naz2A2;%yk(#-eVrC)FzoWFdf(~1t1Iwd&UTOvY zQCc#c%JnFn%FSne;Y}OFJuU;dBJzN?SV|gy5!xlsze)Se=ClwKbbB1$^uE39oQbgc zc>ao;@(2DWKMA)5InbGO*^$w8nSd%W3?vh(Mclpe-%RKStI#oYZ!6(uR#r-gNE#FC zHL934xtB)3vv5OT_c!NRRw(|9T+=eBH0o15giHH#(c~JYRIG6iGw8B2csY-R06eZB z0Q1xFNqT&k-#^>K?_!*XqB`ivv}sTxX2SYpa*E4xLmk)yg~ql|5Dy;g1B~&jl1tka zK&1a?e`CG_!!K_vjTH5L0qKpKozctj&NP54E8kGH_mYS|Wj5ub97Wo_Q;D1s-glw< zG3((~j86}u-NsCut?a`{4?Q`C@k!swlH6HJ9RUg1Q6YWIv&nC9mOc1%p^CKq8v)>C zlyIiQX!wvEWae+b20ab`mmgTU#+TYrv23M}SMqx?c$xnb!n>kqB{3kw>GnIUXu$O& z68St${QmHtG>h0m9lcASuY$_jm;WzoOz^Zyc;!L|pv9WGG!J2!5ZImz{zpX6^WQHG(wOkAG@tZOW}n$xNMK1XdE5dQrE% z+h|~cVUgG1)8Ajw?_!~bvlQ$*P(|f=<=&u^C^#Lqc%Vt{pq85lp2U`!50o1k5&eJM z3SUsI6~t`(1tkKiVQfO!VVx-X$*+k>;J3L*SmI^-u$k|6igGtU74D!}6I2nniyqD# zs%x^%AwLbu9oTV#0H-2N53o`By8@il|CYlK=I@3Caco?8`50)j<$>#4iK-iy2=s_f zHVKa+T#hSVp2J1~(;w$IBMzc7Q1N|5o{-WycDa=MB=><fXaP4MMbyccYEIgLPYP|iecmTsJ1s#Z2XDWOFltJdij8ITtue2b9fU9~M{3N#xTUkGBP$LvB z%Isy~+8}aAU$}VeOO*HX7b*>+oMQ-5gnLYHhs4dny$OX!GOv zo3jVT%}mCmLy8WEBn6oLSIxn0$o9_Frz?$KW|a{IE(N$V8GBwZTs~y%r}a@(i3X;) z3uZ&k$IDWeWU?=%$$%L)l-OCC$QXR&>z|thV<1H-`D=iuVzG)n$N%^0e4Rd+YeDNB zr58(mwF#Gl#yTn~O-rSW;7VYd>ynEBV6$F3rasAjU<-bnFvo0+7n^9%GC|wQLX>xb zJp`87r2@E}m4fxG~6H_!Lr}AAD={~QmL-$Bp?MTWi5Za>$f*Iruu5K!BW5y7vIYE&3}ei zTXx_F;3o8ou!rwbl;sax-=F0H$-xCE-JYUue~q?{S3EUZcLYHJPL`O0JwMTfR(O3i zJ;BOi*PV0M!r=JehR4G8Nald*wnn$w)>|UE8=*_% z?&dW<>bm1yO544^-c4X{UuyEEM-R_vo=k2Xds#}$-*=JZPObd`VN)RS;D1|;8Mx>< zZu`ZO;i*9%3xd<<&5kQu4$sx3d%i$+IYn)p*~}`82-_JWF#f4^gQSjhPLAw*n=*_L z{mGb9h7Ov|K_Y1yT-UB&1nJX;caU_)Edhk6A(l^z|2|~aMYTF$Zl1mo7l`-3irY3f z81I#BfqGKt>KCwW+BVh^i@JlV^FbtDNqD||IyD~UgqrQbt|2@nLP{_sVSCCtfmqkb z!q(RiZ)K1UVxLPsaJ(av#~-7aI!$5=9snHfla#G@{~a7B$uV~!_*)ZZE!V9$*2*{q zrXpBxujAJ>d(O&#^PcsQZp2q{pTABp#>T~u7LC&)h+B?rdsusD&hV0-`fXiv7db@1 zX}ZhwbZi&zU-6O~+vJLK!uTj#n3BIH5FS{b;0KB1uQ>%p<+=9~%=5U8ks&|uo+u~9 zd`Z$t3jN+o()QtyjbUI}{5icJ`T-S*%g~Eln!&a|j`WrO!8?%|GZ9k1V_&~bjbCoA zk>3%vyMVphErF;@5^+dhN0ggj)$%%(C8l1+a5!K3V)^;C#gozMPI-7*!Y*1QNRhvC z%1*dUtUt~xgU8U0*+P;N#I zu55`ARIO}rf+*7R+69s?(z2UY@)};Uy^o{y-!i7U2>SYgk=2ak*`~$dAbl&O1W7ml zoJkUG@iZO_DIpoJ&B0Tx!cFtU;VSf6EXv@cOI2xy1K5-HU9~i{-mM7NC=5ltz|nqw z9j_sOb_~xm(Es_hiDtRz7@jNc8pf6)jcNWW5EZn_s&`hJS{uZb+?=(}FdAIMn--W2 z?D_&n?Cy~Trf;pO;zzdvl0(a)^^cC319bnXqh=i`DAx7-zf|)&9 zT>nzFgbg*KRDjpyZBcJ#JP`*ZOqVHpR?*~~Lm8+3Vguj`mH*qPcrvE6>RLZIGqv#9 zNe8jq+Ai63)-URu79mTwS8$GhLc{rzZo)WZCMD)8Om0I8k%ve~9UeVHX8?3 zJi!`2_~aGsC~?yS9HCKRYLWnIzD-KU$M}TiTs6()`9LCO&aY9A&{x-XH1wRFl{5)x zp{s?b9;;^0FGLl*n$@Jg%#g=zyq@X#yyIsy{i{T=vCZSKH!V$-)Fu{cFRNFgT#m!j zE&^4G`v0v`Y>zwI)TMt89lCon^M0~9&DCl)Zd3wpL^L_ZN4b(!%hO}JJ~s!xyr<-S z$!Kk%5aE=S5ANgSc7Kfv&`oOM=VZ6m2kDm|O{z}=l1)PX{@y}`>jMAizLzaGJ81L% zCj+ljSDYQ?#W+jjBPwOaMuyze=2+RK6hIm?YM_{9%*a{FH>ZINH}-iN@4tmy9Pb~$ z|3hMGIUM3WvLcP82HP=n58fP|XH~OMxZ!Lc#M)%&{U}~+xpmA)H)-PrB3`}840(Nd zvWRuUbKtZQohOV>rf{DvPFKT8KIG1|v>uR<{+b7ESy`+EwNbedQnoNj<=jz?hv|;V zlN+D$ruJU!b0RCk1)yn;Aerqe<>%$P-_P9+npT0fZQL8WEvV`2xLi=z-CfXfjv8!{ z`crV*Q7({Ma4Kb#$csfiST33JZQ-)@MqHrQG@t)GP^fz9jLm~|I;Xdz#^!6ETCeR1 z%YR5!5Z>(ZQ~Z(j$vXm{j18>Sgs8-ca>~S^&~K7@pb#bF>@+#>R0xHH4770b3?hoP zZd@zc$xE?c^ONtQ-@X2T{9;cz>47g@3HEy_zR5UV0rc9sdE(~w6H(56v8f&Gg2R%O z`_(#cg|b~^Z2V^*sa((&4wpgKx?-0iCO7dqWSTEkPiw#RXec9K<0U|i(iP^U`1eT# zO#FIndWQ&Y(0cZUBON>Mg0I4kYPX%+P|(P=V`u%BO1&7}~$e2y-$_3paD z;~^EqLLN$Yc@r(561_iJWOMT)QC93W`KFJb{_=}~nwRFe`K5eK-J$D8=nNFikF-xT zs&quoFp5hYH|p`Jj&dnME^lhn`*S78qMTenqjygf>g?_KFyHYAeZwfA5>@}yxve$M zbF*3&DdL_)5sqe!%kRBJh)^L=yHOsk~;{F+OK#oiub-&pR<#Rl;<8HP&s26I{!(gIn3SM6VMav@JZEnW<|$ z4lwb}w0a5jew11YfvB1phrU_Muivw`-l?Pvq@7(fOdx}d#@GKgTfUoQ)ErQCRz0xq zIV!D6qd>;>d-L8n*$x9(0f82(tCys^%J1hnt=0WSjAe|Yj(=sT|I&QCj9TGWKa=mU z+Y^&~Aqc2}d}v3h>R__yt=M2?r@}a%8Mfo1rA(kk%gBx}iex78#xB*yv4K6Qyc3eK z($q?pp|WTRv18pTWS#RZ%#IA|k&jjNsp+-ftAa*{bhOs8HwSR?&Acv?gSI>iIwK!X z8&xRA=%4oT#3Qeb=B6#Mog4{*DGa)~IIb4ruv29?LUXJL>NyQ3D21)dhJkxjrd~(Hn&JyEnhr?LERO28)}m zACFR4aSw{w<{;9YVR;zG7H zki5(#!QB_1x9tMQrs3^ouu{St#5&3;e067)a|`PXt9w-Yw5*IbO` z7ESJ$NEAYz+zG4|5PLgb5O}v|`Z#FQYpEaRR6E+Ss!zrmjj{%f+Om~W1?@gaxf&ySOEzh%U;_HDQdJ3OL6k>;@} z1LfveJ8OKdhq`TG7(3j>k8#<{tA> z=+or)49)4B{r;u;v~c-*{s?9O(d*}ZSyrQyy(XOheQ82zwY0%`Z?+zYGCMg8u&zqKVrN|p2 zgErUSbVQ1B?8i|GntyB~10n*4;JVw!2vc@3n!%q788m%r;XN_>4y=T!ZiN-(xmjKW zPOOP4Cl|42@HB^6Bc)^$?CVD}|a*7fgjg&akS6 zWD_osXK11m?qw=&qzNiETd8WqJdW2ek=v{;vD2Lzu5C`gO5^YIcs+M%Pm00HH2p$V(bbH2 zr^MvPr}b&Q_7jqu$J*Sl<&~xuE6umb|?C{VE!jqX{+a=$JqSpwMzRz5Z@)zK+?^ z1Z*J2Kx6jwM8U*hgQkgJrRt@O!Z9Qy5W!G0ab-(i@=YIBDT3>p=`)|+F8Tg}X781x zSf5W_lI98RY**};_S7?xmJX*+$%33+u@sykX6cQ}=BF2;m3*cew`=53%eqbl8#$~X zUxgnRa^m*77b-wTs%i98<>x`ub)NT{{~cVv>eWKU&@5(){@Ac*Df6xf%Nu+S58$Y4(idGk=98eM5I8=OH+wQQJZ_;fV$@8mPXYT~aR^^~U0vmTC!dGE0=+%w}{c=DyQd`Z^b}|F$NiI>_7-)uyQ?dyYGvV=xUAM096??xtS19cLqhb zKlxphr?pZEh0HHg`wx-80@vePHw;UD-*xo~LR+-P;I|&PeEu;Os0vk15nqk5iRXPHY!##@>-s|=2BMVk#t|Sld-!xE z0wh#ra5+lsn1BDQ8jKsf>1EI0ohk}M{nIDo#8gTzAG1X8me=P177dS|Cu?Z<2ZQ@# zj-Qp%>F@Z1x zqkWG%5e)urrs!<9-dYQLvr=kgE_D_Ib=L)uGbCc2+qe{G(ZTWX?n2__5({pD$C?1e zuH^b4gNeq^e`qBM7AZ((c;*!gI9?@9iX`DyAx{=(8K~3fu})E`niH$lOzEC1OHX69 zHIyyNeM~=o(dAu>unt)5*Irg;XYj5&vKS?}jmF`h#z&q^;hNOyrhX|GdVBixpoWN% zg=ZmqDa0ETw2QXi?G~GpTb-Bwa1P}3X0DZsmW z1enNdj^5m=91oBcUJ(b%%7ieAf8$heKIX-Kwo?%Fn!qIg_w2xxSGcZ>04|UmP3~9I zBTH9UEA{286FOkeXPchhgVNW0uM#ic7z^4}UDI*8Kz+IYx>9BM60?^{rfihj^cg1e z*$R>7p^l%q0m}$ZCe7L|>cqSQ3%sM|4-z^>ghT(8pE6m?VDhkcBUEdQq9qj?`6_2k zxSsjEWtvWUKCH;=9Ed{*M`?LK3&8ZzTYg#<*4a==-urGIQW~O3cU%1y@K8ZB^(FyzHS%lWdCm=_(2|OJjRE_D>oo z&}BN>OZqS2P1{GZ4p3)QI)GSmXv?eFQViOLGJ$HGpt!rsLw1OpykZ+y&RX#a8VMWlv!unFhJd)vw;BW_$?B z4iJn_KMae*&roepqtCeF-P9;|n0xoT@=o3?#=$Nt?LNghWKM3j~`w9j#+y6cLV(CIh(raxtVBZUI$dQjm9cHn46h<+XRt%)w6(+*!pz= zg3+rHX{QgVekwNN<`z5A&)@Q1%bFp5Pq0y{l5>X((F$quf~LL4_G>svgh*cFW558R zI)HD#NAFkaBd4tVp@uR417xrqPd?cT9^6gFB`0p2i0#iUt4zaLu&ya=SK@rZX3x2b zuQDGkDe`u$SPjFPBMROXf}t!LY9I_dPz{uLke4a_yBg49UDUi%^Z00+_lp$>%~lmI z3c^7&mp>B?A&4Ba$*9{?*)K#|9}kf!5XyDS$(_UQ2E5Bh)lU)&8a9J*A-S?mA@gR- zszi$*4RF+Lr?8n>$kg;?nMM3;YRoV*7eNf;AtyWVw$wIF5~JO_AlX#oxo6p^=a%4+ zPvgHOIWxhLdn{sVL3D4AkZX7nL$(8W&kn*b^?;+J#5gXSguNplm+g-xM6;<_mIrf(zG6X|)t*ew2sl z4RTgNCcp*f{W3@DmmLb$@T&uFCG9^MHqzm^Wlk9HbfqJ8#FJ(^LR`3fP)j`U8?-9={6Qo_Zz%p2{FPim2-2`Dg+>Gg@|p2v(q^Z%TJD%uSy@nd9^ z#!#dkTRRe=b2(PAAlK93qh*g z+KW2Z>Jw&z^FRkoa-d>ocs<8Di5`+K(b8J`xd)zR$~0=PQ?@4&WSgAXq^&K5?I-68 zQDpGh$*UJA1%Br>+HvqjbLMVr->M;vPrM%Om^%UT6mFr#DnFyFGp}pg4}vr*gQvbz z_NqMCFy6Y${LL#ziuV*XoZ&t<^Qjg#m4R4E1$=OxVM^#R4@G$rMjHA;BANit$bF6MLsFMF|@G_Nfwz( znM`@-7(2d(9hd*98MlafHtOc+%nLiqP|6s;B8V7(dh#h`g7Tw6m z`BG>OpYG;|{uVEm7ox>JQTk3E!8oU1%4dVGmE#wk7tb8;WeDoZ+e>05K&5rUB3GSl ztK%%3?`3X6CX2H#^p`e4U+!XMwTR))dm+adcm{$78bLC+YA#1P`T={6d)9KksRaz))TqFh= zdV)O7StnZU$2u!{ayJ(AVd#=gc+{o`vuZ5Qh?0XTe&ZpIBU)$c_Iyajhe@({xK7s!Lj3u

M@q>aZwa_hz@Nzw^+M!g*{R(i+;?cRQ2tUr}vE!-VVFJ^SEz0}^?@sJQ z&+Scl8i9kx+a}H0;P>?~V47w64G6U5%Mtj}dAqS-UMP{>h$HB%kg}i{3&+EBSw7nl z(Buq)X(+wmxjskvo2G9JYNoq-nxN5KJ|p~Wsy_MVmbw528<5fkppaJJSe1g_CFWTB zf|w;9Wk!zb4!b9TTB(|K8BE5q4azcf{yzQ35RXO{@cp7~yY9To7P@4kd_OP|c{bJY zyXaUrGMa|vo>kx}3 z%P!ukIsMQ%eFRMF=*~V}siTF7e6ut0yMv}k^n+1$@-&Uxi|bn&dki>WbPSmRgy;yz zBvNgSN(8kYcl-JHf;nK5X;T0MrX|?%@z|!xd{Lf1^uPXM{u zyRL_Qk7sV~x=<=;5VTiLd=uGq1kP~zn?{^0aXn8CF*)?4skM&t3)z!I*m!17<>Q0^ zTBEkIkIgmi3SinxJKIn}{@9uV?y^8SzJ-M`r$9rW%U(48l^1*WH5n=eZnU?NMbtkp zD(>sZ7+a`BCC3L4&+VmfQp0BiHNV7qOAmvZyJ;+=)r3z4jwx_&`S9cYJC?Aq_GrZX zed>iE_T2nRpWf=0bN%6M_Vx_^_E*mZ8|ZF?WKMbcj&B0}+8AIIYq;ewl86xl@ZGzX z?2~F)^c#Yo`|1i;xeC|kXrZjWeT&g+HVZ@XY2Jb`DYSY|bb?3kBXHT0PD=>Ki&*;w zrAL)Ak1XPX*=rOFP|B$e6bCJ<*@jr9br5K>^8A&8MW@x&^*vh6*Pfvo)bg2aDwk>exsG}c z6m@z5c=?qexSToj7aNPr&~<0Keg`2jGOLOdV=hqRHnGgBDHh2O+DZh6^k?4d*K5|C z!OMx!x6TJ;j_f!s)QtOv_4+VUo9t&VE_xmnB6Lp{R5m=gho;XM5kHpY6-gI6mmLMM1Ri%-(Z$8&1FPs7bj*ho+;4B zRa7q0h{;AQ}eZPEPL*mip}RR{!(f9dEv-~!=q zHQja>J?5{wS7K1+Pp9y*c(l6Yf@% z3P#k~@akGE?XF|vz%8$;COJ$8yQc#?ZSIh1+ z4H~TqwWam{xY}xjHn#+GC zQ|D6bJu~$-XmDtt<;A+Hgk%=TUY?!|}q&6_QI=iLQVbguWbVLm^*6MEjF z-6FMMdGmV9i03W+uE||jpQ_!A+ru-mvYx`5rj=KdV59Kw-qfCnDr^s$#{+Cg^asBK{BDU7Z1FWN^n43Y*XnL@zM3-FD4p ziF@I4H*Vc8JRZ-LD!Y}$1j^)8JGsxW-TvN@$vOqF0^%z?%i*$NMXYlNBqTaRy`<0A z=}F!#2sNs2u4GL@Kdd<=z1%PHL$MF~TFqq)zVon@c(1ZmJ*HzKyp>A=XyS{67S(iU zvlPaPh77aUC6>JqqEy{`R?k=3ubL*!=uh56z`oN^Wjdo|cux)y!ZZVpm2uG4 zDoEe{M;T!{l>ir_bN4!msavf&YAF$)*q=Uol%BK!Ul1TtIxxe*So9ws6AV;0Qc2HF z@pbBxIXs+)1lM^}qazl~uIo-Fyj**MmmzwrhuKf1j;<3fTfW^IUkk2~(n{;OJNPk3 zwqTZ1=3d^dARW^_y^}Pa$R(^J!&4t0Qv_>!3!5~^_9VHCDyfUsj05eTX&x=^6h4&i zmIKqx*)uPb$lNit7ZK{}BP;g?7=qJ*D~ zNZ0jAT4!~3IXhaF?Tqi83}#3OKLFb9m;-POQST!4Av|c34scin&+Z-h5k%h4w?m1z z051_xmFNH9iiG#IktLF+GY@XbCc9AK zqMIqgM=b@+JdW7j*lJ7W2MwnmM9fKGH2aa&UnAXb_yCYD=tdgj0g1lsSKe|UfyT^R zpbC}fzo1XmrMo*q^yS&!U-8^J7&Pp>1~^=9spO-iY1Ti-;~i=2{bx?b$iXVF;n(R3 zblH&KOM!__IVO#|Ln{WDr5RWPq6m3~@RIe-C$9Ha7s-w7yjvI9NEB z)TJxEa!w77xmvw#Cp$m;?}YBRXnd0V03VIfsnwr=yR{L$ZNW_o$@Wf4|H{0ngkGy4fn?JD0sG{>+hp>&rlsGDi1`)#7Of; zM5Ge>jtXFv^lv+*eA955_g^MpPCrq>IP)V-slMvPb029;e-cIC1tXF&VqpdOB({zbWCvk$YC`-Q3cqcVh%Ylb5tCV1oA?rz=%Cp#7~>yl7;MzP?*lD zj+GMiW6}w?jsXOlaR)DPSR(poIyFHJSf(zxvl!`f_%>}r7ks`-^dT2{?EPySBRWy- zW6ocGIy9{OI)9u19c{>Qp2Kt?5&m>(8mc~|RskHiQbGuXLxwswOeNf%)ia9g2RGzm z4?G*Zn*{RCeZAoLkG}zo6apB2&Q#ikgBA0cg*Yhj(0*Pa+dx=;0^fW%$t0cvl%LXK zH_HzI!D6pbUzGS~pg?h^WqNQeB#oO23%K1q_!3iPZ(==fobUan(98}JThV|4 zB^+&^`*r$$IlHu1Bm+6}OB{D8v1hLYDl7hCtP3F2K=i}XVJrcRPHlg~vxKA0xt3m|_G zn)Du#u1TVpg*1EW|4IBWieCVNxOAG~7yW$q$uDZ&NIbqp@=FcCN{T=NRSZHOpP8Jx z>-dWrR2=XPt2V)mzjC1e3Q@BHBJyB5yd4N556GeGc(mpre@u$NlyIppPN&HuSb#Gv z7?KBD`+mDsX=5;eqO{NLXZ#UHp2}<|_py&EeDST~OX^eyX2DH$JvocpDnEMbD(EZX zZOipTJ6T%*Qd~<`@alh0&fw23Zj2)Ehj6{f(aVzHoJ#SY&sjk4Z}&LXA5-vH*_SCE zst=jT$x88Rx?YGMKl$TD;oQLfUa9o=n-soF;TB&nahS16$fSU>3*RLDnxKcflOXN& zoeN%vbob_;S;>pQrUF_CD1uSVmO;cxDUI#ZBusH?d4S)I2_g_>J`$8{<*{u|V9iGFuf5cYX2 z-lBlmuAq}>U!Eg@A7wW47z*0@9OZ|zEA2BhGIKRpk}s&-TnvG|;@#h9Iw_B`KF7CI z<&&CMSZ!Qbvscd5;4>y_Aq-?%=*{p**q(=+KCq`*Sb7_S!Yp#u9q(7YIi9$+(F7~_ zoS}Yyz8CMc+#o=oUxl-h zUNHJjsx^M`W!&HFGO`diJiZIi0tOA|kpdjazNdkkQTj&O691Wl0X(l1$sTE%Y-l8P zYuVr|g4&8xGZy<^OVYp=HOc^gwiC(j-xBT@+}L@hmM{{jpgmJiQDA85bV+HkmZA|p zzC-MQ2J@`1}GZk6IWjs;maKjGh+wSxT|+2EGO zLwSfz%4_HPP+znfHNEhq?o~5pClx&$mpaxqI?#K1J4$Tu2%iMJ^e@}$69gb5Uzher zhqB0Kl7nEk3Q~5h@~21(qUT5%g$wpmG@lhr^>IG;h<1BKac8mK*fNN1rFy-p@PwD_ zD5sqBopU>m*(0`TTGBtj1|Uhh9?9L6ESR{2)WpXmK>e*-a}H>eqxb9qjSu@aMe!Co zjeKBJ;|@URQ#YW_XYHUwAT9Z1V&|7N0MVAuYf zn6v*#ZYseIuyr<3YcA045F@%%TVYOrHe8K8);266fzPrM9t zdktW-po}_doi0Jq)A<}|*$-|691o{LUMSgwuOGYEb)@N$cCsjnw5cyy_nWxgeM~^3 z-dvuePaCT+Yd!a&yL;<3&|p#X@^&&Eyr0)q^T;+|SJ!L64K}=zKbLK6#k}u&U)GsO z*%#ka>ltB{Wz@7{)sMpiyfTBmRhVrGN)t+9RZ_+`1)KpaNi_QlKp6F~uW z<5>7W8WRoIXe144$vSPalHCYPO}rMS0&SWJt25n&CwhUoP$}s9@uCl8?cV*wlv`a< zqPOR(*3I+Fr*j(}M`QG~Ne(p3yDw15zY^E`o|^ZSmP}vqiDi!{9vN!5j`AjsL1_g^ z^7qs{9ygr^cvEM>&hU2Yfr{Itf3m8b9vDb(4WwlW+urxs=eH;{h3&7E*08N}4sc|=T)9lWJo<;2mAkT9bXD(Paai5 znHr3Y5qe1mc&qjepwj8*Rw=>~ed9{@y_MizscuufqpnK{ za%DvqlRM}Qq?J}(G9si2fPd5P&1+^;8#4yrOj9bP!^l1mopQeDzhz#9LJxS`mNi-3x;O+YrRX^u-1LjTMH{4T!WTBLO0wG;DU?S2Ky z!|e7nQwEF|O@lWu(OU3n32RmGOxTV%(a{a~k*woqF0)cZc2>?luTOFPc0-*(7x3bZ z!#wcxwiKlbkG8$t?V6DUhpJV>*UMIOCEXI+n<*bQ2%1o~fSjji)T>7-Q?`B>74{d* zobz~&*Odt|p&EIi`TR2IYd2!Z=hs0iHwCC@Hd;xnj3W2tLVoJ z7a4MGJdnpV5k${{I_O!+lhCA*moa3&QnSvxW&@T7$|ZBe_WcN<65@BhY#0X>GhHqw*yhq$0ACPDB&b@(ZMW!47;TG|=46~_JE}4Q_z?Hib$>il9hWwDE{z!AutKRnzpw_fbrDBJ5>UO zh|cdqd+6E8E5KHfNrp!by@LS$lNMzK`s()OV#qItnk`n_ zA)_xnOzO6${f~kTl#ONj1xNiuNG>4qT=H31F{?Hfs6>6m*)ApQUlg{R2_kVP(W*!nQz){jY!-xM!$Siq

iKR1RB$FTl^-B5e`@hLU@GlvKT&<*_~acjG}`zQp#m7guz{cd-VJGc zy7CM+0TG|SL;2yr13$Tjxh!GzrTM;9%!Mg-GUU67kp91_nnb`ZN`3@>Hlr-Rc+(C+ zeWm9b!4VMv5r?)6yk)bOIU4DeVSzcDJj%%L8sXP2foK`dQ$eV9PR)Y zF@?h4s-`kcYLihbHz7`%(D=vYVF$GkHNt@^g2-d~nOq^i-3T|PfZo5{y`*MV5w(J= zqTEQZDcpQf`dew8UI3ZjI>?j&qHVHqZ^3hCax5bF{A^a#5g{50*G8mdfnk^fD>D}C zbR|R1WaIyO?jD?$gzJv=tiZ zGn>+)tn!0LuIjhlV*r%&(ZpYXKT~!Fupak0JS#bkzWCsRiuaYR9Nc?|RSkIR#T|## z2~Fj0*bR|+Ys7ucu$uU)Yu^%9lFac* zB!&%QCSlW!UBdTF{KOVS7ng;+-rnwB!wP8hbu26H2?>(0MtaJ_x~my}SX)LFev zq@o1%6?Oj48)U4jLFi=LYESC^r@*~v!B~F&&dcPyPf+2wu?%IH=#*#>a$+lH#KVD^ zwMs$0-vF+zvGp8Wb9I{VVSu$9HOxs$);kLT^PQ7-SvmDv$_Ju8FAM-5EjHB)tki{+9FMz6EfWZ$LetUIt+v(0a6#a6^2J3GWaaEYSLIx&6vu z8%D%_!5fMI`k^i=>{?j63E1Ws0&3_fesD1#;1u%>X#SUcNUr$H>;Gro&+E8jR|jxI z_cGy$fZ;aoc}xYww=%f%!%%E|v;uBapujfa>G+3V;42^nankA9WbHgGN~TFLK(}^+ z)`y4i1@b+wb!G=6&8?f4U(d%DzXe07jI>ViwRltez8=N7LfT%9t8$TGC6Y|VS%B3z zm1GV%8xTgD^s1GH8+We)qYU5EioiX05EgqdqulUAKm_QrtbnO7tfgZ@LRA4>gx;zK1`O)drXUQr+Xzf6@q1mE56#2 z#c^dT19u9dB+%kLA(@w~TecwmxsB@^*O{pa><+V#-A@gID`JqP9j{JU!zwDD{Ou<0 zSO6Yk+vOtT?>&D-tq4+-wth1yLhj47_{R1No%g<&k#j^m*jJj?zd!dpYD&#LE@`@G z;uV%&u6YxS`(e;q+gjsWGP>ac_SGXK8kmOs2?1%Y0KxfhC))qw0t0-=Et_+c7Q>9m z1n;U4vL{FU-aD7#cEO%MN(@Veu=6p+USG+mr3E$Q6)vr6qY>U3<<|*sK1*>u;C`Yn zRbu<#pH~{?0t(1&56?Kv1K>AFSQNZRzc$KVN;I%!>g>#yImRnnd0=XlbR~%?B%;RQ z9F8Js0U=+GLlYs%f;L#3px4b4Nn$qcl{kYJw|8!qg-tRcI=Vuh{2C$tUx=DFAg*$& z0#2KCx-#r|w)I9hL`z)kwcEuoy2N|gRj{1FAH}2h`oky55H(Lw2K=4RV}1JHN5vO! zzX4uRaZE_gAqD44x4qx{b=WbF)2;VcO9rD2oXKh2k2_+2N$w;j+YOk=mC~yPw2@uVD}ICXtKyo0j1u(s zWNEz338@e>_@05NAz08ntA);LDhx;VINJs)-`)|zABa4)l{L^DNFya3=i1R{& z9~0Cy*P5S;+SJDwoVN)Xz5882vq$t2M5pJz^tP-KN8jnGD~SCEYi)Sm-zLi{<1soQ zs7UiiD)2;_A~pBKtE_#qwq$#luC6Z~gB9?bSD5d;VvbmaAQpsY$s{WxM}RISqx@iI zgoUCR-&7v9G6r~M!LLw|APD$;#ryMzI(;B=GoTgrl`8rZQ-yIFCs}7F_>S>8>>53n z(MiuJZliB^UT8W>HVgRmz-OyQ-?6jz+*AhdBTB1 zOIj@-=tUsSfiu7)pQW)9enlDrd$3a@LD?!Z73j&&TcT2VeQQc6R_1KkY z+({vy9R>vN5-!+&brt7 zlU?ZdA-2Ijr6_H8W9RLVya}E+KK%Sa{Q1c$)H_tk--G5L0+F2a8q>}7)U!HQiknz| zwpD>a)8jv^X8ZU$AA=^*bg{}6@i*s1rh=u(`ZWkr*kJWt->Za*pSXP#3+5_~Sjku2 zZAKnN4^yB)R^G(pEw@)iCT}+Iz&o9 zkP>MSkQz`zqy&)^5D*Ya0SPHZLg_}jq*J=zJ)Yxv&-tzQ`{!G07Hb?wo*j2w_jTQS zd#`DrAt2zUNtKqSl7@)PTo6SElwA5nygjj;|;qm_Ss9^$N#+- zsYrP}I2p_{BTIK5g^KXUZ#xxlv+Y=5Jy&8C z%T+FxS>c6OM|=w_hHAXxZ|hTv-TU(Nv(HAgO{uioV%Ol=!BjEe!gPx3_LKDu?qTB7 zOPQoNd`!|e_5L}_+n=M}U*$5lP(hJ&g(!!bYo zW4-8&)VdR>h=o+O^1?W*k#*%o@Li9Ihitu7Hsb|Knl+qm)ijiBcDHLw1Lu_B7_axk z1ia(^U~anqaq6=xZ1w&F+De7fFH8VCTeJ3|-5t=E5769L7%`K8c_RdN*DPB4ylSI* zk@G%S|}-2K$qxZtxE@Q>ka<mG40k$DBN!)V-{pRK;z%tkJ+V*6$qKoXQ?o`no8Wnyjapoc* zYrH_}!!ur>&oSeU<}{b_3mX<~2*ES1b|WgCd;&ciPA0A_bf*Ulcnseab_Q$N`!>Ne z|B9xBc8XJdHG$X?!+O2X^7ohv;d#J}>g(oe4Avb?zSq?dRluTgMr~Bh5v+e4bKJQ^ zKqImY$ZW}L_Q;6Jnxofz9{C&gSx23tJ44N9*PxZzZ#GN+S^0p#8cZaUVno#vph!fw zRLze2f&lmA$#OBU*%*i+2&`*X^TgDpnT%UKiO;GT$OdLomMsx|_;ah$d__QLzuI;S zDo@K+W%0{s?gwp;Cs)qmp}c1P&+B7GjqyBAwc9Q4(2_(o2N(|qvM!1x489kAe6VR&0OeOy!)*cm2RN|z;WB+-VN<#n zcTCf^Cyj~?TX!g&Y`UFj>B+cmquUL{7+i7w9p{h?*t>!dRXO;#lhTjYMr5wAsvT}d zb#aUyZqF@hCJQVFi@Gg5H2OfU6kaBZfFaNo<7079O_322hB9htm@oCbIiUAhD_!;O zroVGu`|CTWA8x=GNg8-zdEc$(TMU~x?#Iiy(tP$q1)WBccoCIliu##Bsf>zv zbW^)czmKhFS*Gv(dDn_Zy>DyDQd60#H<8QvHe&RSIUwD!itCS8YygjcL9rH|4jgf1 zP5LXVbwu_I}x^GBJD<}!&>$TMUa<_8i; z&}V#bJQ~WQx*4_pAP`7N>o2~=u{GFqOCAgWRiqF&%BKxFn{>aAQBudY>M25O&*N)VWD4Y%4K%P%t?VR`%*3ro^qY3i?0V?D&C7;{(mmI4Rl>9l? znbeL|KrC|anFC6NVeZ0nXK)eGE-PTHj*b8hd-#9FkUE-Ae(8Iz!d8vcdc0QJT7LSgeHF+}`)PqjmXU%Bl0aQ4B6Vyw z1#S$sH)k9k>V+ru8!vfTj<`N(>o574!aL1Gw*E^)w0Z7`YTYt|}x@agJiQTCZtT$i&EH z9ZFpIH=OPFzX_$w2Nd?|Ruf^s9#HP2Z1&fFOcm~sis=0ckj&AKs4QHp?25)qutyhu zS4ykN{>CW3J=MJ)sSZO&VJ`N8cAN?WG7#5Zdll%nZC7BW%=j3&4KnaD0qp+L{F3P;iL_7Q>4I1O|} zg>mU}4+(5i${#>x-4J=*tNYgV2MFss6W% zIB-leH(gj{+-A|zg#UBlwk&`2StXq+KOgJ@;Gt!kz~XMFKVISRJNPs;2vp5QPXv-qWm(60 zJKYD@KM0A9k~*CsTpiHWL}&Q&=BvgXKxRp6NErNd#LZhNPwr<-^wn$3-+%S2*12v0 z9sF98J`FH8Qimpwh~WHFv}L@73t}RH7C8PM?PI8Ga>wUV)?kN>eAX)#xeZFpn_t~> zc0-8iGH}9rlvT)8LGI~rV_Cnp{4tLnlvZh^j#G4S)teOqnMXT|+4X>PUv09{2{76sSM8SU1V~ZMlBin=C{E$n z^7b!B<3x}*VFp+UolqLru;abe+{fpynF=3qCA5QsF5KJ08AqLqJ4bKNJlo6Nnp|kn ztXFS;`}BW&{RbF!=ha1jY=Ju{XlZ>C{K@aU$`}hr&zM-Zy~kJ!rnix6_#hv$sEx-o z;%SH%ZzeXp0_eKYh#q>xs5)x))uR;N<3D9F{?$Adh(xtf&`ZBc0=@^nh`y|`|7(1W zk@<9EVA*E%Vp_Nlw1@fbn-BE*#xlDKC!3%D7#(o=Q#z!BK!e}%^~H~9irQm;{6Cjc zifLu!{MJ1Q?|T1?r}pMwtQXqEE9sUxkr1%yN9Hv~!tq+EtRA@CKjxJ4Q$emGnf@vJ?)@iW;#mv#N9}oSEZP{a$gxKoC67KJIi& zUp&cDiXJkuFMJD(B-aRDl-wH(*#dz47^EQ&V17#=JT#mES2;5m99Hpun0M}Nymh6^ zs@6fefrB1{pgj@zT?^~ejsN6tAG`rlD3XGAHWPzT!npxp8LOjuf(sxRCklJ4448E$ z3$Die<#xzPcE9Xw&0Au~KGxXUN?AJ}-9C(9%YbXk0W6&{<#~z?0^mqWg5!5P(1vwy zfpwjW2Y9kBh@st0+*Q*-w@)z`CNY+NwQ~<2Zg**}^wU7=l~ew|=#b~I7fnEm{|Pjr zjZ?!iYuY>M;y0?p-v^9;Dc-D-m~&V?D%_d-@-vwo7>xWw$A2-#5d1Yah#B)MfdJT; z?kr_R7xmrA+#539d<&#--x}x6<=?Tb=O-LAPp4wAh^qMokX=1cu0i&3vvd~E);il( zL$-HRga@1p^;18@XWKL5M?0QZWqTO)Qf@p$hF}Vr`RxtnN&{qq!40e)pGrWv;cVT> zW4oDrxUii7B{XWwR0lGL*nEs!c?*?&Ejq1LKkgv|hf03ujUU}t4pc#L_p<9*)^Ag% zVri7piH!YO^BV--B*_zm`eLrhS!)1n_nOzTwolh#J%rk^2jsWi!&Ff>uU;eSYc|)2 z5T_Nsud=yS!V`Q{Ip?irXyAUF>o~nG3982rzj<4-s29x`6^d9T$d2$=-1n{tAeDWi zZv1-|uP}!UEF+cB(fnWySbPmggB1spKEoBCq9yke{dZEPmcZH#mD}~3z;?SqYlweS z&181?^WP=IbvjG}hyPbow|gM66V~&go$I6;VCI9=Plm8!SNrlRS8fsPYpw>E_Faj+ znp>w87hM+E5a7p+Q&MZAV=JxsrJU9C+1RxhhrmD#EPwy6VZ=7*PKYPmb-Oz1sUq5* zQ+}QkVs$@X8Lxc3qB%h4v-Spb0n7)3F&)|ib#2JXR005>j( zxRL)GH!hI`CSx$10Hh{X8AZvz;0FZv$9F1B(Z6I3J*_vb^a0~3z5M``2*Eaw+!C|- znfJJ>pUS15Fi&tSPsN(X&s)t7C6q^IcgBL#XAW<&Et$^>!X*QJUyrF4{p@Ky!4<{+ zC0I5>bNKM3hzJGVI=)J}x#9O48kBzDgBp9@$ZcLM2sa#USVoCjzx}&^=0#IR{YMM} z5hQ0}d2E;Th3{*(F45xGE-jS&Y5vE{ukOtL-lux0%2YE?Fs+(6SDF7z%Nh+pV=tiv z<%4Ki@kc#C4c6Dd6ZJnmJVH0LaPqj^-oX;=KE^=@66?6>Lzwn1^i?A`r6f5zpWm#sEuJezjAN!Q2?65F>s@#JljcCtYRMbIrI8;5M=#d^y%&lH*nr9;xuU*4jTvZF=(k| z(808}>HZ(Z;M~3P`g1amiFcL84NM*19W`e)T^rE7km6MIgg{LB(`LO-G(70}pR*;EdPri?NZ1pB+QLa~%K;EP??h$2`bAGX) zVyr4O$@YyUaQj^Bw)^{;(8uYF+%iYq_Z!kLRQ#v5sNFCSVhWA%y!HI8Z??zjNJdW7in`?qzsy+YPuWc2lJM^`=U z6s~*M&=?ZCA7B22Ih39tfa=VHOmU{fuG3bTK)@t$?)od?_Z0cyI$F^w4xHjBwf+5f zvpUUklKz`GLK#|yH%H|N@* zDJ8}I({}=zzjIvQ>CRCL?v_(%%dxA+zh|_>7pT~fw?OZ5cOU8T&AF~NDM4JE>xX@A zubGma;iAfMa`yB7G$PtX^5UW%Y6`!HvDw8#&Nnr{`5G=#3K8e P~g6DRQPLtD* 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
kA} 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 xwc|Us>@v+&5H3}BN{{vk7WbOa} literal 0 HcmV?d00001 diff --git a/arbitrum-docs/assets/submit-tx-to-sequencer.png b/arbitrum-docs/assets/submit-tx-to-sequencer.png new file mode 100644 index 0000000000000000000000000000000000000000..b376f9c0c448c815278e0914b9c0e941ebe64bac GIT binary patch literal 24780 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 Ch@guA literal 0 HcmV?d00001 diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index 9304512dd..f4162db2c 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -41,12 +41,18 @@ Please see the [Arbitrum Node](https://docs.arbitrum.io/run-arbitrum-node/overvi The Sequencer Endpoint is the most direct method for users looking to minimize delays in their transactions reaching the sequencer. Unlike standard RPC URLs, the Sequencer Endpoint supports only `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls, bypassing the load balancer entirely. This endpoint makes it an optimal choice for users who require the quickest transaction processing time. +The diagram below shows different ways to submit transactions to the sequencer: + +![submit-transaction-to-sequencer](../assets/submit-tx-to-sequencer.png) + ## Bypassing the Sequencer This section delves into an alternative method for submitting transactions to the Arbitrum chain, bypassing the sequencer. This page focuses on how users can send their transactions directly to the delayed inbox contract on the parent chain rather than through the sequencer. This method offers two distinct paths a transaction can take, with each route interacting with the network differently to achieve transaction inclusion. This approach provides users with greater flexibility and ensures that transactions can still be processed if the sequencer is unavailable or if users choose not to depend on it. This section highlights these alternative submission mechanisms and underscores the robustness and decentralization features inherent in the Arbitrum network. In **Diagram 3**, we demonstrate how users can submit their transactions using the delayed inbox contract to bypass the sequencer. As illustrated in the diagram, there are two possible paths for transaction handling. When a transaction is submitted to the delayed inbox, the sequencer may automatically pick it up, include it as an ordered transaction, and send it to the sequencer feed. However, if the sequencer does not process the transaction within 24 hours, users have the reliable option to call the `forceInclude` function on the sequencer inbox contract. This action ensures that the sequencer to picks up the transaction and includes it in the ordered transaction list, providing users with a sense of security about their transactions. +![bypassing-the-sequencer](../assets/bypassing-the-sequencer.png) + To send a transaction to the delayed inbox instead of submitting it to the sequencer, users can construct their transaction and then call the [`sendL2Message`](https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/bridge/AbsInbox.sol#L150) function, passing the data of the serialized signed transaction as an argument. This function allows users to send a generic L2 message to the chain, suitable for any message that does not require L1 validation. If the sequencer is not back online within 24 hours or decides to censor the transaction, users can invoke the [`forceInclusion`](https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/bridge/SequencerInbox.sol#L284) function on the SequencerInbox contract. This action ensures their transaction is included on the chain, bypassing the sequencer's role. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index e03c8c543..d652e5e7c 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -9,6 +9,8 @@ content_type: get-started The Sequencer is a pivotal component of the Arbitrum network and is responsible for efficiently ordering and processing transactions. It plays a crucial role in providing users with fast transaction confirmations while maintaining the security and integrity of the blockchain. In Arbitrum, the Sequencer orders incoming transactions and manages the batching, compression, and posting of transaction data to parent chain, optimizing costs and performance. +![sequencer-operations](../assets/sequencer-operations.png) + In this section, we will explore the operation of the Sequencer in detail. The topics covered include: - [Sequencing and Broadcasting (Sequencer Feed)](#sequencing-and-broadcasting): An overview of the real-time transaction feed provided by the Sequencer, which allows nodes to receive instant updates on the transaction sequence. @@ -29,7 +31,9 @@ The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro a The Sequencer communicates the transaction sequence through two primary channels: 1. **Real-Time Sequencer Feed**: A live broadcast that publishes transactions instantly as they are sequenced. Nodes and clients subscribed to this feed receive immediate notifications, allowing them to process transactions without delay. -2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process) +2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process.) + +![sequencer-feed](../assets/sequencer-feed.png) ### Real-Time Sequencer Feed @@ -93,6 +97,8 @@ Understanding Batch-Posting is essential for grasping how Arbitrum achieves scal The Sequencer in Arbitrum is critical in collecting and organizing child chain transactions before posting them to the parent chain. The batching process is designed to optimize for both cost efficiency and timely transaction inclusion. +![batching](../assets/batching.png) + **Transaction Collection and Ordering:** - **Continuous Reception:** The Sequencer continuously receives transactions submitted by users. @@ -116,6 +122,8 @@ This batching mechanism allows the Sequencer to efficiently manage transactions The Sequencer employs compression when forming transaction batches to optimize the data and cost of batches posted to the parent chain. Arbitrum uses the Brotli compression algorithm due to its high compression ratio and efficiency, crucial for reducing parent chain posting costs. +![compression](../assets/compression.png) + ### Compression level in the Brotli algorithm Brotli’s compression algorithm includes a parameter: **compression level**, which ranges from **0 to 11**. This parameter allows you to balance two key factors: @@ -139,6 +147,8 @@ After batching and compressing transactions, the Sequencer posts these batches t There are two primary methods the Sequencer uses to send batches to the parent chain, depending on whether the chain supports EIP-4844 (Proto-Danksharding) and the current network conditions: +![submit-to-sequencer-inbox](../assets/submit-to-sequencer-inbox.png) + ### 1. Using Blobs with `addSequencerL2BatchFromBlobs` - **Default Approach**: When the parent chain supports EIP-4844, the Sequencer utilizes blob transactions to post batches efficiently. diff --git a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md b/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md new file mode 100644 index 000000000..443b570f9 --- /dev/null +++ b/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md @@ -0,0 +1,679 @@ +--- +title: 'Inside Arbitrum Nitro' +sidebar_label: 'Deep dive: Inside Arbitrum' +description: 'Learn the fundamentals of Nitro, Arbitrum stack.' +author: dzgoldman +sme: dzgoldman +user_story: As a current or prospective Arbitrum user, I need learn more about Nitros design. +content_type: get-started +--- + +import ImageWithCaption from '@site/src/components/ImageCaptions/'; + +This document is a deep-dive explanation of
Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. + +The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. + +## Why use Arbitrum? Why use Nitro? + +Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: + +- Trustless security: security rooted in Ethereum, with any one + party able to ensure correct Layer 2 results +- Compatibility with Ethereum: able to run unmodified EVM contracts and unmodified Ethereum transactions +- Scalability: moving contracts’ computation and storage off of the main Ethereum chain, allowing much higher throughput +- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-Transaction cost. + +Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. + +Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in several ways: + +- **Advanced Calldata Compression,** which further drives down transaction costs on Arbitrum by reducing the amount of data posted to L1. +- **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. +- **Ethereum L1 Gas Compatibility,** bringing pricing and accounting for EVM operations perfectly in line with Ethereum. +- **Additional L1 Interoperability,** including tighter synchronization with L1 Block numbers, and full support for all Ethereum L1 precompiles. +- **Safe Retryables,** eliminating the failure mode where a Retryable Ticket fails to get created. +- **Geth Tracing,** for even broader debugging support. +- And many, many more changes. + +## The Big Picture + +At the most basic level, an Arbitrum chain works like this: + + + +Users and contracts put messages into the inbox. The chain reads the messages one at a time, and processes each one. This updates the state of the chain and produces some outputs. + +If you want an Arbitrum chain to process a transaction for you, you need to put that transaction into the chain’s inbox. Then the chain will see your transaction, execute it, and produce some outputs: a transaction receipt, and any withdrawals that your transaction initiated. + +Execution is deterministic -- which means that the chain’s behavior is uniquely determined by the contents of its inbox. Because of this, the result of your transaction is knowable as soon as your transaction has been put in the inbox. Any Arbitrum node will be able to tell you the result. (And you can run an Arbitrum node yourself if you want.) + +All of the technical detail in this document is connected to this diagram. To get from this diagram to a full description of Arbitrum, we’ll need to answer questions like these: + +- Who keeps track of the inbox, Chain state, and outputs? +- How does Arbitrum make sure that the chain state and outputs are correct? +- How can Ethereum users and contracts interact with Arbitrum? +- How does Arbitrum support Ethereum-compatible contracts and transactions? +- How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? +- How can I run my own Arbitrum node or Validator? + +## Nitro's Design: The Four Big Ideas + +The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. + +**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic State Transition Function. + +**Big Idea: Geth at the Core**: Nitro supports Ethereum's data structures, formats, and virtual machine by compiling in the core code of the popular go-ethereum ("Geth") Ethereum node software. Using Geth as a library in this way ensures a very high degree of compatibility with Ethereum. + +**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. + +**Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. + +## Sequencing, Followed by Deterministic Execution + +This diagram summarizes how transactions are processed in Nitro. + +![seq-then-exec](../assets/seq-then-exec.png) + +Let's follow a user's transaction through this process. + +First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. + +Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. + +Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) + +The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. + +It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. + +### How the Sequencer Publishes the Sequence + +So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. + +The real-time feed is published by the Sequencer so that anyone who subscribes to the feed receives instant notifications of each transaction as it is sequenced. Nitro nodes can subscribe to the feed directly from the Sequencer, or through a relay that forwards the feed. The feed represents the Sequencer's promise that it will record transactions in a particular order. If the Sequencer is honest and doesn't have a long downtime, this promise will be kept. So anyone who trusts the Sequencer to keep its promises can rely on the feed to get instant information about the transaction sequence--and they can run the sequenced transactions through the state transition function to learn the results of each transaction immediately. This is "soft finality" for transactions; it's "soft" because it depends on the Sequencer keeping its promises. + +The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically--perhaps every few minutes in production--the Sequencer concatenates the next group of transactions in the feed, compresses them for efficiency, and posts the result as calldata on Ethereum. This is the final and official record of the transaction sequence. As soon as this Ethereum transaction has finality on Ethereum, the Layer 2 Nitro transactions it records will have finality. These transactions are final because their position in the sequence has finality, and the outcome of the transactions is deterministic and knowable to any party. This is "hard finality". + +The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. + +## Geth at the Core + +The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. + +![geth-sandwich](../assets/geth-sandwich.png) + +The software that makes up a Nitro node can be thought of as built in three main layers, which are shown above: + +- The base layer is the core of geth--the parts of Geth that emulate the execution of EVM contracts and maintain the data structures that make up the Ethereum state. Nitro compiles in this code as a library, with a few minor modifications to add necessary hooks. +- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain Bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. +- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible Blockchain node. + +Because the top and bottom layers rely heavily on code from geth, this structure has been dubbed a "geth sandwich." Strictly speaking, Geth plays the role of the bread in the sandwich, and ArbOS is the filling, but this sandwich is named for the bread. + +The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. + +## Separating Execution from Proving + +One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. + +When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) + +Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. + +#### WAVM + +The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. + +WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. + +Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. + +Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. + +#### ReadPreImage and the Hash Oracle Trick + +The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. + +(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) + +As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. + +The only other use of `ReadPreImage` is to fetch the contents of recent L2 Block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. + +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. + +## Optimistic Rollup + +Arbitrum is an optimistic rollup. Let’s unpack that term. + +_Rollup_ + +Arbitrum is a rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. + +This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. + +_Optimistic_ + +Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to Challenge that claim. If the Challenge Period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). + +Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. + +## Resolving disputes using interactive fraud proofs + +Among optimistic rollups, the most important design decision is how to resolve disputes. Suppose Alice claims that the chain will produce a certain result, and Bob disagrees. How will the protocol decide which version to accept? + +There are basically two choices: interactive proving, or re-executing transactions. Arbitrum uses interactive proving, which we believe is more efficient and more flexible. Much of the design of Arbitrum follows from this fact. + +### Interactive proving + +The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. + +Arbitrum's approach is based on Dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. + +The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. + +### Re-executing transactions + +The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. + +### Why interactive proving is better + +We believe strongly that interactive proving is the superior approach, for the following reasons. + +**More efficient in the optimistic case**: Because interactive proving can resolve disputes that are larger than one transaction, it can allow a rollup block to contain only a single claim about the end state of the chain after all of the execution covered by the block. By contrast, reexecution requires posting a state claim for each transaction within the rollup block. With hundred or thousands of transactions per rollup block, this is a substantial difference in L1 footprint -- and L1 footprint is the main component of cost. + +**More efficient in the pessimistic case**: In case of a dispute, interactive proving requires the L1 referee contract only to check that Alice and Bob's actions "have the right shape", for example, that Alice has divided her N-step claim into two claims half as large. (The referee doesn't need to evaluate the correctness of Alice's claims--Bob does that, off-chain.) Only one instruction needs to be reexecuted. By contrast, reexecution requires the L1 referee to emulate the execution of an entire transaction. + +**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive Fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. + +**More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. + +### Interactive proving drives the design of Arbitrum + +Much of the design of Arbitrum is driven by the opportunities opened up by interactive proving. If you're reading about some feature of Arbitrum, and you're wondering why it exists, two good questions to ask are: "How does this support interactive proving?" and "How does this take advantage of interactive proving?" The answers to most "why" questions about Arbitrum relate to interactive proving. + +## Arbitrum Rollup Protocol + +Before diving into the rollup protocol, there are two things we need to cover. + +First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. + +You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! + +The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) + +You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. + +With those preliminaries behind us, let’s jump into the details of the rollup protocol. + +The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). + +The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. + +### The Rollup Chain + +The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum Rollup Protocol manages and oversees. + +Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. + +:::note + +Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and chain state, whereas proposers can also assert and challenge the chain state. + +::: + +Each RBlock contains: + +- the RBlock number +- the predecessor RBlock number: RBlock number of the last RBlock before this one that is (claimed to be) correct +- the number of L2 blocks that have been created in the chain's history +- the number of inbox messages that have been consumed in the chain’s history +- a hash of the outputs produced over the chain’s history. + +Except for the RBlock number, the contents of the RBlock are all just claims by the RBlock's proposer. Arbitrum doesn’t know at first whether any of these fields are correct. If all of these fields are correct, the protocol should eventually confirm the RBlock. If one or more of these fields are incorrect, the protocol should eventually reject the RBlock. + +An RBlock is implicitly claiming that its predecessor RBlock is correct. This implies, transitively, that an RBlock implicitly claims the correctness of a complete history of the chain: a sequence of ancestor RBlocks that reaches all the way back to the birth of the chain. + +An RBlock is also implicitly claiming that its older siblings (older RBlocks with the same predecessor), if there are any, are incorrect. If two RBlocks are siblings, and the older sibling is correct, then the younger sibling is considered incorrect, even if everything else in the younger sibling is true. + +The RBlock is assigned a deadline, which says how long other validators have to respond to it. If you’re a validator, and you agree that an RBlock is correct, you don’t need to do anything. If you disagree with an RBlock, you can post another RBlock with a different result, and you’ll probably end up in a challenge against the first RBlock's bonder. (More on challenges below.) + +In the normal case, the rollup chain will look like this: + +```mermaid +graph RL + f["First unresolved block"] + l["Latest confirmed block"] + + 98-->97-->96-->95 + f-.-95 + + 95-->94 + l-.-94 + + 94-->93-->92-->91-->90-->x[...] + + subgraph Legend + direction RL + Confirmed + Unconfirmed + end + + classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff + class 94,93,92,91,90,x,Confirmed confirmed + + + classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff + class 98,97,96,95,Unconfirmed unconfirmed + + classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 + class l,f note +``` + +On the left, representing an earlier part of the chain’s history, we have confirmed RBlocks. These have been fully accepted and recorded by the Layer 1 contracts that manage the chain. The newest of the confirmed RBlocks, RBlock 94, is called the “latest confirmed RBlock.” On the right, we see a set of newer proposed RBlocks. The protocol can’t yet confirm or reject them, because their deadlines haven’t run out yet. The oldest RBlock whose fate has yet to be determined, RBlock 95, is called the “first unresolved RBlock.” + +Notice that a proposed RBlock can build on an earlier proposed RBlock. This allows validators to continue proposing RBlocks without needing to wait for the protocol to confirm the previous one. Normally, all of the proposed RBlocks will be valid, so they will all eventually be accepted. + +Here’s another example of what the chain state might look like, if several validators are being malicious. It’s a contrived example, designed to illustrate a variety of cases that can come up in the protocol, all smashed into a single scenario. + +```mermaid +graph RL + subgraph Legend + direction RL + Confirmed + Rejected + Unconfirmed + end + + f["First unresolved block"] + l["Latest confirmed block"] + + + l-.-103 + f-.-106 + + 108-->107-->106-->103 + 111-->104 + 101-->100 + 105-->104-->103 + 110-->109-->103-->102-->100-->x[...] + + classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff + class 100,102,103,x,Confirmed confirmed + + classDef rejected fill:#fdaa07,stroke:#fd8607,stroke-width:2px,color:#fff + class 101,104,105,Rejected rejected + + classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff + class 106,107,108,109,110,111,Unconfirmed unconfirmed + + classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 + class l,f note + +``` + +There’s a lot going on here, so let’s unpack it. + +- RBlock 100 has been confirmed. +- RBlock 101 claimed to be a correct successor to RBlock 100, but 101 was rejected (hence it is orange). +- RBlock 102 was eventually confirmed as the correct successor to 100. +- RBlock 103 was confirmed and is now the latest confirmed RBlock. +- RBlock 104 was proposed as a successor to RBlock 103, and 105 was proposed as a successor to 104. 104 was rejected as incorrect, and as a consequence 105 was rejected because its predecessor was rejected. +- RBlock 106 is unresolved. It claims to be a correct successor to RBlock 103 but the protocol hasn’t yet decided whether to confirm or reject it. It is the first unresolved RBlock. +- RBlocks 107 and 108 claim to chain from 106. They are also unresolved. If 106 is rejected, they will be automatically rejected too. +- RBlock 109 disagrees with RBlock 106, because they both claim the same predecessor. At least one of them will eventually be rejected, but the protocol hasn’t yet resolved them. +- RBlock 110 claims to follow 109. It is unresolved. If 109 is rejected, 110 will be automatically rejected too. +- RBlock 111 claims to follow 104. 111 will inevitably be rejected because its predecessor has already been rejected. But it hasn’t been rejected yet, because the protocol resolves RBlocks in RBlock number order, so the protocol will have to resolve 106 through 110, in order, before it can resolve 111. After 110 has been resolved, 111 can be rejected immediately. + +Again: this sort of thing is very unlikely in practice. In this diagram, at least four parties must have bonded on wrong RBlocks, and when the dust settles at least four parties will have lost their bonds. The protocol handles these cases correctly, of course, but they’re rare corner cases. This diagram is designed to illustrate the variety of situations that are possible in principle, and how the protocol would deal with them. + +### Staking + +At any given time, some validators will be bonders, and some will not. Bonders deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the bonder loses a challenge. Nitro chains accept bonds in ETH. + +A single bond can cover a chain of RBlocks. Every bonder is bonded on the latest confirmed RBlock; and if you’re bonded on an RBlock, you can also bond on one successor of that RBlock. So you might be bonded on a sequence of RBlocks that represent a single coherent claim about the correct history of the chain. A single bond suffices to commit you to that sequence of RBlocks. + +In order to create a new RBlock, you must be a bonder, and you must already be bonded on the predecessor of the new RBlock you’re creating. The bond requirement for RBlock creation ensures that anyone who creates a new RBlock has something to lose if that RBlock is eventually rejected. + +The protocol keeps track of the current required bond amount. Normally this will equal the base bond amount, which is a parameter of the Nitro chain. But if the chain has been slow to make progress lately, the required bond will increase, as described in more detail below. + +The rules for staking are as follows: + +- If you’re not bonded, you can bond on the latest confirmed RBlock. When doing this, you deposit the current minimum bond amount. +- If you’re bonded on an RBlock, you can also add your bond to any one successor of that RBlock. (The protocol tracks the maximum RBlock number you’re bonded on, and lets you add your bond to any successor of that RBlock, updating your maximum to that successor.) This doesn’t require you to place a new bond. + - A special case of adding your bond to a successor RBlock is when you create a new RBlock as a successor to an RBlock you’re already bonded on. +- If you’re bonded only on the latest confirmed RBlock (and possibly earlier RBlocks), you or anyone else can ask to have your bond refunded. Your bonded funds will be returned to you, and you will no longer be a bonder. +- If you lose a challenge, your bond is removed from all RBlocks and you forfeit your bonded funds. + +Notice that once you are bonded on an unresolved RBlock, there is no way to unbond. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your bond. The only way to get your bond back is to wait until all of the RBlocks you are bonded on are confirmed. + +#### Setting the current minimum bond amount + +One detail we deferred earlier is how the current minimum bond amount is set. Normally, this is just equal to the base bond amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the bond requirement will escalate temporarily. Specifically, the base bond amount is multiplied by a factor that is exponential in the time since the deadline of the first unresolved RBlock passed. This ensures that if malicious parties are placing false bonds to try to delay progress (despite the fact that they’re losing those bonds), the bond requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the bond requirement will go back down. + +### Rules for Confirming or Rejecting RBlocks + +The rules for resolving RBlocks are fairly simple. + +The first unresolved RBlock can be confirmed if: + +- the RBlock's predecessor is the latest confirmed RBlock, and +- the RBlock's deadline has passed, and +- there is at least one bonder, and +- All bonders are bonded to the RBlock. + +The first unresolved RBlock can be rejected if: + +- the RBlock's predecessor has been rejected, or +- all of the following are true: + - the RBlock's deadline has passed, and + - there is at least one bonder, and + - no bonder is bonded on the RBlock. + +A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. + +## Challenges + +Suppose the rollup chain looks like this: + +![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) + +RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. + +At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) + +Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. + +The challenge is a game in which Alice and Bob alternate moves, with an Ethereum contract as the referee. Alice, the defender, moves first. + +The game will operate in two phases: dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. + +We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. + +### Dissection Protocol: Simplified Version + +Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. + +Alice’s first move requires her to dissect her claims about intermediate states between the beginning (0 instructions executed) and the end (N instructions executed). So we require Alice to divide her claim in half, and post the state at the half-way point, after N/2 instructions have been executed. + +Now Alice has effectively bisected her N-step Assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. + +At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. + +### Why Dissection Correctly Identifies a Cheater + +Before talking about the complexities of the real Challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. + +To prove (1), observe that if Alice’s initial claim is correct, she can offer a truthful midpoint claim, and both of the implied half-size claims will be correct. So whichever half Bob objects to, Alice will again be in the position of defending a correct claim. At each stage of the protocol, Alice will be defending a correct claim. At the end, Alice will have a correct one-step claim to prove, so that claim will be provable and Alice can win the challenge. + +To prove (2), observe that if Alice’s initial claim is incorrect, this can only be because her claimed endpoint after N steps is incorrect. Now when Alice offers her midpoint state claim, that midpoint claim is either correct or incorrect. If it’s incorrect, then Bob can challenge Alice’s first-half claim, which will be incorrect. If Alice’s midpoint state claim is correct, then her second-half claim must be incorrect, so Bob can challenge that. So whatever Alice does, Bob will be able to challenge an incorrect half-size claim. At each stage of the protocol, Bob can identify an incorrect claim to challenge. At the end, Alice will have an incorrect one-step claim to prove, which she will be unable to do, so Bob can win the challenge. + +(If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) + +### The Real Dissection Protocol + +The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. + +**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). + +**K-way dissection:** Rather than dividing a claim into two segments of size N/2, we divide it into K segments of size N/K. This requires posting K-1 intermediate claims, at points evenly spaced through the claimed execution. This reduces the number of rounds by a factor of log(K)/log(2). + +**Answer a dissection with a dissection:** Rather than having each round of the protocol require two moves, where Alice dissects and Bob chooses a segment to challenge, we instead require Bob, in challenging a segment, to post his own claimed endpoint state for that segment (which must differ from Alice’s) as well as his own dissection of his version of the segment. Alice will then respond by identifying a subsegment, posting an alternative endpoint for that segment, and dissecting it. This reduces the number of moves in the game by an additional factor of 2, because the size is cut by a factor of K for every move, rather than for every two moves. + +**Deal With the Empty-Inbox Case**: The real AVM can’t always execute N units of gas without getting stuck. The machine might halt, or it might have to wait because its inbox is exhausted so it can’t go on until more messages arrive. So Bob must be allowed to respond to Alice’s claim of N units of execution by claiming that N steps are not possible. The real protocol thus allows any response (but not the initial claim) to claim a special end state that means essentially that the specified amount of execution is not possible under the current conditions. + +**Time Limits:** Each player is given a time allowance. The total time a player uses for all of their moves must be less than the time allowance, or they lose the game. Think of the time allowance as being about a week. + +It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. + +### Efficiency + +The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. + +The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. + +## Validators + +Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. + +Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. + +Offchain Labs provides open source validator software, including +a pre-built Docker image. + +Every validator can choose their own approach, but we expect validators to follow three common strategies: + +- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An Active Validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. +- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. +- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) + +Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. + +The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). + +Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. + +- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. +- Parties who have significant assets at bond on a chain, such as dApp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. +- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. + +## ArbOS + +ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. + +### Why ArbOS? + +In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. + +Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. + +## Full Nodes + +As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. + +Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. + +## The Sequencer + +The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. + +Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. + +[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. + +### Instant confirmation + +Without a Sequencer, a node can predict what the results of a Client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. + +The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. + +### Inboxes, fast and slow + +When we add a Sequencer, the operation of the inbox changes. + +- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) +- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "Delayed Inbox" queue, which is managed by an L1 Ethereum contract. + - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. + - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) + +### If the Sequencer is well-behaved... + +A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. + +It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 Confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. + +This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. + +This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. + +So a Sequencer is generally a win, if the Sequencer is well behaved. + +### If the Sequencer is malicious... + +A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. + +On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. + +Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. + +### Decentralized fair sequencing + +Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. + +How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. + +## Bridging + +We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. + +The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). + +### L1 contracts can submit L2 transactions + +An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. + +The advantage of this method is that it is simple and has relatively low latency. The disadvantage, compared to the other method we’ll describe soon, is that the L2 transaction might revert if the L1 caller doesn’t get the L2 gas price and max gas amount right. Because the L1 caller can’t see the result of its L2 transaction, it can’t be absolutely sure that its L2 transaction will succeed. + +This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. + +### L1 to L2 ticket-based transactions + +Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. + +When saving a transaction for retry, Nitro records the sender’s address, destination address, callvalue, and calldata. All of this is saved, and the callvalue is deducted from the sender’s account and (logically) attached to the saved transaction. + +If the redemption succeeds, the transaction is done, a receipt is issued for it, and the ticketID is canceled and can’t be used again. If the redemption fails, for example because the packaged transaction fails, the redemption reports failure and the ticketID remains available for redemption. + +Normally the original submitter will try to cause their transaction to succeed immediately, so it never needs to be recorded or retried. As an example, our "token deposit" use case above should, in the happy, common case, still only require a single signature from the user. If this initial execution fails, the ticketID will still exist as a backstop which others can redeem later. + +Submitting a transaction in this way carries a price in ETH which the submitter must pay, which varies based on the calldata size of the transaction. Once submitted, the ticket is valid for about a week. If the ticket has not been redeemed in that period, it is deleted. + +When the ticket is redeemed, the pre-packaged transaction runs with sender and origin equal to the original submitter, and with the destination, callvalue, and calldata the submitter provided at the time of submission. + +This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. + +### L2 to L1 ticket-based calls + +Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 Outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. + +These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) + +## Gas and Fees + +NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. + +### The Speed Limit + +The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. + +This sets an effective Speed Limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. + +Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. + +### Fees + +User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. + +Fees are charged for two resources that a transaction can use: + +- _L2 gas_: an Ethereum-equivalent amount of gas, as required to execute the transaction on the Nitro chain, + +* _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, + +#### L2 gas fees + +L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. + +The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. + +The algorithm compares gas usage against a parameter called the "speed limit" which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. + +Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. + +To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. + +#### L1 calldata fees + +L1 calldata fees exist because the Sequencer, or the Batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. + +Every transaction that comes in through the Sequencer will pay an L1 calldata fee. Transactions that come in through the delayed inbox do not pay this fee because they don't add to batch posting costs--but these transactions pay gas fees to Ethereum when they are put into the delayed inbox. + +The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transaction. First, it computes the transaction's size, which is an estimate of how many bytes the transaction will add to the compressed batch it is in; the formula for this includes an estimate of how compressible the transaction is. Second, it multiplies the computed size estimate by the current price per estimated byte, to determine the transaction's L1 calldata wei, in wei. Finally, it divides this cost by the current L2 basefee to translate the fee into L2 gas units. The result is reported as the "poster fee" for the transaction. + +The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. + +#### Total fee and gas estimation + +The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. + +## Inside AnyTrust + +AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. + +The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to bond on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. + +AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. + +### Keysets + +A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. + +A Keyset contains + +- the number of Committee members, and +- for each Committee member, a BLS public key, and +- the number of Committee signatures required. + +Keysets are identified by their hashes. + +An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. + +Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. + +### Data Availability Certificates + +A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: + +- the hash of a data block, and +- an expiration time, and +- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of + - the hash of the Keyset used in signing, and + - a bitmap saying which Committee members signed, and + - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. + +Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. + +In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. + +AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. + +The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that + +- the number of signers is at least the number required by the Keyset, and +- the aggregated signature is valid for the claimed signers, and +- the expiration time is at least two weeks after the current L2 timestamp. + +If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. + +### Data Availability Servers + +Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: + +- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. +- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. + +Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) + +The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. + +### Sequencer-Committee Interaction + +When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. + +Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. + +If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly. From ebc9ae415e2bb1f5e02028d2215a7c9bb3027a70 Mon Sep 17 00:00:00 2001 From: Pete Date: Fri, 27 Dec 2024 14:13:33 -0600 Subject: [PATCH 75/75] added quicklooks --- .../01-a-gentle-introduction.mdx | 23 +- .../02-transaction-lifecycle.mdx | 8 +- .../how-arbitrum-works/03-sequencer.mdx | 12 +- .../04-geth-at-the-core.mdx | 45 +- .../05-separating-execution-from-proving.mdx | 8 +- .../06-optimistic-rollup.mdx | 33 +- .../07-interactive-fraud-proofs.mdx | 38 +- .../08-anytrust-protocol.mdx | 10 +- .../how-arbitrum-works/09-gas-fees.mdx | 16 +- .../10-l1-to-l2-messaging.mdx | 40 +- .../11-l2-to-l1-messaging.mdx | 14 +- .../inside-arbitrum-nitro.md | 679 ------------------ 12 files changed, 128 insertions(+), 798 deletions(-) delete mode 100644 arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md diff --git a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx index 9cb237a09..e8ae0214d 100644 --- a/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx +++ b/arbitrum-docs/how-arbitrum-works/01-a-gentle-introduction.mdx @@ -9,18 +9,19 @@ content_type: get-started import ImageWithCaption from '@site/src/components/ImageCaptions/'; -This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. +This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. -The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called [AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx), which is used by the Arbitrum Nova chain. +The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called [AnyTrust](/how-arbitrum-works/08-anytrust-protocol.mdx), which is used by the Arbitrum Nova chain. ## Why use Arbitrum? Why use Nitro? Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: -- Trustless security: security rooted in Ethereum, with any one party able to ensure correct Layer 2 results +- Trustless security: security rooted in Ethereum, with any one + party able to ensure correct Layer 2 results - Compatibility with Ethereum: able to run unmodified EVM contracts and unmodified Ethereum transactions - Scalability: moving contracts’ computation and storage off of the main Ethereum chain, allowing much higher throughput -- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-transaction cost. +- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-Transaction cost. Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. @@ -30,13 +31,13 @@ Nitro is a major upgrade to Arbitrum including: - **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. - **Ethereum L1 Gas Compatibility,** bringing pricing and accounting for EVM operations perfectly in line with Ethereum. - **Additional L1 Interoperability,** including tighter synchronization with L1 Block numbers, and full support for all Ethereum L1 precompiles. -- **Safe Retryables,** eliminating the failure mode where a retryable ticket fails to get created. -- **Geth Tracing,** for even broader debugging support. +- **Safe Retryables,** eliminating the failure mode where a Retryable Ticket fails to get created. +- **Geth Tracing,** for even broader debugging support. - And many, many more changes. ## The Big Picture -At the most basic level, an Arbitrum chain works like this: +At the most basic level, an Arbitrum chain works like this: Chain state, and outputs? - How does Arbitrum make sure that the chain state and outputs are correct? - How can Ethereum users and contracts interact with Arbitrum? - How does Arbitrum support Ethereum-compatible contracts and transactions? - How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? -- How can I run my own Arbitrum node or validator? +- How can I run my own Arbitrum node or Validator? ## Nitro's Design: The Four Big Ideas The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. -**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic state transition function. +**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic State Transition Function. **Big Idea: Geth at the Core**: Nitro supports Ethereum's data structures, formats, and virtual machine by compiling in the core code of the popular go-ethereum ("Geth") Ethereum node software. Using Geth as a library in this way ensures a very high degree of compatibility with Ethereum. -**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. +**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. **Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. diff --git a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx index f4162db2c..2a5f063e8 100644 --- a/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx +++ b/arbitrum-docs/how-arbitrum-works/02-transaction-lifecycle.mdx @@ -7,11 +7,11 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -This section explores the various methods users can employ to submit transactions for inclusion on the Arbitrum chain. We discuss the different pathways available—sending transactions to the sequencer or bypassing it by submitting transactions through the delayed inbox contract on the parent chain. By outlining these options, we aim to clarify how users can interact with the network, detail the processes involved in each method, and identify the modules responsible for handling these transactions. This overview will enhance your understanding of the initial steps in Arbitrum ecosystem's transaction lifecycle and prepare you for a detailed exploration of transaction inclusion mechanisms in the subsequent sections. +This section explores the various methods users can employ to submit transactions for inclusion on the Arbitrum chain. We discuss the different pathways available—sending transactions to the Sequencer or bypassing it by submitting transactions through the Delayed Inbox contract on the Parent chain. By outlining these options, we aim to clarify how users can interact with the network, detail the processes involved in each method, and identify the modules responsible for handling these transactions. This overview will enhance your understanding of the initial steps in Arbitrum ecosystem's Transaction lifecycle and prepare you for a detailed exploration of transaction inclusion mechanisms in the subsequent sections. The first subsection, [Submitting Transactions to the Sequencer](#submitting-transactions-to-the-sequencer), presents four different methods users can utilize to send their transactions to the sequencer: via Public RPC, Third-Party RPC, Arbitrum Nodes, and the Sequencer Endpoint. Transactions sent through the first three pathways will route through our Load Balancer before reaching the sequencer. In contrast, the Sequencer Endpoint allows transactions to bypass the Load Balancer and be sent directly to the sequencer. -The second subsection, [Bypassing the Sequencer](#bypassing-the-sequencer), describes an alternative method where users can include their transactions on the Arbitrum chain without relying on the sequencer. By sending transactions directly to the delayed inbox contract on the parent chain (Layer 1), users gain additional flexibility, ensuring that their transactions can be processed even if the sequencer is unavailable or if they prefer not to use it. +The second subsection, [Bypassing the Sequencer](#bypassing-the-sequencer), describes an alternative method where users can include their transactions on the Arbitrum chain without relying on the sequencer. By sending transactions directly to the delayed inbox contract on the parent chain (Layer 1), users gain additional flexibility, ensuring that their transactions can be processed even if the sequencer is unavailable or if they prefer not to use it. This diagram illustrates the various pathways for submitting transactions to the Arbitrum chain. It highlights the options for sending transactions through the sequencer or bypassing it and using the delayed inbox contract on the parent chain. @@ -21,7 +21,7 @@ This section outlines the different methods for users to submit transactions to ### 1. Public RPC -Arbitrum provides public RPCs for its main chains: Arbitrum One, Arbitrum Nova, and Arbitrum Sepolia. Due to their rate-limited nature, these RPC endpoints are suitable for less resource-intensive operations. Public RPCs can be an accessible option for general use cases and light interactions with the network. +Arbitrum provides public RPCs for its main chains: Arbitrum One, Arbitrum Nova, and Arbitrum Sepolia. Due to their rate-limited nature, these RPC endpoints are suitable for less resource-intensive operations. Public RPCs can be an accessible option for general use cases and light interactions with the network. For more details on the specific RPC endpoints for each chain, please see [this section](https://docs.arbitrum.io/build-decentralized-apps/reference/node-providers#arbitrum-public-rpc-endpoints) of the documentation. @@ -33,7 +33,7 @@ You can find a list of supported third-party providers [here](https://docs.arbit ### 3. Arbitrum Nodes -Another approach for sending transactions to the sequencer is through self-hosted Arbitrum nodes. Running a node gives you direct control over your transactions, which go to the sequencer via the sequencer feed. +Another approach for sending transactions to the sequencer is through self-hosted Arbitrum nodes. Running a node gives you direct control over your transactions, which go to the sequencer via the Sequencer Feed. Please see the [Arbitrum Node](https://docs.arbitrum.io/run-arbitrum-node/overview) documentation to learn more about setting up and running a node. diff --git a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx index d652e5e7c..c2edddb78 100644 --- a/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx +++ b/arbitrum-docs/how-arbitrum-works/03-sequencer.mdx @@ -7,7 +7,7 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -The Sequencer is a pivotal component of the Arbitrum network and is responsible for efficiently ordering and processing transactions. It plays a crucial role in providing users with fast transaction confirmations while maintaining the security and integrity of the blockchain. In Arbitrum, the Sequencer orders incoming transactions and manages the batching, compression, and posting of transaction data to parent chain, optimizing costs and performance. +The Sequencer is a pivotal component of the Arbitrum network and is responsible for efficiently ordering and processing transactions. It plays a crucial role in providing users with fast Transaction confirmations while maintaining the security and integrity of the Blockchain. In Arbitrum, the Sequencer orders incoming transactions and manages the batching, compression, and posting of transaction data to Parent chain, optimizing costs and performance. ![sequencer-operations](../assets/sequencer-operations.png) @@ -24,14 +24,14 @@ By examining these aspects, you will understand the Sequencer's role within the ## Sequencing and Broadcasting -The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro architecture. It enables real-time dissemination of transaction data as they are accepted and ordered by the Sequencer. It allows users and nodes to receive immediate updates on transaction sequencing, facilitating rapid transaction confirmations and enhancing the network's overall responsiveness. +The **Sequencer Feed** is a critical component of the Arbitrum network's Nitro architecture. It enables real-time dissemination of transaction data as they are accepted and ordered by the Sequencer. It allows users and nodes to receive immediate updates on transaction sequencing, facilitating rapid transaction confirmations and enhancing the network's overall responsiveness. ### How the Sequencer Publishes the Sequence The Sequencer communicates the transaction sequence through two primary channels: 1. **Real-Time Sequencer Feed**: A live broadcast that publishes transactions instantly as they are sequenced. Nodes and clients subscribed to this feed receive immediate notifications, allowing them to process transactions without delay. -2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process.) +2. **Batches Posted on the Parent Chain**: At regular intervals, the Sequencer aggregates transactions and posts them to the parent chain for finality. (Refer to the Batch-Posting section for detailed information on this process.) ![sequencer-feed](../assets/sequencer-feed.png) @@ -47,7 +47,7 @@ This mechanism is particularly valuable for applications requiring low latency a ### Soft Finality and Trust Model -"Soft finality" refers to the preliminary confirmation of transactions based on the Sequencer's real-time feed. Key aspects include: +"Soft finality" refers to the preliminary Confirmation of transactions based on the Sequencer's real-time feed. Key aspects include: - **Dependence on Sequencer Integrity**: The feed's accuracy and reliability depend on the Sequencer operating honestly and without significant downtime. - **Immediate User Feedback**: Users can act on transaction confirmations swiftly, improving the user experience. @@ -75,9 +75,9 @@ Developers and users should design their applications and interactions with thes ### **Delayed Messages on the Sequencer Feed** -As illustrated in the diagram, the Sequencer feed not only sends child chain transactions posted directly to the Sequencer but also incorporates parent chain-submitted child chain transactions. These include L2 messages submitted on L1 and retryable transactions. The Sequencer agent monitors the finalized messages submitted to the parent chain’s Delayed Inbox Contract. Once finalized, it processes them as incoming messages to the feed, ensuring they are added as ordered transactions. +As illustrated in the diagram, the Sequencer feed not only sends Child chain transactions posted directly to the Sequencer but also incorporates parent chain-submitted child chain transactions. These include L2 messages submitted on L1 and retryable transactions. The Sequencer agent monitors the finalized messages submitted to the parent chain’s Delayed Inbox Contract. Once finalized, it processes them as incoming messages to the feed, ensuring they are added as ordered transactions. -It is important to note that the Nitro node can be configured to add delayed inbox transactions immediately after their submission to the parent chain, even before finalization. However, this approach introduces a risk of child chain reorganization if the transaction fails to finalize on the parent chain. To mitigate this risk, on Arbitrum One and Nova, the Sequencer only includes these transactions in the feed once they are finalized on the Ethereum chain. +It is important to note that the Nitro node can be configured to add delayed inbox transactions immediately after their submission to the parent chain, even before finalization. However, this approach introduces a risk of child chain reorganization if the transaction fails to finalize on the parent chain. To mitigate this risk, on Arbitrum One and Nova, the Sequencer only includes these transactions in the feed once they are finalized on the Ethereum chain. You can also explore how the feed sends incoming messages via WebSocket and learn how to extract message data from the feed on this page: [Read Sequencer Feed](https://docs.arbitrum.io/run-arbitrum-node/sequencer/read-sequencer-feed). diff --git a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx index b2a221528..f8e8e539b 100644 --- a/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx +++ b/arbitrum-docs/how-arbitrum-works/04-geth-at-the-core.mdx @@ -7,19 +7,19 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. +The second key design idea in Nitro is "Geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. ![geth-sandwich](../assets/geth-sandwich.png) The software that makes up a Nitro node can be thought of as built in three main layers, which are shown above: - The base layer is the core of geth--the parts of Geth that emulate the execution of EVM contracts and maintain the data structures that make up the Ethereum state. Nitro compiles in this code as a library, with a few minor modifications to add necessary hooks. -- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. -- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible blockchain node. +- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain Bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. +- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible Blockchain node. Because the top and bottom layers rely heavily on code from geth, this structure has been dubbed a "geth sandwich." Strictly speaking, Geth plays the role of the bread in the sandwich, and ArbOS is the filling, but this sandwich is named for the bread. -The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. +The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a Transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. The rest of this section will be a deep dive into Geth and ArbOS. If deep technical knowledge does not suit you, skip to the next section [Separating Execution from Proving](/how-arbitrum-works/05-separating-execution-from-proving.mdx). @@ -40,7 +40,10 @@ Please note any links on this page may be referencing old releases of Nitro or o ### Hooks -Arbitrum uses various hooks to modify Geth's behavior when processing transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] function. +Arbitrum uses various hooks to modify Geth's behavior when processing +transactions. Each provides an opportunity for ArbOS to update its state and make decisions about the +transaction during its lifetime. Transactions are applied using Geth's [`ApplyTransaction`][applytransaction_link] +function. Below is [`ApplyTransaction`][applytransaction_link]'s callgraph, with additional info on where the various Arbitrum-specific hooks are inserted. Click on any to go to their section. By default, these hooks do nothing so as to leave Geth's default behavior unchanged, but for chains configured with [`EnableArbOS`](#enablearbos) set to true, [`ReadyEVMForL2`](#ReadyEVMForL2) installs the alternative L2 hooks. @@ -128,7 +131,7 @@ The [`EndTxHook`][endtxhook_link] is called after the [`EVM`][evm_link] has retu #### [`APIBackend`][apibackend_link] -APIBackend implements the [`ethapi.Backend`][ethapi.backend_link] interface, which allows simple integration of the Arbitrum chain to existing Geth API. Most calls are answered using the Backend member. +APIBackend implements the [`ethapi.Backend`][ethapi.backend_link] interface, which allows simple integration of the Arbitrum chain to existing Geth API. Most calls are answered using the Backend member. [apibackend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/arbitrum/apibackend.go#L34 [ethapi.backend_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/internal/ethapi/backend.go#L42 @@ -163,14 +166,14 @@ The process is simplified using two functions: [`PrepareRecording`][preparerecor Nitro Geth includes a few L2-specific transaction types. Click on any to jump to their section. -| Tx Type | Represents | Last Hook Reached   | Source | -| :------------------------------------------------ | :----------------------------------- | :------------------------- | ------ | -| [`ArbitrumUnsignedTx`][arbtxunsigned] | An L1 to L2 message | [`EndTxHook`][he] | Bridge | -| [`ArbitrumContractTx`][arbtxcontract] | A nonce-less L1 to L2 message   | [`EndTxHook`][he] | Bridge | -| [`ArbitrumDepositTx`][arbtxdeposit] | A user deposit | [`StartTxHook`][hs] | Bridge | -| [`ArbitrumSubmitRetryableTx`][arbtxsubmit]   | Creating a retryable | [`StartTxHook`][hs]   | Bridge | -| [`ArbitrumRetryTx`][arbtxretry] | A retryable redeem attempt | [`EndTxHook`][he] | L2 | -| [`ArbitrumInternalTx`][arbtxinternal] | ArbOS state update | [`StartTxHook`][hs] | ArbOS | +| Tx Type | Represents | Last Hook Reached   | Source | +| :------------------------------------------------ | :----------------------------------------------------------------------- | :------------------------- | ------ | +| [`ArbitrumUnsignedTx`][arbtxunsigned] | An L1 to L2 message | [`EndTxHook`][he] | Bridge | +| [`ArbitrumContractTx`][arbtxcontract] | A nonce-less L1 to L2 message   | [`EndTxHook`][he] | Bridge | +| [`ArbitrumDepositTx`][arbtxdeposit] | A user deposit | [`StartTxHook`][hs] | Bridge | +| [`ArbitrumSubmitRetryableTx`][arbtxsubmit]   | Creating a retryable | [`StartTxHook`][hs]   | Bridge | +| [`ArbitrumRetryTx`][arbtxretry] | A Retryable Redeem attempt | [`EndTxHook`][he] | L2 | +| [`ArbitrumInternalTx`][arbtxinternal] | ArbOS state update | [`StartTxHook`][hs] | ArbOS | [arbtxunsigned]: #ArbitrumUnsignedTx [arbtxcontract]: #ArbitrumContractTx @@ -209,7 +212,7 @@ Because tracing support requires ArbOS's state-changes happen inside a transacti ##### [`InternalTxStartBlock`][arbinternaltxstartblock_link] -Updates the L1 block number and L1 base fee. This transaction [is generated][block_generated_link] whenever a new block is created. They are [guaranteed to be the first][block_first_link] in their L2 block. +Updates the L1 block number and L1 base fee. This transaction [is generated][block_generated_link] whenever a new block is created. They are [guaranteed to be the first][block_first_link] in their L2 Block. [arbitrumunsignedtx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L43 [arbitrumcontracttx_link]: https://github.com/OffchainLabs/go-ethereum/blob/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/types/arb_types.go#L104 @@ -253,7 +256,7 @@ Introduces [ArbOS](/how-arbitrum-works/04-geth-at-the-core.mdx#arbos), convertin #### `AllowDebugPrecompiles` -Allows access to debug precompiles. Not enabled for Arbitrum One. When false, calls to debug precompiles will always revert. +Allows access to debug precompiles. Not enabled for Arbitrum One. When false, calls to debug precompiles will always revert. #### `DataAvailabilityCommittee` @@ -273,7 +276,7 @@ The total amount of L2 ether in the system should not change except in controlle #### MixDigest and ExtraData -To aid with [outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners/stakers, to store the send count. +To aid with [Outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners/stakers, to store the send count. #### Retryable Support @@ -290,7 +293,7 @@ We created the AdvancedPrecompile interface, which executes and charges gas with #### WASM build support -The WASM Arbitrum executable does not support file operations. We created [`fileutil.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. +The WASM Arbitrum executable does not support file operations. We created [`fileutil.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](https://github.com/OffchainLabs/go-ethereum/tree/7503143fd13f73e46a966ea2c42a058af96f7fcf/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. #### Types @@ -314,7 +317,7 @@ Genesis block in nitro is not necessarily block #0. Nitro supports importing blo ## ArbOS -ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for smart contract execution. +ArbOS is the Layer 2 EVM hypervisor that facilitates the execution environment of L2 Arbitrum. ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function, it accounts for and manages network resources, produces blocks from incoming messages, cross-chain messaging, and operates its instrumented instance of Geth for Smart Contract execution. In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. @@ -345,7 +348,7 @@ Each time a transaction calls a method of an L2-specific precompile, a [`call co ### Messages -An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. +An [`L1IncomingMessage`][l1incomingmessage_link] represents an incoming sequencer message. A message includes one or more user transactions depending on load, and is made into a [unique L2 block][produceblockadvanced_link]. The L2 block may include additional system transactions added in while processing the message's user transactions, but ultimately the relationship is still bijective: for every [`L1IncomingMessage`][l1incomingmessage_link] there is an L2 block with a unique L2 block hash, and for every L2 block after chain initialization there was an [`L1IncomingMessage`][l1incomingmessage_link] that made it. A sequencer Batch may contain more than one [`L1IncomingMessage`][l1incomingmessage_link]. [l1incomingmessage_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/incomingmessage.go#L54 [produceblockadvanced_link]: https://github.com/OffchainLabs/nitro/blob/4ac7e9268e9885a025e0060c9ec30f9612f9e651/arbos/block_processor.go#L118 @@ -397,7 +400,7 @@ Based on this information, ArbOS maintains an L1 data fee, also tracked as part #### [`l2PricingState`][l2pricingstate_link] -The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational speed limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. +The L2 pricing state tracks L2 resource usage to determine a reasonable L2 gas price. This process considers a variety of factors, including user demand, the state of Geth, and the computational Speed Limit. The primary mechanism for doing so consists of a pair of pools, one larger than the other, that drain as L2-specific resources are consumed and filled as time passes. L1-specific resources like L1 `calldata` are not tracked by the pools, as they have little bearing on the actual work done by the network actors that the speed limit is meant to keep stable and synced. While much of this state is accessible through the [`ArbGasInfo`](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo) and [`ArbOwner`](/build-decentralized-apps/precompiles/02-reference.mdx#arbowner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](#hooks). Each of an incoming message's transactions removes from the pool the L2 component of the gas it uses, and afterward the message's timestamp [informs the pricing mechanism][notify_pricer_link] of the time that's passed as ArbOS [finalizes the block][finalizeblock_link]. diff --git a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx index 6d00d1980..d77c2726f 100644 --- a/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx +++ b/arbitrum-docs/how-arbitrum-works/05-separating-execution-from-proving.mdx @@ -11,7 +11,7 @@ One of the challenges in designing a practical Rollup system is the tension betw When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form and as a Docker image containing a compiled binary.) -Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. +Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (WASM), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. ## WAVM @@ -21,7 +21,7 @@ WAVM differs from wasm in three main ways. First, WAVM removes some wasm feature Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed-cost instructions. These transformations simplify proving. -Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to execute the State Transition Function. +Third, WAVM adds a few opcodes to enable interaction with the Blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to execute the State Transition Function. ## ReadPreImage and the Hash Oracle Trick @@ -31,6 +31,6 @@ The most interesting new instruction is `ReadPreImage` which takes as input a ha As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. -The only other use of `ReadPreImage` is to fetch the contents of recent L2 block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. +The only other use of `ReadPreImage` is to fetch the contents of recent L2 Block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. +This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. diff --git a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx index a2f3a9066..b240fb7b3 100644 --- a/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx +++ b/arbitrum-docs/how-arbitrum-works/06-optimistic-rollup.mdx @@ -7,17 +7,17 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -Arbitrum is an optimistic rollup. Let’s unpack that term. +Arbitrum is an optimistic rollup. Let’s unpack that term. _Rollup_ Arbitrum is a Rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. -This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. +This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a Validator. Nothing about the history or state of the chain is a secret. _Optimistic_ -Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to challenge that claim. If the challenge period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). +Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to Challenge that claim. If the Challenge Period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. @@ -31,13 +31,13 @@ There are basically two choices: interactive proving, or re-executing transactio The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. -Arbitrum's approach is based on dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. +Arbitrum's approach is based on Dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. ### Re-executing transactions -The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. +The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual Transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. ### Why interactive proving is better @@ -47,7 +47,7 @@ We believe strongly that interactive proving is the superior approach, for the f **More efficient in the pessimistic case**: In case of a dispute, interactive proving requires the L1 referee contract only to check that Alice and Bob's actions "have the right shape", for example, that Alice has divided her N-step claim into two claims half as large. (The referee doesn't need to evaluate the correctness of Alice's claims--Bob does that, off-chain.) Only one instruction needs to be reexecuted. By contrast, reexecution requires the L1 referee to emulate the execution of an entire transaction. -**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. +**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive Fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. **More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. @@ -65,23 +65,23 @@ You’re welcome to study, observe, and even participate in the Rollup protocol, The second thing to understand about the Rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable, and Arbitrum nodes will report that your transaction has been completed. The role of the Rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the Rollup protocol.) -You might wonder why we need the Rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The Rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed, Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. However, once the result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum, such as processing withdrawals of funds from Nitro back to L1. +You might wonder why we need the Rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The Rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, Trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed, Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. However, once the result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum, such as processing withdrawals of funds from Nitro back to L1. With those preliminaries behind us, let’s jump into the details of the Rollup protocol. -The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). +The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). -The key security property of the Rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that the execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire), can force your transactions to be processed correctly. And that is true, no matter how many malicious people are trying to stop you. +The key security property of the Rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that the execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire), can force your transactions to be processed correctly. And that is true, no matter how many malicious people are trying to stop you. ### The Rollup Chain -The Rollup protocol tracks a chain of Rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum rollup protocol manages and oversees. +The Rollup protocol tracks a chain of Rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum Rollup Protocol manages and oversees. -Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually, every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. +Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually, every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. :::note -Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and chain state, whereas proposers can also assert and challenge the chain state. +Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and Chain state, whereas proposers can also assert and challenge the chain state. ::: @@ -237,7 +237,7 @@ A consequence of these rules is that once the first unresolved RBlock's deadline ## Delays -Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid RBlock (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. +Even if the Assertion Tree has multiple conflicting RBlocks and, say, multiple disputes are in progress, validators can continue making assertions; honest validators will simply build on the one valid RBlock (intuitively: an assertion is also an implicit claim of the validity of all of its parent-assertions.) Likewise, users can continue transacting on L2, since transactions continue to be posted in the chain's inbox. The only delay that users experience during a dispute is of their [L2 to L1 messages](/how-arbitrum-works/11-l2-to-l1-messaging.mdx) (i.e., "their withdrawals"). Note that a "delay attacker" who seeks to grief the system by deliberately causing such delays will find this attack quite costly, since each bit of delay-time gained requires the attacker lose another stake. @@ -249,11 +249,12 @@ Some Arbitrum nodes will choose to act as _validators_. This means that they wat Not all nodes will choose to do this. Because the Rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. -Offchain Labs provides open source validator software, including a pre-built Docker image. +Offchain Labs provides open source validator software, including +a pre-built Docker image. Every validator can choose their own approach, but we expect validators to follow three common strategies: -- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An active validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. +- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An Active Validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. - The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. - The _watchtower validator_ strategy never bonds. It simply watches the Rollup protocol, and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond and that that can happen before the dishonest RBlock’s deadline expires. (In practice, this will allow several days for a response.) @@ -264,5 +265,5 @@ The underlying protocol supports permissionless validation, i.e.,--anyone can do Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. - Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. -- Parties who have significant assets at bond on a chain, such as dapp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. +- Parties who have significant assets at bond on a chain, such as dApp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. - Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. diff --git a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx index 309a6bed4..7c1b76980 100644 --- a/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx +++ b/arbitrum-docs/how-arbitrum-works/07-interactive-fraud-proofs.mdx @@ -13,13 +13,13 @@ Suppose the rollup chain looks like this: RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. -At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) +At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) -Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. +Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a Challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. The challenge is a game in which Alice and Bob alternate moves, with an Ethereum contract as the referee. Alice, the defender, moves first. -The game will operate in two phases: dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. +The game will operate in two phases: Dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. @@ -29,13 +29,13 @@ Alice is defending the claim that starting with the state in the predecessor RBl Alice’s first move requires her to dissect her claims about intermediate states between the beginning (0 instructions executed) and the end (N instructions executed). So we require Alice to divide her claim in half, and post the state at the half-way point, after N/2 instructions have been executed. -Now Alice has effectively bisected her N-step assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. +Now Alice has effectively bisected her N-step Assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. ## Why Dissection Correctly Identifies a Cheater -Before talking about the complexities of the real challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. +Before talking about the complexities of the real Challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. To prove (1), observe that if Alice’s initial claim is correct, she can offer a truthful midpoint claim, and both of the implied half-size claims will be correct. So whichever half Bob objects to, Alice will again be in the position of defending a correct claim. At each stage of the protocol, Alice will be defending a correct claim. At the end, Alice will have a correct one-step claim to prove, so that claim will be provable and Alice can win the challenge. @@ -47,7 +47,7 @@ To prove (2), observe that if Alice’s initial claim is incorrect, this can onl The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. -**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). +**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). **K-way dissection:** Rather than dividing a claim into two segments of size N/2, we divide it into K segments of size N/K. This requires posting K-1 intermediate claims, at points evenly spaced through the claimed execution. This reduces the number of rounds by a factor of log(K)/log(2). @@ -132,7 +132,7 @@ there is time to diagnose and fix the error with a contract upgrade. ## One Step Proof Assumptions -The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise +The One Step Proof (OSP) implementation makes certain assumptions about the cases that can arise in a correct execution. This documents those assumptions about what's being executed. If a case is "unreachable", that is, the case is assumed to never arise in correct execution, @@ -159,7 +159,7 @@ The following assumptions, together, must prevent an unreachable case from arisi ### The WAVM code is generated by Arbitrator from valid WASM -WAVM is the name of the custom instruction set similar to WASM used for proving. +WAVM is the name of the custom instruction set similar to WASM used for proving. Arbitrator transpiles WASM code into WAVM. It also invokes wasm-validate from [wabt](https://github.com/WebAssembly/wabt) (the WebAssembly Binary Toolkit) to ensure the input WASM is valid. @@ -191,7 +191,7 @@ and not too large: #### Block headers -Nitro may request up to the last 256 L2 block headers. +Nitro may request up to the last 256 L2 Block headers. The last block header is required to determine the current state, and blocks before it are required to implement the `BLOCKHASH` evm instruction. @@ -332,16 +332,16 @@ Those calls are documented in `wavm-modules.mdx`. For these instruction descriptions, all pointers and offsets are represented as WASM i32s. -| Opcode | Name | Description | -| ------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0x800A | CallerModuleInternalCall | Pushes the current program counter, module number, and module's internals offset (all i32s) to the stack. Then, it retrieves the caller module internals offset from the current stack frame. If 0, errors, otherwise, jumps to the caller module at function (internals offset + opcode argument data) and instruction 0. | -| 0x8010 | GetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, writes the global state bytes32 value of the specified index to the specified pointer in memory. | -| 0x8011 | SetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, reads a bytes32 from the specified pointer in memory and sets the global state bytes32 value of the specified index to it. | -| 0x8012 | GetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, writes the global state u32 value of the specified index to the specified pointer in memory. | -| 0x8013 | SetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, reads a u64 from the specified pointer in memory and sets the global state u64 value of the specified index to it. | -| 0x8020 | ReadPreImage | Pops an offset and then a pointer from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Reads a 32 byte Keccak-256 hash from the specified pointer in memory. Writes up to 32 bytes of the preimage to that hash, beginning with the `offset` byte of the preimage. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | -| 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the sequencer inbox, 1 for the delayed inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | -| 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | +| Opcode | Name | Description | +| ------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0x800A | CallerModuleInternalCall | Pushes the current program counter, module number, and module's internals offset (all i32s) to the stack. Then, it retrieves the caller module internals offset from the current stack frame. If 0, errors, otherwise, jumps to the caller module at function (internals offset + opcode argument data) and instruction 0. | +| 0x8010 | GetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, writes the global state bytes32 value of the specified index to the specified pointer in memory. | +| 0x8011 | SetGlobalStateBytes32 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state bytes32s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Otherwise, reads a bytes32 from the specified pointer in memory and sets the global state bytes32 value of the specified index to it. | +| 0x8012 | GetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, writes the global state u32 value of the specified index to the specified pointer in memory. | +| 0x8013 | SetGlobalStateU64 | Pops a pointer and then an index from the stack. If the index is greater than or equal to the number of global state u64s, errors. If the pointer mod 32 is not zero, errors. If the pointer + 8 is outside the programs memory, errors. Otherwise, reads a u64 from the specified pointer in memory and sets the global state u64 value of the specified index to it. | +| 0x8020 | ReadPreImage | Pops an offset and then a pointer from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Reads a 32 byte Keccak-256 hash from the specified pointer in memory. Writes up to 32 bytes of the preimage to that hash, beginning with the `offset` byte of the preimage. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | +| 0x8021 | ReadInboxMessage | Pops an offset, then a pointer, and then an i64 message number from the stack. If the pointer mod 32 is not zero, errors. If the pointer + 32 is outside the programs memory, errors. Attempts to read an inbox message from the inbox identifier contained in the argument data (0 for the Sequencer inbox, 1 for the Delayed Inbox) at the specified message number. If this exceeds the machine's inbox limit, enters the "too far" state. Otherwise, writes up to 32 bytes of the specified inbox message, beginning with the `offset` byte of the message. If `offset` is greater than or equal to the number of bytes in the preimage, writes nothing. Pushes the number of bytes written to the stack as an i32. | +| 0x8022 | HaltAndSetFinished | Sets the machine status to finished, halting execution and marking it as a success. | ## WAVM Floating point implementation diff --git a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx index 8a77e119a..781c4bc02 100644 --- a/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx +++ b/arbitrum-docs/how-arbitrum-works/08-anytrust-protocol.mdx @@ -7,15 +7,15 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. +AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. -The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. +The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to stake on correct results), have access to the data of every L2 Transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. ## Keysets -A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. +A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. A Keyset contains @@ -42,7 +42,7 @@ A central concept in AnyTrust is the Data Availability Certificate (hereafter, a Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. -In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. +In ordinary (non-AnyTrust) Nitro, the Arbitrum Sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. @@ -67,7 +67,7 @@ The DAS software, based on configuration options, can store its data in local fi ## Sequencer-Committee Interaction -When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. +When the Arbitrum sequencer produces a data Batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. diff --git a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx index d822bdbc7..a8a0137dc 100644 --- a/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx +++ b/arbitrum-docs/how-arbitrum-works/09-gas-fees.mdx @@ -7,16 +7,16 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -Gas is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. +Gas is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. There are two parties a user pays when submitting a tx: - the poster, if reimbursable, for L1 resources such as the L1 calldata needed to post the tx - the network fee account for L2 resources, which include the computation, storage, and other burdens L2 nodes must bear to service the tx -The L1 component is the product of the transaction's estimated contribution to its batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. +The L1 component is the product of the Transaction's estimated contribution to its Batch's size — computed using Brotli on the transaction by itself — and the L2's view of the L1 data price, a value which dynamically adjusts over time to ensure the batch-poster is ultimately fairly compensated. -The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the state transition function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. +The L2 component consists of the traditional fees Geth would pay to stakers in a vanilla L1 chain, such as the computation and storage charges applying the State Transition Function entails. ArbOS charges additional fees for executing its L2-specific [precompiles](/build-decentralized-apps/precompiles/01-overview.mdx), whose fees are dynamically priced according to the specific resources used while executing the call. The following sections will detail how to calculate L1 and L2 fees. If you do not need precise calculations or a technical understanding, skip to the next section, [L1 to L2 messaging](/how-arbitrum-works/10-l1-to-l2-messaging.mdx). @@ -28,11 +28,11 @@ ArbOS dynamically prices L1 gas, with the price adjusting to ensure that the amo There are two types of L1 costs: batch posting costs, and rewards. -Batch posting costs reflect the actual cost a batch poster pays to post batch data on L1. Whenever a batch is posted, the L1 contract that records the batch will send a special "batch posting report" message to L2 ArbOS, reporting who paid for the batch and what the L1 basefee was at the time. This message is placed in the chain's delayed inbox, so it will be delivered to L2 ArbOS after some delay. +Batch posting costs reflect the actual cost a batch poster pays to post batch data on L1. Whenever a batch is posted, the L1 contract that records the batch will send a special "batch posting report" message to L2 ArbOS, reporting who paid for the batch and what the L1 basefee was at the time. This message is placed in the chain's Delayed Inbox, so it will be delivered to L2 ArbOS after some delay. When a batch posting report message arrives at L2, ArbOS computes the cost of the referenced batch by multiplying the reported basefee by the batch's data cost. (ArbOS retrieves the batch's data from its inbox state, and computes the L1 gas that the batch would have used by counting the number of zero bytes and non-zero bytes in the batch.) The resulting cost is recorded by the pricer as funds due to the party who is reported to have submitted the batch. -The second type of L1 cost is an optional (per chain) per-unit reward for handling transaction calldata. In general the reward might be paid to the sequencer, or to members of the Data Availability Committee in an AnyTrust chain, or to anyone else who incurs per-calldata-byte costs on behalf of the chain. The reward is a fixed number of wei per data unit, and is paid to a single address. +The second type of L1 cost is an optional (per chain) per-unit reward for handling transaction calldata. In general the reward might be paid to the Sequencer, or to members of the Data Availability Committee in an AnyTrust chain, or to anyone else who incurs per-calldata-byte costs on behalf of the chain. The reward is a fixed number of wei per data unit, and is paid to a single address. The L1 pricer keeps track of the funds due to the reward address, based on the number of data units handled so far. This amount is updated whenever a batch posting report arrives at L2. @@ -63,7 +63,7 @@ The L1 pricer also records the total number of "data units" (the sum of the esti ## L2 gas pricing -The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). +The L2 gas price on a given Arbitrum chain has a set floor, which can be queried via [ArbGasInfo](/build-decentralized-apps/precompiles/02-reference.mdx#arbgasinfo)'s `getMinimumGasPrice` method (currently @arbOneGasFloorGwei@ gwei on Arbitrum One and @novaGasFloorGwei@ gwei on Nova). ### Estimating L2 Gas @@ -75,7 +75,7 @@ L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amou The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. -The algorithm compares gas usage against a parameter called the [speed limit](#the-speed-limit) which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. +The algorithm compares gas usage against a parameter called the [speed limit](#the-speed-limit) which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the Speed Limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. @@ -97,7 +97,7 @@ Gas estimation for Retryable submissions is possible via the [NodeInterface](/bu ### The Speed Limit -The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. +The security of Nitro chains depends on the assumption that when one Validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a Challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. This sets an effective speed limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. diff --git a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx index b440d9bce..d58d71c75 100644 --- a/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/10-l1-to-l2-messaging.mdx @@ -17,41 +17,41 @@ import { NodeDescription, } from '/src/components/MermaidWithHtml/MermaidWithHtml'; -In the [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. +In the [Transaction Lifecycle](/how-arbitrum-works/02-transaction-lifecycle.mdx) section, we covered how users interact with L2 contracts. They submit transactions by putting messages into the chain’s inbox or having a full node Sequencer or aggregator do so on their behalf. -L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). +L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same Transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. Consequently, a cross-chain contract-to-contract call can never produce a result available to the calling contract (except for acknowledgment of a successful call submitted for later execution). In this section, we will discuss how contracts interact between L1 and L2, how an L1 contract is called an L2 contract, and vice versa. ## Retryable Tickets -Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. +Retryable tickets are Arbitrum's canonical method for creating L1 to L2 messages, i.e., L1 transactions that initiate a message to be executed on L2. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1; its _submission_ on L1 is separable / asynchronous with its _execution_ on L2. Retryables provide atomicity between the cross chain operations; if the L1 transaction to request submission succeeds (i.e. does not revert) then the execution of the Retryable on L2 has a strong guarantee to ultimately succeed as well. ## Retryable Tickets Lifecycle -Here we walk through the different stages of the lifecycle of a retryable ticket; (1) submission, (2) auto-redemption, and (3) manual redemption. +Here we walk through the different stages of the lifecycle of a Retryable Ticket; (1) submission, (2) auto-redemption, and (3) manual redemption. ### Submission 1. Creating a retryable ticket is initiated with a call (direct or internal) to the `createRetryableTicket` function of the [`inbox` contract][inbox_link]. A ticket is guaranteed to be created if this call succeeds. Here, we describe parameters that need to be carefully set. Note that, this function forces the sender to provide a _reasonable_ amount of funds (at least enough to submitting, and _attempting_ to executing the ticket), but that doesn't guarantee a successful auto-redemption. -| Parameter | Description | -| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `l1CallValue (also referred to as deposit)` | Not a real function parameter, it is rather the callValue that is sent along with the transaction | -| `address to` | The destination L2 address | -| `uint256 l2CallValue` | The callvalue for retryable L2 message that is supplied within the deposit (l1CallValue) | -| `uint256 maxSubmissionCost` | The maximum amount of ETH to be paid for submitting the ticket. This amount is (1) supplied within the deposit (l1CallValue) to be later deducted from sender's L2 balance and is (2) directly proportional to the size of the retryable’s data and L1 basefee | -| `address excessFeeRefundAddress` | The unused gas cost and submssion cost will deposit to this address, formula is: `(gasLimit x maxFeePerGas - execution cost) + (maxSubmission - (autoredeem ? 0 : submission cost))`. (Note: excess deposit will transfer to the alias address of the parent chain tx's `msg.sender` rather than this address) | -| `address callValueRefundAddress` | The L2 address to which the l2CallValue is credited if the ticket times out or gets cancelled (this is also called the `beneficiary`, who's got a critical permission to cancel the ticket) | -| `uint256 gasLimit` | Maximum amount of gas used to cover L2 execution of the ticket | -| `uint256 maxFeePerGas` | The gas price bid for L2 execution of the ticket that is supplied within the deposit (l1CallValue) | -| `bytes calldata data` | The calldata to the destination L2 address | +| Parameter | Description | +| :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `l1CallValue (also referred to as deposit)` | Not a real function parameter, it is rather the callValue that is sent along with the transaction | +| `address to` | The destination L2 address | +| `uint256 l2CallValue` | The callvalue for retryable L2 message that is supplied within the deposit (l1CallValue) | +| `uint256 maxSubmissionCost` | The maximum amount of ETH to be paid for submitting the ticket. This amount is (1) supplied within the deposit (l1CallValue) to be later deducted from sender's L2 balance and is (2) directly proportional to the size of the retryable’s data and L1 basefee | +| `address excessFeeRefundAddress` | The unused gas cost and submssion cost will deposit to this address, formula is: `(gasLimit x maxFeePerGas - execution cost) + (maxSubmission - (autoredeem ? 0 : submission cost))`. (Note: excess deposit will transfer to the alias address of the Parent chain tx's `msg.sender` rather than this address) | +| `address callValueRefundAddress` | The L2 address to which the l2CallValue is credited if the ticket times out or gets cancelled (this is also called the `beneficiary`, who's got a critical permission to cancel the ticket) | +| `uint256 gasLimit` | Maximum amount of gas used to cover L2 execution of the ticket | +| `uint256 maxFeePerGas` | The gas price bid for L2 execution of the ticket that is supplied within the deposit (l1CallValue) | +| `bytes calldata data` | The calldata to the destination L2 address | 2. Sender's deposit must be enough to make the L1 submission succeed and for the L2 execution to be _attempted_. If provided correctly, a new ticket with a unique `TicketID` is created and added to retryable buffer. Also, funds (`submissionCost` + `l2CallValue`) are deducted from the sender and placed into the escrow for later use in redeeming the ticket. 3. Ticket creation causes the [`ArbRetryableTx`](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx) precompile to emit a `TicketCreated` event containing the `TicketID` on L2. -[inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/bridge/Inbox.sol +[inbox_link]: https://github.com/OffchainLabs/nitro-contracts/blob/67127e2c2fd0943d9d87a05915d77b1f220906aa/src/Bridge/Inbox.sol @@ -133,7 +133,7 @@ Here we walk through the different stages of the lifecycle of a retryable ticket ### Manual Redemption -5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. +5. At this point, _anyone_ can attempt to manually redeem the ticket again by calling [ArbRetryableTx](/build-decentralized-apps/precompiles/02-reference.mdx#arbretryabletx)'s `redeem` precompile method, which donates the call's gas to the next attempt. Note that the amount of gas is NOT limited by the original gasLimit set during the ticket creation. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to redeem, so there's no chance the block's gas limit is reached before execution. 6. If the fixed period (one week) elapses without a successful redeem, the ticket **expires** and will be [automatically **discarded**][discard_link], unless some party has paid a fee to [**keep the ticket alive**][renew_link] for another full period. A ticket can live indefinitely as long as it is renewed each time before it expires. @@ -207,7 +207,7 @@ In the lifecycle of a retryable ticket, two types of L2 transaction receipts wil ### Alternative "unsafe" Retryable Ticket Creation -The `Inbox.createRetryableTicket` convenience method includes sanity checks to help minimize the risk of user error: the method will ensure that enough funds are provided directly from L1 to cover the current cost of ticket creation. It also will convert the provided `callValueRefundAddress` and `excessFeeRefundAddress` to their address alias (see below) if either is a contract (determined by if the address has code during the call), providing a path for the L1 contract to recover funds. A power-user may bypass these sanity-check measures via the `Inbox`'s `unsafeCreateRetryableTicket` method; as the method's name desperately attempts to warn you, it should only be accessed by a user who truly knows what they're doing. +The `Inbox.createRetryableTicket` convenience method includes sanity checks to help minimize the risk of user error: the method will ensure that enough funds are provided directly from L1 to cover the current cost of ticket creation. It also will convert the provided `callValueRefundAddress` and `excessFeeRefundAddress` to their Address Alias (see below) if either is a contract (determined by if the address has code during the call), providing a path for the L1 contract to recover funds. A power-user may bypass these sanity-check measures via the `Inbox`'s `unsafeCreateRetryableTicket` method; as the method's name desperately attempts to warn you, it should only be accessed by a user who truly knows what they're doing. ## Eth deposits @@ -219,7 +219,7 @@ In principle, retryable tickets can alternatively be used to deposit Ether; this ## Transacting via the Delayed Inbox -While retryables and Eth deposits _must_ be submitted through the delayed inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/03-sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. +While retryables and Eth deposits _must_ be submitted through the Delayed Inbox, in principle, _any_ message can be included this way; this is a necessary recourse to ensure the Arbitrum chain preserves censorship resistance even if the Sequencer misbehaves (see [The Sequencer and Censorship Resistance](/how-arbitrum-works/03-sequencer.mdx)). However, under ordinary/happy circumstances, the expectation/recommendation is that clients use the delayed inbox only for Retryables and Eth deposits, and transact via the Sequencer for all other messages. ### Address Aliasing @@ -248,6 +248,6 @@ modifier onlyFromMyL1Contract() override { ### Signed Messages -The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's batch. For these messages, the address of the L1 sender is effectively ignored at L2. +The delayed inbox can also accept messages that include a signature. In this case, the message will execute with the `msg.sender` address equal to the address that produced the included signature (i.e., _not_ its alias). Intuitively, the signature proves that the sender address is not a contract, and thus is safe from cross-chain exploit concerns described above. Thus, it can safely execute from signer's address, similar to a transaction included in a Sequencer's Batch. For these messages, the address of the L1 sender is effectively ignored at L2. These signed messages submitted through the delayed inbox can be used to execute messages that bypass the Sequencer and require EOA authorization at L2, e.g., force-including an Ether withdrawal (see ["withdraw eth tutorial"](https://github.com/OffchainLabs/arbitrum-tutorials/blob/a1c3f64a5abdd0f0e728cb94d4ecc2700eab7579/packages/delayedInbox-l2msg/scripts/withdrawFunds.js#L61-L65)). diff --git a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx index cafcd9f9c..91bf84bf6 100644 --- a/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx +++ b/arbitrum-docs/how-arbitrum-works/11-l2-to-l1-messaging.mdx @@ -7,23 +7,27 @@ user_story: As a current or prospective Arbitrum user, I need to learn more abou content_type: get-started --- -Arbitrum's Outbox system allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) (Retryables), "in reverse" though with a few differences. +Arbitrum's Outbox system +allows for arbitrary L2 to L1 contract calls; i.e., messages initiated from L2 which eventually +resolve in execution on L1. L2-to-L1 messages (aka "outgoing" messages) bear many things in common +with Arbitrum's [L1-to-L2 messages](/how-arbitrum-works/10-l1-to-l2-messaging.mdx) (Retryables), "in +reverse" though with a few differences. ### Protocol Flow -Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. +Part of the L2 state of an Arbitrum chain — and consequently, part of what's asserted in every RBlock — is a Merkle root of all L2-to-L1 messages in the chain's history. Upon an asserted RBlock being confirmed (typically ~1 week after its asserted), this Merkle root is posted on L1 in the `Outbox` contract. The Outbox contract then lets users execute their messages — validating Merkle proofs of inclusion, and tracking which L2 to L1 messages have already been spent. ### Client Flow -From a client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. +From a Client perspective, an L2 to L1 message begins with a call to the L2 [`ArbSys`](/build-decentralized-apps/precompiles/02-reference.mdx#arbsys) precompile contract's `sendTxToL1` method. Once the message is included in an Assertion (typically within ~1 hour) and the assertion is confirmed (typically about ~ 1 week), any client can execute the message. To do this, the client first retrieves the proof data via a call to the Arbitrum chain's "virtual"/precompile-esque\*\* `NodeInterface` contract's `constructOutboxProof` method. The data returned can then be used in the `Outbox`'s `executeTransaction` method to perform the L1 execution. ### Protocol Design Details -An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock confirmation processed can't be griefed. +An important feature in the design of the Outbox system is that calls to `confirmNode` have constant overhead. Requiring that `confirmNode` only update the constant-sized outgoing message root hash, and that users themselves carry out the final step of execution, achieves this goal; i.e., no matter the number of outgoing messages in the root, or the gas cost of executing them on L1, the cost of confirming nodes remains constant; this ensures that the RBlock Confirmation processed can't be griefed. Unlike Retryables, which have an option to provide Ether for automatic L2 execution, outgoing messages can't provide in-protocol automatic L1 execution, for the simple reason that Ethereum itself doesn't offer scheduled execution affordances. However, application-layer contracts that interact with the Outbox could in principle be built to provide somewhat-analogous "execution market" functionality for outsourcing the final L1 execution step. Another difference between outgoing messages and Retryables is that Retryables have a limited lifetime before which they must be redeemed (or have their lifetime explicitly extended), whereas L2 to L1 messages are stored in L1 state, and thus persist permanently / have no deadline before which they must be executed. -The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/01-a-gentle-introduction.mdx) +The week long delay period before outgoing messages can be executed is inherent and fundamental to the nature of Arbitrum Rollup, or any Optimistic Rollup style L2; the moment a Transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need-be. For a protocol overview, see [Inside Arbitrum](/how-arbitrum-works/01-a-gentle-introduction.mdx) \*\* We refer to `NodeInterface` as a "virtual" contract; its methods are accessible via calls `0x00000000000000000000000000000000000000C8`, but it doesn't really live on chain. It isn't really a precompile, but behaves a lot like a precompile that can't receive calls from other contracts. This is a cute trick that let's us provide Arbitrum-specific data without having to implement a custom RPC. diff --git a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md b/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md deleted file mode 100644 index 443b570f9..000000000 --- a/arbitrum-docs/how-arbitrum-works/inside-arbitrum-nitro.md +++ /dev/null @@ -1,679 +0,0 @@ ---- -title: 'Inside Arbitrum Nitro' -sidebar_label: 'Deep dive: Inside Arbitrum' -description: 'Learn the fundamentals of Nitro, Arbitrum stack.' -author: dzgoldman -sme: dzgoldman -user_story: As a current or prospective Arbitrum user, I need learn more about Nitros design. -content_type: get-started ---- - -import ImageWithCaption from '@site/src/components/ImageCaptions/'; - -This document is a deep-dive explanation of Arbitrum Nitro’s design and the rationale for it. This isn’t API documentation, nor is it a guided tour of the code--look elsewhere for those. “Inside Arbitrum Nitro” is for people who want to understand Nitro's design. - -The body of this document will describe Arbitrum Rollup, the primary use case of the Nitro technology and the one used on the Arbitrum One chain. There is a variant use case, called AnyTrust, which is used by the Arbitrum Nova chain. AnyTrust is covered by a section at the end of this document. - -## Why use Arbitrum? Why use Nitro? - -Arbitrum is an L2 scaling solution for Ethereum, offering a unique combination of benefits: - -- Trustless security: security rooted in Ethereum, with any one - party able to ensure correct Layer 2 results -- Compatibility with Ethereum: able to run unmodified EVM contracts and unmodified Ethereum transactions -- Scalability: moving contracts’ computation and storage off of the main Ethereum chain, allowing much higher throughput -- Minimum cost: designed and engineered to minimize the L1 gas footprint of the system, minimizing per-Transaction cost. - -Some other Layer 2 systems provide some of these features, but to our knowledge no other system offers the same combination of features at the same cost. - -Nitro is a major upgrade to Arbitrum, improving over "classic" Arbitrum in several ways: - -- **Advanced Calldata Compression,** which further drives down transaction costs on Arbitrum by reducing the amount of data posted to L1. -- **Separate Contexts For Common Execution and Fault Proving,** increasing the performance of L1 nodes, and thus offering lower fees. -- **Ethereum L1 Gas Compatibility,** bringing pricing and accounting for EVM operations perfectly in line with Ethereum. -- **Additional L1 Interoperability,** including tighter synchronization with L1 Block numbers, and full support for all Ethereum L1 precompiles. -- **Safe Retryables,** eliminating the failure mode where a Retryable Ticket fails to get created. -- **Geth Tracing,** for even broader debugging support. -- And many, many more changes. - -## The Big Picture - -At the most basic level, an Arbitrum chain works like this: - - - -Users and contracts put messages into the inbox. The chain reads the messages one at a time, and processes each one. This updates the state of the chain and produces some outputs. - -If you want an Arbitrum chain to process a transaction for you, you need to put that transaction into the chain’s inbox. Then the chain will see your transaction, execute it, and produce some outputs: a transaction receipt, and any withdrawals that your transaction initiated. - -Execution is deterministic -- which means that the chain’s behavior is uniquely determined by the contents of its inbox. Because of this, the result of your transaction is knowable as soon as your transaction has been put in the inbox. Any Arbitrum node will be able to tell you the result. (And you can run an Arbitrum node yourself if you want.) - -All of the technical detail in this document is connected to this diagram. To get from this diagram to a full description of Arbitrum, we’ll need to answer questions like these: - -- Who keeps track of the inbox, Chain state, and outputs? -- How does Arbitrum make sure that the chain state and outputs are correct? -- How can Ethereum users and contracts interact with Arbitrum? -- How does Arbitrum support Ethereum-compatible contracts and transactions? -- How are ETH and tokens transferred into and out of Arbitrum chains, and how are they managed while on the chain? -- How can I run my own Arbitrum node or Validator? - -## Nitro's Design: The Four Big Ideas - -The essence of Nitro, and its key innovations, lie in four big ideas. We'll list them here with a very quick summary of each, then we'll unpack them in more detail in later sections. - -**Big Idea: Sequencing, Followed by Deterministic Execution**: Nitro processes transactions with a two-phase strategy. First, the transactions are organized into a single ordered sequence, and Nitro commits to that sequence. Then the transactions are processed, in that sequence, by a deterministic State Transition Function. - -**Big Idea: Geth at the Core**: Nitro supports Ethereum's data structures, formats, and virtual machine by compiling in the core code of the popular go-ethereum ("Geth") Ethereum node software. Using Geth as a library in this way ensures a very high degree of compatibility with Ethereum. - -**Big Idea: Separate Execution from Proving**: Nitro takes the same source code and compiles it twice, once to native code for execution in a Nitro node, optimized for speed, and again to WASM for use in proving, optimized for portability and security. - -**Big Idea: Optimistic Rollup with Interactive Fraud Proofs**: Nitro settles transactions to the Layer 1 Ethereum chain using an optimistic rollup protocol, including the interactive fraud proofs pioneered by Arbitrum. - -## Sequencing, Followed by Deterministic Execution - -This diagram summarizes how transactions are processed in Nitro. - -![seq-then-exec](../assets/seq-then-exec.png) - -Let's follow a user's transaction through this process. - -First, the user creates a transaction, uses their wallet to sign it, and sends it to the Nitro chain's Sequencer. The Sequencer's job, as its name implies, is to take the arriving transactions, put them into an ordered sequence, and publish that sequence. - -Once the transactions are sequenced, they are run through the _state transition function_, one by one, in order. The state transition function takes as input the current state of the chain (account balances, contract code, and so on), along with the next transaction. It updates the state and sometimes emits a new Layer 2 block on the Nitro chain. - -Because the protocol doesn't trust the Sequencer not to put garbage into its sequence, the state transition function will detect and discard any invalid (e.g., improperly formed) transactions in the sequence. A well-behaved Sequencer will filter out invalid transactions so the state transition function never sees them--and this reduces cost and therefore keeps transactions fees low--but Nitro will still work correctly no matter what the Sequencer puts into its feed. (Transactions in the feed are signed by their senders, so the Sequencer can't create forged transactions.) - -The state transition function is deterministic, which means that its behavior depends only on the current state and the contents of the next transaction--and nothing else. Because of this determinism, the result of a transaction T will depend only on the genesis state of the chain, the transactions before T in the sequence, and T itself. - -It follows that anyone who knows the transaction sequence can compute the state transition function for themselves--and all honest parties who do this are guaranteed to get identical results. This is the normal way that Nitro nodes operate: get the transaction sequence, and run the state transition function locally. No consensus mechanism is needed for this. - -### How the Sequencer Publishes the Sequence - -So how do nodes get the sequence? The Sequencer publishes it in two ways: a real-time feed, and batches posted on L1 Ethereum. - -The real-time feed is published by the Sequencer so that anyone who subscribes to the feed receives instant notifications of each transaction as it is sequenced. Nitro nodes can subscribe to the feed directly from the Sequencer, or through a relay that forwards the feed. The feed represents the Sequencer's promise that it will record transactions in a particular order. If the Sequencer is honest and doesn't have a long downtime, this promise will be kept. So anyone who trusts the Sequencer to keep its promises can rely on the feed to get instant information about the transaction sequence--and they can run the sequenced transactions through the state transition function to learn the results of each transaction immediately. This is "soft finality" for transactions; it's "soft" because it depends on the Sequencer keeping its promises. - -The Sequencer also publishes its sequence on the L1 Ethereum chain. Periodically--perhaps every few minutes in production--the Sequencer concatenates the next group of transactions in the feed, compresses them for efficiency, and posts the result as calldata on Ethereum. This is the final and official record of the transaction sequence. As soon as this Ethereum transaction has finality on Ethereum, the Layer 2 Nitro transactions it records will have finality. These transactions are final because their position in the sequence has finality, and the outcome of the transactions is deterministic and knowable to any party. This is "hard finality". - -The Sequencer's batches are compressed using a general-purpose data compression algorithm called "brotli", on its highest-compression setting. - -## Geth at the Core - -The second key design idea in Nitro is "geth at the core." Here "geth" refers to go-ethereum, the most common node software for Ethereum. As its name would suggest, go-ethereum is written in the Go programming language, as is almost all of Nitro. - -![geth-sandwich](../assets/geth-sandwich.png) - -The software that makes up a Nitro node can be thought of as built in three main layers, which are shown above: - -- The base layer is the core of geth--the parts of Geth that emulate the execution of EVM contracts and maintain the data structures that make up the Ethereum state. Nitro compiles in this code as a library, with a few minor modifications to add necessary hooks. -- The middle layer, which we call ArbOS, is custom software that provides additional functions associated with Layer 2 functionality, such as decompressing and parsing the Sequencer's data batches, accounting for Layer 1 gas costs and collecting fees to reimburse for them, and supporting cross-chain Bridge functionalities such as deposits of Ether and tokens from L1 and withdrawals of the same back to L1. We'll dig in to the details of ArbOS below. -- The top layer consists of node software, mostly drawn from geth. This handles connections and incoming RPC requests from clients and provides the other top-level functionality required to operate an Ethereum-compatible Blockchain node. - -Because the top and bottom layers rely heavily on code from geth, this structure has been dubbed a "geth sandwich." Strictly speaking, Geth plays the role of the bread in the sandwich, and ArbOS is the filling, but this sandwich is named for the bread. - -The State Transition Function consists of the bottom Geth layer, and a portion of the middle ArbOS layer. In particular, the STF is a designated function in the source code, and implicitly includes all of the code called by that function. The STF takes as input the bytes of a transaction received in the inbox, and has access to a modifiable copy of the Ethereum state tree. Executing the STF may modify the state, and at the end will emit the header of a new block (in Ethereum's block header format) which will be appended to the Nitro chain. - -## Separating Execution from Proving - -One of the challenges in designing a practical rollup system is the tension between wanting the system to perform well in ordinary execution, versus being able to reliably prove the results of execution. Nitro resolves this tension by using the same source code for both execution and proving, but compiling it to different targets for the two cases. - -When compiling the Nitro node software for _execution_, the ordinary Go compiler is used, producing native code for the target architecture, which of course will be different for different node deployments. (The node software is distributed in source code form, and as a Docker image containing a compiled binary.) - -Separately, for _proving_, the portion of the code that is the State Transition Function is compiled by the Go compiler to WebAssembly (wasm), which is a typed, portable machine code format. The wasm code then goes through a simple transformation into a format we call WAVM, which is detailed below. If there is a dispute about the correct result of computing the STF, it is resolved with reference to the WAVM code. - -#### WAVM - -The wasm format has many features that make it a good vehicle for fraud proofs---it is portable, structured, well-specified, and has reasonably good tools and support---but it needs a few modifications to do the job completely. Nitro uses a slightly modified version of wasm, which we call WAVM. A simple transformation stage turns the wasm code produced by the Go compiler into WAVM code suitable for proving. - -WAVM differs from wasm in three main ways. First, WAVM removes some features of wasm that are not generated by the Go compiler; the transformation phase verifies that these features are not present. - -Second, WAVM restricts a few features of wasm. For example, WAVM does not contain floating-point instructions, so the transformer replaces floating-point instructions with calls to the Berkeley SoftFloat library. (We use software floating-point to reduce the risk of floating-point incompatibilities between architectures. The core Nitro functions never use floating-point, but the Go runtime does use some floating-point operations.) WAVM does not contain nested control flow, so the transformer flattens control flow constructs, turning control flow instructions into jumps. Some wasm instructions take a variable amount of time to execute, which we avoid in WAVM by transforming them into constructs using fixed cost instructions. These transformations simplify proving. - -Third, WAVM adds a few opcodes to enable interaction with the blockchain environment. For example, new instructions allow the WAVM code to read and write the chain's global state, to get the next message from the chain's inbox, or to signal a successful end to executing the State Transition Function. - -#### ReadPreImage and the Hash Oracle Trick - -The most interesting new instruction is `ReadPreImage` which takes as input a hash `H` and an offset `I`, and returns the word of data at offset `I` in the preimage of `H` (and the number of bytes written, which is zero if `I` is at or after the end of the preimage). Of course, it is not feasible in general to produce a preimage from an arbitrary hash. For safety, the `ReadPreImage` instruction can only be used in a context where the preimage is publicly known, and where the size of the preimage is known to be less than a fixed upper bound of about 110 kbytes. - -(In this context, "publicly known" information is information that can be derived or recovered efficiently by any honest party, assuming that the full history of the L1 Ethereum chain is available. For convenience, a hash preimage can also be supplied by a third party such as a public server, and the correctness of the supplied value is easily verified.) - -As an example, the state of a Nitro chain is maintained in Ethereum's state tree format, which is organized as a Merkle tree. Nodes of the tree are stored in a database, indexed by the Merkle hash of the node. In Nitro, the state tree is kept outside of the State Transition Function's storage, with the STF only knowing the root hash of the tree. Given the hash of a tree node, the STF can recover the tree node's contents by using `ReadPreImage`, relying on the fact that the full contents of the tree are publicly known and that nodes in the Ethereum state tree will always be smaller than the upper bound on preimage size. In this manner, the STF is able to arbitrarily read and write to the state tree, despite only storing its root hash. - -The only other use of `ReadPreImage` is to fetch the contents of recent L2 Block headers, given the header hash. This is safe because the block headers are publicly known and have bounded size. - -This "hash oracle trick" of storing the Merkle hash of a data structure, and relying on protocol participants to store the full structure and thereby support fetch-by-hash of the contents, goes back to the original Arbitrum design. - -## Optimistic Rollup - -Arbitrum is an optimistic rollup. Let’s unpack that term. - -_Rollup_ - -Arbitrum is a rollup, which means that the inputs to the chain -- the messages that are put into the inbox -- are all recorded on the Ethereum chain as calldata. Because of this, everyone has the information they would need to determine the current correct state of the chain -- they have the full history of the inbox, and the results are uniquely determined by the inbox history, so they can reconstruct the state of the chain based only on public information, if needed. - -This also allows anyone to be a full participant in the Arbitrum protocol, to run an Arbitrum node or participate as a validator. Nothing about the history or state of the chain is a secret. - -_Optimistic_ - -Arbitrum is optimistic, which means that Arbitrum advances the state of its chain by letting any party (a “validator”) post on Layer 1 a rollup block that that party claims is correct, and then giving everyone else a chance to Challenge that claim. If the Challenge Period (6.4 days) passes and nobody has challenged the claimed rollup block, Arbitrum confirms the rollup block as correct. If someone challenges the claim during the challenge period, then Arbitrum uses an efficient dispute resolution protocol (detailed below) to identify which party is lying. The liar will forfeit a deposit, and the truth-teller will take part of that deposit as a reward for their efforts (some of the deposit is burned, guaranteeing that the liar is punished even if there's some collusion going on). - -Because a party who tries to cheat will lose a deposit, attempts to cheat should be very rare, and the normal case will be a single party posting a correct rollup block, and nobody challenging it. - -## Resolving disputes using interactive fraud proofs - -Among optimistic rollups, the most important design decision is how to resolve disputes. Suppose Alice claims that the chain will produce a certain result, and Bob disagrees. How will the protocol decide which version to accept? - -There are basically two choices: interactive proving, or re-executing transactions. Arbitrum uses interactive proving, which we believe is more efficient and more flexible. Much of the design of Arbitrum follows from this fact. - -### Interactive proving - -The idea of interactive proving is that Alice and Bob will engage in a back-and-forth protocol, refereed by an L1 contract, to resolve their dispute with minimal work required from any L1 contract. - -Arbitrum's approach is based on Dissection of the dispute. If Alice's claim covers N steps of execution, she posts two claims of size N/2 which combine to yield her initial N-step claim, then Bob picks one of Alice's N/2-step claims to challenge. Now the size of the dispute has been cut in half. This process continues, cutting the dispute in half at each stage, until they are disagreeing about a single step of execution. Note that so far the L1 referee hasn't had to think about execution "on the merits". It is only once the dispute is narrowed down to a single step that the L1 referee needs to resolve the dispute by looking at what the instruction actually does and whether Alice's claim about it is correct. - -The key principle behind interactive proving is that if Alice and Bob are in a dispute, Alice and Bob should do as much off-chain work as possible needed to resolve their dispute, rather than putting that work onto an L1 contract. - -### Re-executing transactions - -The alternative to interactive proving would be to have a rollup block contain a claimed machine state hash after every individual transaction. Then in case of a dispute, the L1 referee would emulate the execution of an entire transaction, to see whether the outcome matches Alice's claim. - -### Why interactive proving is better - -We believe strongly that interactive proving is the superior approach, for the following reasons. - -**More efficient in the optimistic case**: Because interactive proving can resolve disputes that are larger than one transaction, it can allow a rollup block to contain only a single claim about the end state of the chain after all of the execution covered by the block. By contrast, reexecution requires posting a state claim for each transaction within the rollup block. With hundred or thousands of transactions per rollup block, this is a substantial difference in L1 footprint -- and L1 footprint is the main component of cost. - -**More efficient in the pessimistic case**: In case of a dispute, interactive proving requires the L1 referee contract only to check that Alice and Bob's actions "have the right shape", for example, that Alice has divided her N-step claim into two claims half as large. (The referee doesn't need to evaluate the correctness of Alice's claims--Bob does that, off-chain.) Only one instruction needs to be reexecuted. By contrast, reexecution requires the L1 referee to emulate the execution of an entire transaction. - -**Higher per-tx gas limit:** Interactive proving can escape from Ethereum's tight per-transaction gas limit. The gas limit isn't infinite, for obvious reasons, but it can be larger than on Ethereum. As far as Ethereum is concerned, the only downside of a gas-heavy Arbitrum transaction is that it may require an interactive Fraud proof with slightly more steps (and only if indeed it is fraudulent). By contrast, reexecution must impose a _lower_ gas limit than Ethereum, because it must be possible to emulate execution of the transaction (which is more expensive than executing it directly) within a single Ethereum transaction. - -**More implementation flexibility:** Interactive proving allows more flexibility in implementation. All that is necessary is the ability to verify a one-step proof on Ethereum. By contrast, reexecution approaches are tethered to limitations of the EVM. - -### Interactive proving drives the design of Arbitrum - -Much of the design of Arbitrum is driven by the opportunities opened up by interactive proving. If you're reading about some feature of Arbitrum, and you're wondering why it exists, two good questions to ask are: "How does this support interactive proving?" and "How does this take advantage of interactive proving?" The answers to most "why" questions about Arbitrum relate to interactive proving. - -## Arbitrum Rollup Protocol - -Before diving into the rollup protocol, there are two things we need to cover. - -First, _if you’re an Arbitrum user or developer, you don’t need to understand the rollup protocol_. You don’t ever need to think about it, unless you want to. Your relationship with it can be like a train passenger’s relationship with the train’s engine: you know it exists, you rely on it to keep working, but you don’t spend your time monitoring it or studying its internals. - -You’re welcome to study, observe, and even participate in the rollup protocol, but you don’t need to, and most people won’t. So if you’re a typical train passenger who just wants to read or talk to your neighbor, you can skip right to the [next section](#validators) of this document. If not, read on! - -The second thing to understand about the rollup protocol is that _the protocol doesn’t decide the results of transactions, it only confirms the results_. The results are uniquely determined by the sequence of messages in the chain’s inbox. So once your transaction message is in the chain’s inbox, its result is knowable--and Arbitrum nodes will report that your transaction is done. The role of the rollup protocol is to confirm transaction results that, as far as Arbitrum users are concerned, have already occurred. (This is why Arbitrum users can effectively ignore the rollup protocol.) - -You might wonder why we need the rollup protocol. If everyone knows the results of transactions already, why bother confirming them? The rollup protocol exists for two reasons. First, somebody might lie about a result, and we need a definitive, trustless way to tell who is lying. Second, Ethereum doesn’t know the results. The whole point of a Layer 2 scaling system is to run transactions without Ethereum needing to do all of the work--and indeed Arbitrum can go fast enough that Ethereum couldn’t hope to monitor every Arbitrum transaction. But once a result is confirmed, Ethereum knows about it and can rely on it, enabling operations on Ethereum such as processing withdrawals of funds from Nitro back to L1. - -With those preliminaries behind us, let’s jump into the details of the rollup protocol. - -The parties who participate in the protocol are called _validators_. Some validators will choose to be bonders--they will place an ETH deposit which they’ll be able to recover if they’re not caught cheating. In the common case, it's expected that only one validator will be bonded, since as long as it's bonded on the current outcome, and there are no conflicting claims, there's no need for other parties to bond/take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/bonders are allowlisted (see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization)). "Watchtower validators," who monitor the chain but don't take any on-chain actions, can be run permissionlessly (see ["validators"](#validators) below). - -The key security property of the rollup protocol is that any one honest validator can force the correct execution of the chain to be confirmed. This means that execution of an Arbitrum chain is as trustless as Ethereum. You, and you alone (or someone you hire) can force your transactions to be processed correctly. And that is true no matter how many malicious people are trying to stop you. - -### The Rollup Chain - -The rollup protocol tracks a chain of rollup blocks---we'll call these "RBlocks" for clarity. They're not the same as Layer 1 Ethereum blocks, and also not the same as Layer 2 Nitro blocks. You can think of the RBlocks as forming a separate chain, which the Arbitrum Rollup Protocol manages and oversees. - -Validators can propose RBlocks. New RBlocks will be _unresolved_ at first. Eventually every RBlock will be _resolved_, by being either _confirmed_ or _rejected_. The confirmed RBlocks make up the confirmed history of the chain. - -:::note - -Validators and proposers serve different roles. Validators validate transactions to the State Transition Function (STF) and chain state, whereas proposers can also assert and challenge the chain state. - -::: - -Each RBlock contains: - -- the RBlock number -- the predecessor RBlock number: RBlock number of the last RBlock before this one that is (claimed to be) correct -- the number of L2 blocks that have been created in the chain's history -- the number of inbox messages that have been consumed in the chain’s history -- a hash of the outputs produced over the chain’s history. - -Except for the RBlock number, the contents of the RBlock are all just claims by the RBlock's proposer. Arbitrum doesn’t know at first whether any of these fields are correct. If all of these fields are correct, the protocol should eventually confirm the RBlock. If one or more of these fields are incorrect, the protocol should eventually reject the RBlock. - -An RBlock is implicitly claiming that its predecessor RBlock is correct. This implies, transitively, that an RBlock implicitly claims the correctness of a complete history of the chain: a sequence of ancestor RBlocks that reaches all the way back to the birth of the chain. - -An RBlock is also implicitly claiming that its older siblings (older RBlocks with the same predecessor), if there are any, are incorrect. If two RBlocks are siblings, and the older sibling is correct, then the younger sibling is considered incorrect, even if everything else in the younger sibling is true. - -The RBlock is assigned a deadline, which says how long other validators have to respond to it. If you’re a validator, and you agree that an RBlock is correct, you don’t need to do anything. If you disagree with an RBlock, you can post another RBlock with a different result, and you’ll probably end up in a challenge against the first RBlock's bonder. (More on challenges below.) - -In the normal case, the rollup chain will look like this: - -```mermaid -graph RL - f["First unresolved block"] - l["Latest confirmed block"] - - 98-->97-->96-->95 - f-.-95 - - 95-->94 - l-.-94 - - 94-->93-->92-->91-->90-->x[...] - - subgraph Legend - direction RL - Confirmed - Unconfirmed - end - - classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff - class 94,93,92,91,90,x,Confirmed confirmed - - - classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff - class 98,97,96,95,Unconfirmed unconfirmed - - classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 - class l,f note -``` - -On the left, representing an earlier part of the chain’s history, we have confirmed RBlocks. These have been fully accepted and recorded by the Layer 1 contracts that manage the chain. The newest of the confirmed RBlocks, RBlock 94, is called the “latest confirmed RBlock.” On the right, we see a set of newer proposed RBlocks. The protocol can’t yet confirm or reject them, because their deadlines haven’t run out yet. The oldest RBlock whose fate has yet to be determined, RBlock 95, is called the “first unresolved RBlock.” - -Notice that a proposed RBlock can build on an earlier proposed RBlock. This allows validators to continue proposing RBlocks without needing to wait for the protocol to confirm the previous one. Normally, all of the proposed RBlocks will be valid, so they will all eventually be accepted. - -Here’s another example of what the chain state might look like, if several validators are being malicious. It’s a contrived example, designed to illustrate a variety of cases that can come up in the protocol, all smashed into a single scenario. - -```mermaid -graph RL - subgraph Legend - direction RL - Confirmed - Rejected - Unconfirmed - end - - f["First unresolved block"] - l["Latest confirmed block"] - - - l-.-103 - f-.-106 - - 108-->107-->106-->103 - 111-->104 - 101-->100 - 105-->104-->103 - 110-->109-->103-->102-->100-->x[...] - - classDef confirmed fill:#47b860,stroke:#37914c,stroke-width:2px,color:#fff - class 100,102,103,x,Confirmed confirmed - - classDef rejected fill:#fdaa07,stroke:#fd8607,stroke-width:2px,color:#fff - class 101,104,105,Rejected rejected - - classDef unconfirmed fill:#2aa1f0,stroke:#1c86ca,stroke-width:2px,color:#fff - class 106,107,108,109,110,111,Unconfirmed unconfirmed - - classDef note fill:#F1F5F6,stroke:#dbdede,stroke-width:1px,color:#000 - class l,f note - -``` - -There’s a lot going on here, so let’s unpack it. - -- RBlock 100 has been confirmed. -- RBlock 101 claimed to be a correct successor to RBlock 100, but 101 was rejected (hence it is orange). -- RBlock 102 was eventually confirmed as the correct successor to 100. -- RBlock 103 was confirmed and is now the latest confirmed RBlock. -- RBlock 104 was proposed as a successor to RBlock 103, and 105 was proposed as a successor to 104. 104 was rejected as incorrect, and as a consequence 105 was rejected because its predecessor was rejected. -- RBlock 106 is unresolved. It claims to be a correct successor to RBlock 103 but the protocol hasn’t yet decided whether to confirm or reject it. It is the first unresolved RBlock. -- RBlocks 107 and 108 claim to chain from 106. They are also unresolved. If 106 is rejected, they will be automatically rejected too. -- RBlock 109 disagrees with RBlock 106, because they both claim the same predecessor. At least one of them will eventually be rejected, but the protocol hasn’t yet resolved them. -- RBlock 110 claims to follow 109. It is unresolved. If 109 is rejected, 110 will be automatically rejected too. -- RBlock 111 claims to follow 104. 111 will inevitably be rejected because its predecessor has already been rejected. But it hasn’t been rejected yet, because the protocol resolves RBlocks in RBlock number order, so the protocol will have to resolve 106 through 110, in order, before it can resolve 111. After 110 has been resolved, 111 can be rejected immediately. - -Again: this sort of thing is very unlikely in practice. In this diagram, at least four parties must have bonded on wrong RBlocks, and when the dust settles at least four parties will have lost their bonds. The protocol handles these cases correctly, of course, but they’re rare corner cases. This diagram is designed to illustrate the variety of situations that are possible in principle, and how the protocol would deal with them. - -### Staking - -At any given time, some validators will be bonders, and some will not. Bonders deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the bonder loses a challenge. Nitro chains accept bonds in ETH. - -A single bond can cover a chain of RBlocks. Every bonder is bonded on the latest confirmed RBlock; and if you’re bonded on an RBlock, you can also bond on one successor of that RBlock. So you might be bonded on a sequence of RBlocks that represent a single coherent claim about the correct history of the chain. A single bond suffices to commit you to that sequence of RBlocks. - -In order to create a new RBlock, you must be a bonder, and you must already be bonded on the predecessor of the new RBlock you’re creating. The bond requirement for RBlock creation ensures that anyone who creates a new RBlock has something to lose if that RBlock is eventually rejected. - -The protocol keeps track of the current required bond amount. Normally this will equal the base bond amount, which is a parameter of the Nitro chain. But if the chain has been slow to make progress lately, the required bond will increase, as described in more detail below. - -The rules for staking are as follows: - -- If you’re not bonded, you can bond on the latest confirmed RBlock. When doing this, you deposit the current minimum bond amount. -- If you’re bonded on an RBlock, you can also add your bond to any one successor of that RBlock. (The protocol tracks the maximum RBlock number you’re bonded on, and lets you add your bond to any successor of that RBlock, updating your maximum to that successor.) This doesn’t require you to place a new bond. - - A special case of adding your bond to a successor RBlock is when you create a new RBlock as a successor to an RBlock you’re already bonded on. -- If you’re bonded only on the latest confirmed RBlock (and possibly earlier RBlocks), you or anyone else can ask to have your bond refunded. Your bonded funds will be returned to you, and you will no longer be a bonder. -- If you lose a challenge, your bond is removed from all RBlocks and you forfeit your bonded funds. - -Notice that once you are bonded on an unresolved RBlock, there is no way to unbond. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your bond. The only way to get your bond back is to wait until all of the RBlocks you are bonded on are confirmed. - -#### Setting the current minimum bond amount - -One detail we deferred earlier is how the current minimum bond amount is set. Normally, this is just equal to the base bond amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the bond requirement will escalate temporarily. Specifically, the base bond amount is multiplied by a factor that is exponential in the time since the deadline of the first unresolved RBlock passed. This ensures that if malicious parties are placing false bonds to try to delay progress (despite the fact that they’re losing those bonds), the bond requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the bond requirement will go back down. - -### Rules for Confirming or Rejecting RBlocks - -The rules for resolving RBlocks are fairly simple. - -The first unresolved RBlock can be confirmed if: - -- the RBlock's predecessor is the latest confirmed RBlock, and -- the RBlock's deadline has passed, and -- there is at least one bonder, and -- All bonders are bonded to the RBlock. - -The first unresolved RBlock can be rejected if: - -- the RBlock's predecessor has been rejected, or -- all of the following are true: - - the RBlock's deadline has passed, and - - there is at least one bonder, and - - no bonder is bonded on the RBlock. - -A consequence of these rules is that once the first unresolved RBlock's deadline has passed (and assuming there is at least one bonder bonded on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one bonder is bonded on it and at least one bonder is bonded on a different RBlock with the same predecessor. If this happens, the two bonders are disagreeing about which RBlock is correct. It’s time for a challenge, to resolve the disagreement. - -## Challenges - -Suppose the rollup chain looks like this: - -![img](https://lh4.googleusercontent.com/kAZY9H73dqcHvboFDby9nrtbYZrbsHCYtE5X9NIZQsvcz58vV0WUWUq1xsYKzYWQSc1nPZ8W86LLX0lD3y-ctEaG2ISa2Wpz2pYxTzW09P1UvqSDuoqkHlGDYLLMTzLqX4rlP8Ca) - -RBlocks 93 and 95 are siblings (they both have 92 as predecessor). Alice is bonded on 93 and Bob is bonded on 95. - -At this point we know that Alice and Bob disagree about the correctness of RBlock 93, with Alice committed to 93 being correct and Bob committed to 93 being incorrect. (Bob is bonded on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) - -Whenever two bonders are bonded on sibling RBlocks, and neither of those bonders is already in a challenge, anyone can start a challenge between the two. The rollup protocol will record the challenge and referee it, eventually declaring a winner and confiscating the loser’s bond. The loser will be removed as a bonder. - -The challenge is a game in which Alice and Bob alternate moves, with an Ethereum contract as the referee. Alice, the defender, moves first. - -The game will operate in two phases: dissection, followed by one-step proof. Dissection will narrow down the size of the dispute until it is a dispute about just one instruction of execution. Then the one-step proof will determine who is right about that one instruction. - -We’ll describe the dissection part of the protocol twice. First, we’ll give a simplified version which is easier to understand but less efficient. Then we’ll describe how the real version differs from the simplified one. - -### Dissection Protocol: Simplified Version - -Alice is defending the claim that starting with the state in the predecessor RBlock, the state of the Virtual Machine can advance to the state specified in RBlock A. Essentially she is claiming that the Virtual Machine can execute N instructions, and that that execution will consume M inbox messages and transform the hash of outputs from H’ to H. - -Alice’s first move requires her to dissect her claims about intermediate states between the beginning (0 instructions executed) and the end (N instructions executed). So we require Alice to divide her claim in half, and post the state at the half-way point, after N/2 instructions have been executed. - -Now Alice has effectively bisected her N-step Assertion into two (N/2)-step assertions. Bob has to point to one of those two half-size assertions and claim it is wrong. - -At this point we’re effectively back in the original situation: Alice having made an assertion that Bob disagrees with. But we have cut the size of the assertion in half, from N to N/2. We can apply the same method again, with Alice bisecting and Bob choosing one of the halves, to reduce the size to N/4. And we can continue bisecting, so that after a logarithmic number of rounds Alice and Bob will be disagreeing about a single step of execution. That’s where the dissection phase of the protocol ends, and Alice must make a one-step proof which will be checked by the EthBridge. - -### Why Dissection Correctly Identifies a Cheater - -Before talking about the complexities of the real Challenge protocol, let’s stop to understand why the simplified version of the protocol is correct. Here correctness means two things: (1) if Alice’s initial claim is correct, Alice can always win the challenge, and (2) if Alice’s initial claim is incorrect, Bob can always win the challenge. - -To prove (1), observe that if Alice’s initial claim is correct, she can offer a truthful midpoint claim, and both of the implied half-size claims will be correct. So whichever half Bob objects to, Alice will again be in the position of defending a correct claim. At each stage of the protocol, Alice will be defending a correct claim. At the end, Alice will have a correct one-step claim to prove, so that claim will be provable and Alice can win the challenge. - -To prove (2), observe that if Alice’s initial claim is incorrect, this can only be because her claimed endpoint after N steps is incorrect. Now when Alice offers her midpoint state claim, that midpoint claim is either correct or incorrect. If it’s incorrect, then Bob can challenge Alice’s first-half claim, which will be incorrect. If Alice’s midpoint state claim is correct, then her second-half claim must be incorrect, so Bob can challenge that. So whatever Alice does, Bob will be able to challenge an incorrect half-size claim. At each stage of the protocol, Bob can identify an incorrect claim to challenge. At the end, Alice will have an incorrect one-step claim to prove, which she will be unable to do, so Bob can win the challenge. - -(If you’re a stickler for mathematical precision, it should be clear how these arguments can be turned into proofs by induction on N.) - -### The Real Dissection Protocol - -The real dissection protocol is conceptually similar to the simplified one described above, but with several changes that improve efficiency or deal with necessary corner cases. Here is a list of the differences. - -**Dissection over L2 blocks, then over instructions:** Alice's assertion is over an RBlock, which asserts the result of creating some number of Layer 2 Nitro blocks. Dissection first occurs over these Layer 2 blocks, to narrow the dispute down to a dispute about a single Layer 2 Nitro block. At this point, the dispute transforms into a dispute about a single execution of the State Transition Function or in other words about the execution of a sequence of WAVM instructions. The protocol then executes the recursive dissection sub-protocol again, this time over WAVM instructions, to narrow the dispute to a single instruction. The dispute concludes with a one-step proof of a single instruction (or a party failing to act and losing by timeout). - -**K-way dissection:** Rather than dividing a claim into two segments of size N/2, we divide it into K segments of size N/K. This requires posting K-1 intermediate claims, at points evenly spaced through the claimed execution. This reduces the number of rounds by a factor of log(K)/log(2). - -**Answer a dissection with a dissection:** Rather than having each round of the protocol require two moves, where Alice dissects and Bob chooses a segment to challenge, we instead require Bob, in challenging a segment, to post his own claimed endpoint state for that segment (which must differ from Alice’s) as well as his own dissection of his version of the segment. Alice will then respond by identifying a subsegment, posting an alternative endpoint for that segment, and dissecting it. This reduces the number of moves in the game by an additional factor of 2, because the size is cut by a factor of K for every move, rather than for every two moves. - -**Deal With the Empty-Inbox Case**: The real AVM can’t always execute N units of gas without getting stuck. The machine might halt, or it might have to wait because its inbox is exhausted so it can’t go on until more messages arrive. So Bob must be allowed to respond to Alice’s claim of N units of execution by claiming that N steps are not possible. The real protocol thus allows any response (but not the initial claim) to claim a special end state that means essentially that the specified amount of execution is not possible under the current conditions. - -**Time Limits:** Each player is given a time allowance. The total time a player uses for all of their moves must be less than the time allowance, or they lose the game. Think of the time allowance as being about a week. - -It should be clear that these changes don’t affect the basic correctness of the challenge protocol. They do, however, improve its efficiency and enable it to handle all of the cases that can come up in practice. - -### Efficiency - -The challenge protocol is designed so that the dispute can be resolved with a minimum of work required by the protocol (via its Layer 1 Ethereum contracts) in its role as referee. When it is Alice’s move, the protocol only needs to keep track of the time Alice uses, and ensure that her move does include K-1 intermediate points as required. The protocol doesn’t need to pay attention to whether those claims are correct in any way; it only needs to know whether Alice’s move “has the right shape”. - -The only point where the protocol needs to evaluate a move “on the merits” is at the one-step proof, where it needs to look at Alice’s proof and determine whether the proof that was provided does indeed establish that the virtual machine moves from the before state to the claimed after state after one step of computation. - -## Validators - -Some Arbitrum nodes will choose to act as _validators_. This means that they watch the progress of the rollup protocol and participate in that protocol to advance the state of the chain securely. - -Not all nodes will choose to do this. Because the rollup protocol doesn’t decide what the chain will do but merely confirms the correct behavior that is fully determined by the inbox messages, a node can ignore the rollup protocol and simply compute for itself the correct behavior. For more on what such nodes might do, see the [Full Nodes](#full-nodes) section. - -Offchain Labs provides open source validator software, including -a pre-built Docker image. - -Every validator can choose their own approach, but we expect validators to follow three common strategies: - -- The _active validator_ strategy tries to advance the state of the chain by proposing new RBlocks. An Active Validator is always bonded, because creating an RBlock requires being bonded. A chain really only needs one honest active validator; any more is an inefficient use of resources. For the Arbitrum One chain, Offchain Labs runs an active validator. -- The _defensive validator_ strategy watches the rollup protocol operate. If only correct RBlocks are proposed, this strategy doesn't bond. But if an incorrect RBlock is proposed, this strategy intervenes by posting a correct RBlock or staking on a correct RBlock that another party has posted. This strategy avoids staking when things are going well, but if someone is dishonest it bonds in order to defend the correct outcome. -- The _watchtower validator_ strategy never bonds. It simply watches the rollup protocol and if an incorrect RBlock is proposed, it raises the alarm (by whatever means it chooses) so that others can intervene. This strategy assumes that other parties who are willing to bond will be willing to intervene in order to take some of the dishonest proposer’s bond, and that that can happen before the dishonest RBlock’s deadline expires. (In practice this will allow several days for a response.) - -Under normal conditions, validators using the defensive and watchtower strategies won’t do anything except observe. A malicious actor who is considering whether to try cheating won’t be able to tell how many defensive and watchtower validators are operating incognito. Perhaps some defensive validators will announce themselves, but others probably won’t, so a would-be attacker will always have to worry that defenders are waiting to emerge. - -The underlying protocol supports permissionless validation, i.e.,--anyone can do it. Currently on Arbitrum One, validators that require bond (i.e., active and defensive validators) are whitelisted; see ["State of Progressive Decentralization"](https://docs.arbitrum.foundation/state-of-progressive-decentralization). - -Who will be validators? Anyone will be able to do it, but most people will choose not to. In practice we expect people to validate a chain for several reasons. - -- Validators could be paid for their work, by the party that created the chain or someone else. A chain could be configured such that a portion of the funds from user transaction fees are paid directly to validators. -- Parties who have significant assets at bond on a chain, such as dApp developers, exchanges, power-users, and liquidity providers, may choose to validate in order to protect their investment. -- Anyone who chooses to validate can do so. Some users will probably choose to validate in order to protect their own interests or just to be good citizens. But ordinary users don’t need to validate, and we expect that the vast majority of users won’t. - -## ArbOS - -ArbOS is a trusted "system glue" component that runs at Layer 2 as part of the State Transition Function. ArbOS provides functions needed for a Layer 2 system, such as cross-chain communication, resource accounting and Layer 2 related fee economics, and chain management. - -### Why ArbOS? - -In Arbitrum, much of the work that would otherwise have to be done expensively at Layer 1 is instead done by ArbOS, trustlessly performing these functions at the speed and low cost of Layer 2. - -Supporting these functions in Layer 2 trusted software, rather than building them in to the L1-enforced rules of the architecture as Ethereum does, offers significant advantages in cost because these operations can benefit from the lower cost of computation and storage at Layer 2, instead of having to manage those resources as part of a Layer 1 contract. Having a trusted operating system at Layer 2 also has significant advantages in flexibility, because Layer 2 code is easier to evolve, or to customize for a particular chain, than a Layer-1 enforced architecture would be. - -## Full Nodes - -As the name suggests, full nodes in Arbitrum play the same role that full nodes play in Ethereum: they know the state of the chain and they provide an API that others can use to interact with the chain. - -Arbitrum full nodes normally "live at Layer 2" which means that they don’t worry about the rollup protocol but simply treat their Arbitrum chain as a mechanism that feeds inbox messages to the State Transition Function to evolve the Layer 2 chain and produce outputs. - -## The Sequencer - -The Sequencer is a specially designated full node, which is given limited power to control the ordering of transactions. This allows the Sequencer to guarantee the results of user transactions immediately, without needing to wait for anything to happen on Ethereum. So no need to wait five minutes or so for block confirmations--and no need to even wait 15 seconds for Ethereum to make a block. - -Clients interact with the Sequencer in exactly the same way they would interact with any full node, for example by giving their wallet software a network URL that happens to point to the Sequencer. - -[Currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization), on the Arbitrum One and Arbitrum Nova chains, the Sequencer is run by Offchain Labs. - -### Instant confirmation - -Without a Sequencer, a node can predict what the results of a Client transaction will be, but the node can't be sure, because it can't know or control how the transactions it submits will be ordered in the inbox, relative to transactions submitted by other nodes. - -The Sequencer is given more control over ordering, so it has the power to assign its clients' transactions a position in the inbox queue, thereby ensuring that it can determine the results of client transactions immediately. The Sequencer's power to reorder has limits (see below for details) but it does have more power than anyone else to influence transaction ordering. - -### Inboxes, fast and slow - -When we add a Sequencer, the operation of the inbox changes. - -- Only the Sequencer can put new messages directly into the inbox. The Sequencer tags the messages it is submitting with an Ethereum block number and timestamp. (ArbOS ensures that these are non-decreasing, adjusting them upward if necessary to avoid decreases.) -- Anyone else can submit a message, but messages submitted by non-Sequencer nodes will be put into the "Delayed Inbox" queue, which is managed by an L1 Ethereum contract. - - Messages in the delayed inbox queue will wait there until the Sequencer chooses to "release" them into the main inbox, where they will be added to the end of the inbox. A well-behaved Sequencer will typically release delayed messages after about ten minutes, for reasons explained below. - - Alternatively, if a message has been in the delayed inbox queue for longer than a maximum delay interval (currently @arbOneForceIncludePeriodHours@ hours on Arbitrum One) then anyone can force it to be promoted into the main inbox. (This ensures that the Sequencer can only delay messages but can't censor them.) - -### If the Sequencer is well-behaved... - -A well-behaved Sequencer will accept transactions from all requesters and treat them fairly, giving each one a promised transaction result as quickly as it can. - -It will also minimize the delay it imposes on non-Sequencer transactions by releasing delayed messages promptly, consistent with the goal of providing strong promises of transaction results. Specifically, if the Sequencer believes that 40 Confirmation blocks are needed to have good confidence of finality on Ethereum, then it will release delayed messages after 40 blocks. This is enough to ensure that the Sequencer knows exactly which transactions will precede its current transaction, because those preceding transactions have finality. There is no need for a benign Sequencer to delay non-Sequencer messages more than that, so it won't. - -This does mean that transactions that go through the delayed inbox will take longer to get finality. Their time to finality will roughly double, because they will have to wait one finality period for promotion, then another finality period for the Ethereum transaction that promoted them to achieve finality. - -This is the basic tradeoff of having a Sequencer: if your message uses the Sequencer, finality is C blocks faster; but if your message doesn't use the Sequencer, finality is C blocks slower. This is usually a good tradeoff, because most transactions will use the Sequencer; and because the practical difference between instant and 10-minute finality is bigger than the difference between 10-minute and 20-minute finality. - -So a Sequencer is generally a win, if the Sequencer is well behaved. - -### If the Sequencer is malicious... - -A malicious Sequencer, on the other hand, could cause some pain. If it refuses to handle your transactions, you're forced to go through the delayed inbox, with longer delay. And a malicious Sequencer has great power to front-run everyone's transactions, so it could profit greatly at users' expense. - -On Arbitrum One, Offchain Labs [currently](https://docs.arbitrum.foundation/state-of-progressive-decentralization) runs a Sequencer which is well-behaved--we promise!. This will be useful but it's not decentralized. Over time, we'll switch to decentralized, fair sequencing, as described below. - -Because the Sequencer will be run by a trusted party at first, and will be decentralized later, we haven't built in a mechanism to directly punish a misbehaving Sequencer. We're asking users to trust the centralized Sequencer at first, until we switch to decentralized fair sequencing later. - -### Decentralized fair sequencing - -Viewed from 30,000 feet, decentralized fair sequencing isn't too complicated. Instead of being a single centralized server, the Sequencer is a committee of servers, and as long as a large enough supermajority of the committee is honest, the Sequencer will establish a fair ordering over transactions. - -How to achieve this is more complicated. Research by a team at Cornell Tech, including Offchain Labs CEO and Co-founder Steven Goldfeder, developed the first-ever decentralized fair sequencing algorithm. With some improvements that are under development, these concepts will form the basis for our longer-term solution, of a fair decentralized Sequencer. - -## Bridging - -We have already covered how users interact with L2 contracts--they submit transactions by putting messages into the chain’s inbox, or having a full node Sequencer or aggregator do so on their behalf. Let’s talk about how contracts interact between L1 and L2--how an L1 contract calls an L2 contract, and vice versa. - -The L1 and L2 chains run asynchronously from each other, so it is not possible to make a cross-chain call that produces a result within the same transaction as the caller. Instead, cross-chain calls must be asynchronous, meaning that the caller submits the call at some point in time, and the call runs later. As a consequence, a cross-chain contract-to-contract call can never produce a result that is available to the calling contract (except for acknowledgement that the call was successfully submitted for later execution). - -### L1 contracts can submit L2 transactions - -An L1 contract can submit an L2 transaction, just like a user would, by calling the Nitro chain's inbox contract on Ethereum. This L2 transaction will run later, producing results that will not be available to the L1 caller. The transaction will execute at L2, but the L1 caller won’t be able to see any results from the L2 transaction. - -The advantage of this method is that it is simple and has relatively low latency. The disadvantage, compared to the other method we’ll describe soon, is that the L2 transaction might revert if the L1 caller doesn’t get the L2 gas price and max gas amount right. Because the L1 caller can’t see the result of its L2 transaction, it can’t be absolutely sure that its L2 transaction will succeed. - -This would introduce a serious a problem for certain types of L1 to L2 interactions. Consider a transaction that includes depositing a token on L1 to be made available at some address on L2. If the L1 side succeeds, but the L2 side reverts, you've just sent some tokens to the L1 inbox contract that are unrecoverable on either L2 or L1. Not good. - -### L1 to L2 ticket-based transactions - -Fortunately, we have another method for L1 to L2 calls, which is more robust against gas-related failures, that uses a ticket-based system. The idea is that an L1 contract can submit a “retryable” transaction. The Nitro chain will try to run that transaction. If the transaction succeeds, nothing else needs to happen. But if the transaction fails, Nitro will create a “ticketID” that identifies that failed transaction. Later, anyone can call a special pre-compiled contract at L2, providing the ticketID, to try redeeming the ticket and re-executing the transaction. - -When saving a transaction for retry, Nitro records the sender’s address, destination address, callvalue, and calldata. All of this is saved, and the callvalue is deducted from the sender’s account and (logically) attached to the saved transaction. - -If the redemption succeeds, the transaction is done, a receipt is issued for it, and the ticketID is canceled and can’t be used again. If the redemption fails, for example because the packaged transaction fails, the redemption reports failure and the ticketID remains available for redemption. - -Normally the original submitter will try to cause their transaction to succeed immediately, so it never needs to be recorded or retried. As an example, our "token deposit" use case above should, in the happy, common case, still only require a single signature from the user. If this initial execution fails, the ticketID will still exist as a backstop which others can redeem later. - -Submitting a transaction in this way carries a price in ETH which the submitter must pay, which varies based on the calldata size of the transaction. Once submitted, the ticket is valid for about a week. If the ticket has not been redeemed in that period, it is deleted. - -When the ticket is redeemed, the pre-packaged transaction runs with sender and origin equal to the original submitter, and with the destination, callvalue, and calldata the submitter provided at the time of submission. - -This mechanism is a bit more cumbersome than ordinary L1 to L2 transactions, but it has the advantage that the submission cost is predictable and the ticket will always be available for redemption if the submission cost is paid. As long as there is some user who is willing to redeem the ticket, the L2 transaction will eventually be able to execute and will not be silently dropped. - -### L2 to L1 ticket-based calls - -Calls from L2 to L1 operate in a similar way, with a ticket-based system. An L2 contract can call a method of the precompiled ArbSys contract, to send a transaction to L1. When the execution of the L2 transaction containing the submission is confirmed at L1 (some days later), a ticket is created in the L1 Outbox contract. That ticket can be triggered by anyone who calls a certain L1 outbox method and submits the ticketID. The ticket is only marked as redeemed if the L1 transaction does not revert. - -These L2-to-L1 tickets have unlimited lifetime, until they’re successfully redeemed. No rent is required, as the tickets (actually a Merkle hash of the tickets) are recorded in Ethereum storage, which does not require rent. (The cost of allocating storage for the ticket Merkle roots is covered by L2 transaction fees.) - -## Gas and Fees - -NitroGas (so-called to avoid confusion with Layer 1 Ethereum gas) is used by Arbitrum to track the cost of execution on a Nitro chain. It works the same as Ethereum gas, in the sense that every EVM instruction costs the same amount of gas that it would on Ethereum. - -### The Speed Limit - -The security of Nitro chains depends on the assumption that when one validator creates an RBlock, other validators will check it, and respond with a correct RBlock and a challenge if it is wrong. This requires that the other validators have the time and resources to check each RBlock quickly enough to issue a timely challenge. The Arbitrum protocol takes this into account in setting deadlines for RBlocks. - -This sets an effective Speed Limit on execution of a Nitro chain: in the long run the chain cannot make progress faster than a validator can emulate its execution. If RBlocks are published at a rate faster than the speed limit, their deadlines will get farther and farther in the future. Due to the limit, enforced by the rollup protocol contracts, on how far in the future a deadline can be, this will eventually cause new RBlocks to be slowed down, thereby enforcing the effective speed limit. - -Being able to set the speed limit accurately depends on being able to estimate the time required to validate an RBlock, with some accuracy. Any uncertainty in estimating validation time will force us to set the speed limit lower, to be safe. And we do not want to set the speed limit lower, so we try to enable accurate estimation. - -### Fees - -User transactions pay fees, to cover the cost of operating the chain. These fees are assessed and collected by ArbOS at L2. They are denominated in ETH. - -Fees are charged for two resources that a transaction can use: - -- _L2 gas_: an Ethereum-equivalent amount of gas, as required to execute the transaction on the Nitro chain, - -* _L1 calldata_: a fee per unit of L1 calldata attributable to the transaction, which is charged only if the transaction came in via the Sequencer, and is paid to the Sequencer to cover its costs, - -#### L2 gas fees - -L2 gas fees work very similarly to gas on Ethereum. A transaction uses some amount of gas, and this is multiplied by the current basefee to get the L2 gas fee charged to the transaction. - -The L2 basefee is set by a version of the "exponential mechanism" which has been widely discussed in the Ethereum community, and which has been shown equivalent to Ethereum's EIP-1559 gas pricing mechanism. - -The algorithm compares gas usage against a parameter called the "speed limit" which is the target amount of gas per second that the chain can handle sustainably over time. (Currently the speed limit on Arbitrum One is @arbOneGasSpeedLimitGasPerSec@ gas per second.) The algorithm tracks a gas backlog. Whenever a transaction consumes gas, that gas is added to the backlog. Whenever the clock ticks one second, the speed limit is subtracted from the backlog; but the backlog can never go below zero. - -Intuitively, if the backlog grows, the algorithm should increase the gas price, to slow gas usage, because usage is above the sustainable level. If the backlog shrinks, the price should decrease again because usage has been below the below the sustainable limit so more gas usage can be welcomed. - -To make this more precise, the basefee is an exponential function of the backlog, _F = exp(-a(B-b))_, where a and b are suitably chosen constants: _a_ controls how rapidly the price escalates with backlog, and _b_ allows a small backlog before the basefee escalation begins. - -#### L1 calldata fees - -L1 calldata fees exist because the Sequencer, or the Batch poster which posts the Sequencer's transaction batches on Ethereum, incurs costs in L1 gas to post transactions on Ethereum as calldata. Funds collected in L1 calldata fees are credited to the batch poster to cover its costs. - -Every transaction that comes in through the Sequencer will pay an L1 calldata fee. Transactions that come in through the delayed inbox do not pay this fee because they don't add to batch posting costs--but these transactions pay gas fees to Ethereum when they are put into the delayed inbox. - -The L1 pricing algorithm assigns an L1 calldata fee to each Sequencer transaction. First, it computes the transaction's size, which is an estimate of how many bytes the transaction will add to the compressed batch it is in; the formula for this includes an estimate of how compressible the transaction is. Second, it multiplies the computed size estimate by the current price per estimated byte, to determine the transaction's L1 calldata wei, in wei. Finally, it divides this cost by the current L2 basefee to translate the fee into L2 gas units. The result is reported as the "poster fee" for the transaction. - -The price per estimated byte is set by a dynamic algorithm that compares the total L1 calldata fees collected to the total fees actually paid by batch posters, and tries to bring the two as close to equality as possible. If the batch posters' costs have been less than fee receipts, the price will increase, and if batch poster costs have exceeded fee receipts, the price will decrease. - -#### Total fee and gas estimation - -The total fee charged to a transaction is the L2 basefee, multiplied by the sum of the L2 gas used plus the L1 calldata charge. As on Ethereum, a transaction will fail if it fails to supply enough gas, or if it specifies a basefee limit that is below the current basefee. Ethereum also allows a "tip" but Nitro ignores this field and never collects any tips. - -## Inside AnyTrust - -AnyTrust is a variant of Arbitrum Nitro technology that lowers costs by accepting a mild trust assumption. - -The Arbitrum protocol requires that all Arbitrum nodes, including validators (nodes that verify correctness of the chain and are prepared to bond on correct results), have access to the data of every L2 transaction in the Arbitrum chain's inbox. An Arbitrum rollup provides data access by posting the data (in batched, compressed form) on L1 Ethereum as calldata. The Ethereum gas to pay for this is the largest component of cost in Arbitrum. - -AnyTrust relies instead on an external Data Availability Committee (hereafter, "the Committee") to store data and provide it on demand. The Committee has N members, of which AnyTrust assumes at least two are honest. This means that if N - 1 Committee members promise to provide access to some data, at least one of the promising parties must be honest. Since there are two honest members, and only one failed to make the promise, it follows that at least one of the promisers must be honest — and that honest member will provide data when it is needed to ensure the chain can properly function. - -### Keysets - -A Keyset specifies the public keys of Committee members and the number of signatures required for a Data Availability Certificate to be valid. Keysets make Committee membership changes possible and provide Committee members the ability to change their keys. - -A Keyset contains - -- the number of Committee members, and -- for each Committee member, a BLS public key, and -- the number of Committee signatures required. - -Keysets are identified by their hashes. - -An L1 KeysetManager contract maintains a list of currently valid Keysets. The L2 chain's Owner can add or remove Keysets from this list. When a Keyset becomes valid, the KeysetManager contract emits an L1 Ethereum event containing the Keyset's hash and full contents. This allows the contents to be recovered later by anyone, given only the Keyset hash. - -Although the API does not limit the number of Keysets that can be valid at the same time, normally only one Keyset will be valid. - -### Data Availability Certificates - -A central concept in AnyTrust is the Data Availability Certificate (hereafter, a "DACert"). A DACert contains: - -- the hash of a data block, and -- an expiration time, and -- proof that N-1 Committee members have signed the (hash, expiration time) pair, consisting of - - the hash of the Keyset used in signing, and - - a bitmap saying which Committee members signed, and - - a BLS aggregated signature (over the BLS12-381 curve) proving that those parties signed. - -Because of the 2-of-N trust assumption, a DACert constitutes proof that the block's data (i.e., the preimage of the hash in the DACert) will be available from at least one honest Committee member, at least until the expiration time. - -In ordinary (non-AnyTrust) Nitro, the Arbitrum sequencer posts data blocks on the L1 chain as calldata. The hashes of the data blocks are committed by the L1 Inbox contract, allowing the data to be reliably read by L2 code. - -AnyTrust gives the sequencer two ways to post a data block on L1: it can post the full data as above, or it can post a DACert proving availability of the data. The L1 inbox contract will reject any DACert that uses an invalid Keyset; the other aspects of DACert validity are checked by L2 code. - -The L2 code that reads data from the inbox reads a full-data block as in ordinary Nitro. If it sees a DACert instead, it checks the validity of the DACert, with reference to the Keyset specified by the DACert (which is known to be valid because the L1 Inbox verified that). The L2 code verifies that - -- the number of signers is at least the number required by the Keyset, and -- the aggregated signature is valid for the claimed signers, and -- the expiration time is at least two weeks after the current L2 timestamp. - -If the DACert is invalid, the L2 code discards the DACert and moves on to the next data block. If the DACert is valid, the L2 code reads the data block, which is guaranteed to be available because the DACert is valid. - -### Data Availability Servers - -Committee members run Data Availability Server (DAS) software. The DAS exposes two APIs: - -- The Sequencer API, which is meant to be called only by the Arbitrum chain's Sequencer, is a JSON-RPC interface allowing the Sequencer to submit data blocks to the DAS for storage. Deployments will typically block access to this API from callers other than the Sequencer. -- The REST API, which is meant to be available to the world, is a RESTful HTTP(S) based protocol that allows data blocks to be fetched by hash. This API is fully cacheable, and deployments may use a caching proxy or CDN to increase scale and protect against DoS attacks. - -Only Committee members have reason to support the Sequencer API. We expect others to run the REST API, and that is helpful. (More on that below.) - -The DAS software, based on configuration options, can store its data in local files, or in a Badger database, or on Amazon S3, or redundantly across multiple backing stores. The software also supports optional caching in memory (using Bigcache) or in a Redis instance. - -### Sequencer-Committee Interaction - -When the Arbitrum sequencer produces a data batch that it wants to post using the Committee, it sends the batch's data, along with an expiration time (normally three weeks in the future) via RPC to all Committee members in parallel. Each Committee member stores the data in its backing store, indexed by the data's hash. Then the member signs the (hash, expiration time) pair using its BLS key, and returns the signature with a success indicator to the sequencer. - -Once the Sequencer has collected enough signatures, it can aggregate the signatures and create a valid DACert for the (hash, expiration time) pair. The Sequencer then posts that DACert to the L1 inbox contract, making it available to the AnyTrust chain software at L2. - -If the Sequencer fails to collect enough signatures within a few minutes, it will abandon the attempt to use the Committee, and will "fall back to rollup" by posting the full data directly to the L1 chain, as it would do in a non-AnyTrust chain. The L2 software can understand both data posting formats (via DACert or via full data) and will handle each one correctly.