diff --git a/README.md b/README.md index f7ee6ce2ce..ba2fbf2687 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Nitro is the latest iteration of the Arbitrum technology. It is a fully integrat layer 2 optimistic rollup system, including fraud proofs, the sequencer, the token bridges, advanced calldata compression, and more. -See the live docs-site [here](https://developer.arbitrum.io/) (or [here](./docs) for markdown docs source.) +See the live docs-site [here](https://developer.arbitrum.io/) (or [here](https://github.com/OffchainLabs/arbitrum-docs) for markdown docs source.) The Nitro stack is built on several innovations. At its core is a new prover, which can do Arbitrum’s classic interactive fraud proofs over WASM code. That means the L2 Arbitrum engine can be written and compiled using diff --git a/docs/arbitrum-ethereum-differences.md b/docs/arbitrum-ethereum-differences.md deleted file mode 100644 index 2cccc95c47..0000000000 --- a/docs/arbitrum-ethereum-differences.md +++ /dev/null @@ -1,46 +0,0 @@ -# Arbitrum/Ethereum Differences - -Arbitrum is designed to be as compatible and consistent with Ethereum as possible, from its high-level RPCs to its low-level bytecode and everything in between. Dapp developers with experience building on Ethereum will likely find that little-to-no new L2-specific knowledge is required to build on Arbitrum. - -This document presents an overview of some of the minor differences, perks, and gotchas that devs are advised to be aware of. - -## Transactions / Blocks - -##### Blocks and Time - -Time in L2 is tricky; the timing assumptions one is used to making about L1 blocks don't exactly carry over into the timing of Arbitrum blocks. For details, see [Block Numbers and Time](./time.md). - -##### Block hashes and randomness - -Arbitrum's L2 block hashes should not be relied on as a secure source of randomness (see ['blockhash(x);](./solidity-support.md)) - -##### L1 Fees - -The L2 fees an Arbitrum transaction pays essentially work identically to gas fees on Ethereum. Arbitrum transactions must also, however, pay an L1-fee component to cover the cost of their calldata. (See [L1 pricing](./arbos/l1-pricing.md).) - -##### Tx Receipts - -Arbitrum transaction receipts include two additional fields: - -1. `l1BlockNumber`: The l1 block number that would be used [for block.number calls](time). -1. `gasUsedForL1`: Amount of gas spent on l1 computation in units of l2 gas. - -## L1 to L2 Messages - -Arbitrum chains support arbitrary L1 to L2 message passing; developers using this functionality should familiarize themselves with how they work (see [L1 to L2 Messaging](./arbos/l1-to-l2-messaging.md)). Of particular note: - -- The result of a successful initial/"auto"-execution of an L1 to L2 message will be an unsigned L2 tx receipt. -- The `msg.sender` of the L2 side of an L1 to L2 message will be not the initiating L1 address, but rather its address alias. -- Using the special `ethDeposit` method will _not_ result in an L2 contract's fallback function getting triggered. - -Etc. - -## Precompiles - -Arbitrum chains include a number of special precompiles not present on Ethereum; see [Common Precompiles](./arbos/common-precompiles.md) / [All Precompiles](./arbos/precompiles.md). - -Of particular note is the [ArbAddressTable](./arbos/precompiles.md#ArbAddressTable), which allows contracts to map addresses to integers, saving calldata / fees for addresses expected to be reused as parameters; see [Arb Address Table tutorial](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/address-table) for example usage. - -## Solidity - -You can deploy Solidity contracts onto Arbitrum just like you do Ethereum; there are only a few minor differences in behavior. See [Solidity Support](./solidity-support.md) for details. diff --git a/docs/arbos/arbos.md b/docs/arbos/arbos.md deleted file mode 100644 index e6494dce51..0000000000 --- a/docs/arbos/arbos.md +++ /dev/null @@ -1,105 +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. This section documents the infrastructure that makes this possible. For more details on specific calls, please refer to the [methods documentation](./precompiles.md). - -A precompile consists of a of 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 is 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 tx 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/tree/master/contracts/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 txes, 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](./l1-to-l2-messaging.md). - -## 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](precompiles.md). 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 tx 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`](precompiles.md#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`](precompiles.md#ArbGasInfo) and [`ArbOwner`](precompiles.md#ArbOwner) precompiles, most changes are automatic and happen during [block production][block_production_link] and [the transaction hooks](geth.md#Hooks). Each of an incoming message's txes 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.md#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 tx to succeed rather than requiring a resubmission. Because the first tx 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 txes are only present in cases where the system is under heavy load and the result is that the user's tx is dropped without charges since the state transition fails early. Those trusting the sequencer can rely on the tx 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 tx 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 txes 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 txes 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/docs/arbos/common-precompiles.md b/docs/arbos/common-precompiles.md deleted file mode 100644 index 131aad40ec..0000000000 --- a/docs/arbos/common-precompiles.md +++ /dev/null @@ -1,102 +0,0 @@ -# Overview -ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. This reference details those we expect users to most frequently use. For an exhaustive reference including those we don't expect most users to ever call, please refer to the [Full Precompiles documentation](precompiles.md). - -From the perspective of user applications, precompiles live as contracts at the following addresses. Click on any to jump to their section. - -| Precompile | Address   | Purpose | -| :----------------------------------------- | :------------- | :---------------------------------- | -| [`ArbAggregator`](#ArbAggregator) | `0x6d` | Configuring transaction aggregation | -| [`ArbGasInfo`](#ArbGasInfo) | `0x6c` | Info about gas pricing | -| [`ArbRetryableTx`](#ArbRetryableTx)   | `0x6e` | Managing retryables | -| [`ArbSys`](#ArbSys) | `0x64` | System-level functionality | - -[ArbAggregator_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbAddressTable.go -[ArbGasInfo_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbGasInfo.go -[ArbRetryableTx_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbRetryableTx.go -[ArbSys_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbSys.go - -# [ArbAggregator][ArbAggregator_link] -Provides aggregators and their users methods for configuring how they participate in L1 aggregation. Arbitrum One's default aggregator is the Sequencer, which a user will prefer unless `SetPreferredAggregator` is invoked to change it. - -| Methods | | -| :------------------------------------------------------------- | :------------------------------------------------------ | -| [![](e.png)][As0] [`GetPreferredAggregator`][A0]`(account)` | Gets an account's preferred aggregator | -| [![](e.png)][As1] [`SetPreferredAggregator`][A1]`(aggregator)` | Sets the caller's preferred aggregator to that provided | -| [![](e.png)][As2] [`GetDefaultAggregator`][A2]`()` | Gets the chain's default aggregator | - -[A0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbAggregator.go#L22 -[A1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbAggregator.go#L39 -[A2]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbAggregator.go#L48 - -[As0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbAggregator.sol#L28 -[As1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbAggregator.sol#L32 -[As2]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbAggregator.sol#L35 - - -# [ArbGasInfo][ArbGasInfo_link] -Provides insight into the cost of using the chain. These methods have been adjusted to account for Nitro's heavy use of calldata compression. Of note to end-users, we no longer make a distinction between non-zero and zero-valued calldata bytes. - -| Methods | | -| :----------------------------------------------------- | :---------------------------------------------------------------- | -| [![](e.png)][GIs1] [`GetPricesInWei`][GI1]`()` | Get prices in wei when using the caller's preferred aggregator | -| [![](e.png)][GIs3] [`GetPricesInArbGas`][GI3]`()` | Get prices in ArbGas when using the caller's preferred aggregator | -| [![](e.png)][GIs4] [`GetGasAccountingParams`][GI4]`()` | Get the chain speed limit, pool size, and tx gas limit | -| [![](e.png)][GIs11] [`GetL1BaseFeeEstimate`][GI11]`()` | Get ArbOS's estimate of the L1 basefee in wei | - -[GI1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L63 -[GI3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L99 -[GI4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L111 -[GI11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L150 - -[GIs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L58 -[GIs3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L83 -[GIs4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L94 -[GIs11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L122 - -# [ArbRetryableTx][ArbRetryableTx_link] -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](arbos.md#Retryables). - - -| Methods | | Nitro changes | -| :---------------------------------------------------------- | :--------------------------------------------------------------------------------- | :--------------------- | -| [![](e.png)][RTs0] [`Cancel`][RT0]`(ticket)` | Cancel the ticket and refund its callvalue to its beneficiary | | -| [![](e.png)][RTs1] [`GetBeneficiary`][RT1]`(ticket)`   | Gets the beneficiary of the ticket | | -| [![](e.png)][RTs3] [`GetTimeout`][RT3]`(ticket)` | Gets the timestamp for when ticket will expire | | -| [![](e.png)][RTs4] [`Keepalive`][RT4]`(ticket)` | Adds one lifetime period to the ticket's expiry | Doesn't add callvalue | -| [![](e.png)][RTs5] [`Redeem`][RT5]`(ticket)` | Schedule an attempt to redeem the retryable, donating all of the call's gas   | Happens in a future tx | - -[RT0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbRetryableTx.go#L184 -[RT1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbRetryableTx.go#L171 -[RT3]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbRetryableTx.go#L115 -[RT4]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbRetryableTx.go#L132 -[RT5]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbRetryableTx.go#L36 - -[RTs0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbRetryableTx.sol#L70 -[RTs1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbRetryableTx.sol#L63 -[RTs3]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbRetryableTx.sol#L45 -[RTs4]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbRetryableTx.sol#L55 -[RTs5]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbRetryableTx.sol#L32 - - -# [ArbSys][ArbSys_link] -Provides system-level functionality for interacting with L1 and understanding the call stack. - -| Methods | | -| :----------------------------------------------------------------- | :---------------------------------------------------------- | -| [![](e.png)][Ss0] [`ArbBlockNumber`][S0]`()` | Gets the current L2 block number | -| [![](e.png)][Ss1] [`ArbBlockHash`][S1]`()` | Gets the L2 block hash, if the block is sufficiently recent | -| [![](e.png)][Ss5] [`IsTopLevelCall`][S5]`()` | Checks if the call is top-level | -| [![](e.png)][Ss9] [`SendTxToL1`][S9]`(destination, calldataForL1)` | Sends a transaction to L1, adding it to the outbox | -| [![](e.png)][Ss11] [`WithdrawEth`][S11]`(destination)` | Send paid eth to the destination on L1 | - -[S0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbSys.go#L30 -[S1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbSys.go#L35 -[S5]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbSys.go#L66 -[S9]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbSys.go#L98 -[S11]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/precompiles/ArbSys.go#L187 - -[Ss0]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbSys.sol#L31 -[Ss1]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbSys.sol#L37 -[Ss5]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbSys.sol#L61 -[Ss9]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbSys.sol#L100 -[Ss11]: https://github.com/OffchainLabs/nitro/blob/704e82bb38ae3ccd70c35e31934c7b45f6c25561/solgen/src/precompiles/ArbSys.sol#L92 diff --git a/docs/arbos/e.png b/docs/arbos/e.png deleted file mode 100644 index c109cb37b7..0000000000 Binary files a/docs/arbos/e.png and /dev/null differ diff --git a/docs/arbos/gas.md b/docs/arbos/gas.md deleted file mode 100644 index b62bf96cac..0000000000 --- a/docs/arbos/gas.md +++ /dev/null @@ -1,44 +0,0 @@ -# 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 bare to service the tx - -The L1 component is the product of the tx's estimated contribution to its batch's size — computed using Brotli on the tx 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](arbos.md#l1pricingstate). - -[The L2 component](arbos.md#l2pricingstate) 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](precompiles.md), 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.getMinimumGasPrice`](precompiles.md) (currently 0.1 gwei on both Arbitrum One and 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) for more.) - - -[drop_l1_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l1pricing/l1pricing.go#L232 - -## 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 tx's execution [will be included][estimation_inclusion_link] when estimating gas via the node's RPC. A tx's gas estimate, then, can only be found if all the txes succeed at a given gas limit. This is especially important when working with retryables and scheduling redeem attempts. - -Because a call to [`redeem`](precompiles.md#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 [`NodeInterface.sol`][node_interface_link] and similarly requires the auto-redeem attempt succeed. - -[estimation_inclusion_link]: https://github.com/OffchainLabs/go-ethereum/blob/edf6a19157606070b6a6660c8decc513e2408cb7/internal/ethapi/api.go#L955 -[node_interface_link]: https://github.com/OffchainLabs/nitro/blob/master/solgen/src/node-interface/NodeInterface.sol - -## NodeInterface.sol -To avoid creating new RPC methods for client-side tooling, nitro geth's [`InterceptRPCMessage`][InterceptRPCMessage_link] hook provides an opportunity to swap out the message its handling before deriving a transaction from it. The node [uses this hook][use_hook_link] to detect messages sent to the address `0xc8`, the location of the fictional `NodeInterface` contract specified in [`NodeInterface.sol`][node_interface_link]. - -`NodeInterface` isn't deployed on L2 and only exists in the RPC, but it contains methods callable via `0xc8`. Doing so requires setting the `To` field to `0xc8` and supplying calldata for the method. Below is the list of methods. - -| Method | Info | -|:-----------------------------------------------------------------|:----------------------------------------------------| -| [`estimateRetryableTicket`][estimateRetryableTicket_link]   | Estimates the gas needed for a retryable submission | - -[InterceptRPCMessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/f31341b3dfa987719b012bc976a6f4fe3b8a1221/internal/ethapi/api.go#L929 -[use_hook_link]: https://github.com/OffchainLabs/nitro/blob/57e03322926f796f75a21f8735cc64ea0a2d11c3/arbstate/node-interface.go#L17 -[estimateRetryableTicket_link]: https://github.com/OffchainLabs/nitro/blob/8ab1d6730164e18d0ca1bd5635ca12aadf36a640/solgen/src/node_interface/NodeInterface.sol#L21 diff --git a/docs/arbos/geth.md b/docs/arbos/geth.md deleted file mode 100644 index df38c6a57a..0000000000 --- a/docs/arbos/geth.md +++ /dev/null @@ -1,254 +0,0 @@ -# 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 - -## 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 tx 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] -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] -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] - -This fallible hook ensures the user has enough funds to pay their poster's L1 calldata costs. If not, the tx 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 tx'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 txes. - -[difference_set_aside_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/tx_processor.go#L272 -[refunded_later_link]: https://github.com/OffchainLabs/go-ethereum/blob/f31341b3dfa987719b012bc976a6f4fe3b8a1221/core/state_transition.go#L389 -[that_seen_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/block_processor.go#L146 -[max_perblock_limit_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/l2pricing/pools.go#L100 - -### [`PushCaller`][PushCaller_link] -These hooks track the callers within the EVM callstack, pushing and popping as calls are made and complete. This provides [`ArbSys`](precompiles.md#ArbSys) with info about the callstack, which it uses to implement the methods [`WasMyCallersAddressAliased`](precompiles.md#ArbSys) and [`MyCallersAddressWithoutAliasing`](precompiles.md#ArbSys). - -### [`L1BlockHash`][L1BlockHash_link] -In arbitrum, the BlockHash and Number operations return data that relies on underlying L1 blocks intead of L2 blocks, to accomendate 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] - -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] - -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] -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/8eac46ef5e0298e6cc171f5a46b5c1fe4923bf48/core/state_processor.go#L144 -[EVM_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/vm/evm.go#L101 -[DefaultTxProcessor_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/vm/evm_arbitrum.go#L39 -[TxProcessor_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L33 -[StartTxHook_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L77 -[ReadyEVMForL2_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbstate/geth-hook.go#L38 -[GasChargingHook_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L205 -[PushCaller_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L60 -[PopCaller_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L64 -[ForceRefundGas_link]: https://github.com/OffchainLabs/nitro/blob/2ba6d1aa45abcc46c28f3d4f560691ce5a396af8/arbos/tx_processor.go#L291 -[NonrefundableGas_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L248 -[EndTxHook_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L255 -[L1BlockHash_link]: https://github.com/OffchainLabs/nitro/blob/df5344a48f4a24173b9a3794318079a869aae58b/arbos/tx_processor.go#L407 -[L1BlockNumber_link]: https://github.com/OffchainLabs/nitro/blob/df5344a48f4a24173b9a3794318079a869aae58b/arbos/tx_processor.go#L399 - -## Interfaces and components - -### [`APIBackend`][APIBackend_link] -APIBackend implements the [`ethapi.Bakend`][ethapi.Bakend_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/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/apibackend.go#L27 -[ethapi.Bakend_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/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/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/backend.go#L15 -[Ethereum_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/eth/backend.go#L65 - -### [`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/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/arbos_interface.go#L10 -[Blockchain_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/arbos_interface.go#L12 -[PublishTransactions_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/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/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L21 -[RecordingChainContext_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L101 -[PrepareRecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L133 -[PreimagesFromRecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L148 - -## 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] -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] -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] -Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. - -### [`ArbitrumSubmitRetryableTx`][ArbitrumSubmitRetryableTx_link] -Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](arbos.md#Retryables) for more info. - -### [`ArbitrumRetryTx`][ArbitrumRetryTx_link] -These are scheduled by calls to the [`redeem`](precompiles.md#ArbRetryableTx) precompile method and via retryable auto-redemption. Please see the [retryables documentation](arbos.md#Retryables) for more info. - -### [`ArbitrumInternalTx`][ArbitrumInternalTx_link] -Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a tx of this type to update its state in-between user-generated transactions. Such a tx 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 tx types. - -#### [`ArbInternalTxUpdateL1BlockNumber`][ArbInternalTxUpdateL1BlockNumber_link] -Updates the L1 block number. This tx [is generated][block_generated_link] whenever a message originates from an L1 block newer than any ArbOS has seen thus far. They are [guaranteed to be the first][block_first_link] in their L2 block. - -[ArbitrumUnsignedTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L15 -[ArbitrumContractTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L76 -[ArbitrumSubmitRetryableTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L194 -[ArbitrumRetryTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L133 -[ArbitrumDepositTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L265 -[ArbitrumInternalTx_link]: https://github.com/OffchainLabs/nitro/blob/master/arbos/internal_tx.go - -[InternalType_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L313 -[ArbInternalTxUpdateL1BlockNumber_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/internal_tx.go#L24 -[block_generated_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L150 -[block_first_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L154 - -## 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 tx 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.sol`](gas.md#NodeInterface.sol) or when scheduled | -| [`MessageEthcallMode`][MC2] | eth_calls | never | - -[MC0]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L648 -[MC1]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L649 -[MC2]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L650 - -[geth_message_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L628 -[TxRunMode_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L695 -[set_run_mode_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/internal/ethapi/api.go#L911 -[AsMessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L670 -[underlying_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L694 - -## 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](arbos.md), 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/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/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 tx may revert should another arriving just before even slightly change the tx'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](arbos.md#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`](../../go-ethereum/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, and also wraps geth's standard precompiles. For more information on Arbitrum precompiles, see [ArbOS doc](arbos.md#precompiles). - -### WASM build support -The WASM arbitrum executable does not support file oprations. We created [`fileutil.go`](../../go-ethereum/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](../../go-ethereum/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`](../../go-ethereum/core/types/arbitrum_signer.go), and multiple new [`transaction types`](../../go-ethereum/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`](../../go-ethereum/core/blockchain_arbitrum.go) 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 gensis.Commit and use it to commit non-zero genesis blocks. - -[pad_estimates_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/accounts/abi/bind/base.go#L352 -[conservation_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L42 -[alert_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L290 -[proof_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/system_tests/outbox_test.go#L26 -[merkle_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/merkleAccumulator/merkleAccumulator.go#L14 -[UnderlyingTransaction_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state_transition.go#L68 -[WriteHeadBlock_link]: https://github.com/OffchainLabs/go-ethereum/blob/bf2301d747acb2071fdb64dc82fe7fc122581f0c/core/genesis.go#L332 diff --git a/docs/arbos/l1-pricing.md b/docs/arbos/l1-pricing.md deleted file mode 100644 index 0d12d9180b..0000000000 --- a/docs/arbos/l1-pricing.md +++ /dev/null @@ -1,55 +0,0 @@ -# L1 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 total fee charged to a transaction is the product of the transaction's estimated size, and the current L1 Gas Basefee. - -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 - -## 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 keep 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 is the surplus is increasing, and increase the Basefee is the surplus is shrinking. - -## Getting L1 Fee Info - -The L1 gas basefee can be queried via [`ArbGasInfo.getL1BaseFeeEstimate`](precompiles.md). To estimate the L1 fee a transaction will use, the [`NodeInterface.gasEstimateComponents`](gas.md) or [`NodeInterface.gasEstimateL1Component`](gas.md) 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/docs/arbos/l1-to-l2-messaging.md b/docs/arbos/l1-to-l2-messaging.md deleted file mode 100644 index 6bd9b72f79..0000000000 --- a/docs/arbos/l1-to-l2-messaging.md +++ /dev/null @@ -1,83 +0,0 @@ -# L1 To L2 Messaging - -## Retryables - -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 in L2 has a strong guaranteed to ultimately succeed as well. - -A retryable is submitted via the `Inbox`'s `createRetryableTicket` method. In the common case, a Retryable's submission is followed by an attempt to execute the transaction (i.e., an _"auto-redeem"_). If the attempt fails or isn't scheduled after the Retryable is submitted, anyone can try to _redeem_ it, by calling the [`redeem`](./precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](./precompiles.md#ArbRetryableTx) precompile. The party requesting the redeem provides the gas that will be used to execute the Retryable. If execution of the Retryable succeeds, the Retryable is deleted. If execution fails, the Retryable continues to exist and further attempts can be made to redeem it. If a fixed period (currently one week) elapses without a successful redeem, the Retryable expires and will be [automatically discarded][discard_link], unless some party has paid a fee to [_renew_][renew_link] the Retryable for another full period. A Retryable can live indefinitely as long as it is renewed each time before it expires. - -[discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 -[renew_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L207 - -### Submitting a Retryable - -A transaction to submit a Retryable does the following: - -- create a new Retryable with the caller, destination, callvalue, calldata, and beneficiary of the submit transaction -- deduct funds to cover the callvalue from the caller (as usual) and place them into escrow for later use in redeeming the Retryable -- assign a unique TicketID to the Retryable -- cause the ArbRetryableTx precompiled contract to emit a TicketCreated event containing the TicketID -- if the submit transaction contains gas, schedule a redeem of the new Retryable, using the supplied gas, as if the [`redeem`](./precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](./precompiles.md#ArbRetryableTx) precompile had been called. - -In most use cases, the submitter will provide gas and will intend for the immediate redeem to succeed, with later retries available only as a backup mechanism should the immediate redeem fail. (It might fail, for example, because the L2 gas price has increased unexpectedly.) In this way, an L1 contract can submit a transaction to L2 in such a way that the transaction will normally run immediately at L2 but allowing any party to retry the transaction should it fail. - -When a Retryable is redeemed, it will execute with the sender, destination, callvalue, and calldata of the original submission. The callvalue will have been escrowed during the initial submission of the Retryable, for this purpose. If a Retryable with callvalue is eventually discarded, having never successfully run, the escrowed callvalue will be paid out to a "beneficiary" account that is specified in the initial submission. - -A Retryable's beneficiary has the unique power to [`cancel`](./precompiles.md#ArbRetryableTx) the Retryable. This has the same effect as the Retryable timing out. - -[moved_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/tx_processor.go#L191 - -### Redeeming a Retryable - -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`](precompiles.md#ArbRetryableTx)'s [`redeem`](./precompiles.md#ArbRetryableTx) 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 tx that scheduled it. Note that the redeem attempt's gas comes from the call to [`redeem`](./precompiles.md#ArbRetryableTx), so there's no chance the block's gas limit is reached before execution. - -On success, the `To` address keeps 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 next redeemer. In either case, the network fee was paid during the scheduling tx, so no fees are charged and no refunds are made. - -During redemption of a retryable, attempts to cancel the same retryable, or to schedule another redeem of the same retryable, will revert. In this manner retryables are not self-modifying. - -[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 - -### Receipts - -If the lifecycle of a retryable ticket, two types of L2 transaction receipts will be emitted: - -**Ticket Creation Receipt**: This receipts indicates that a retryable 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`](./precompiles.md#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 retryable ticket. It includes a `RedeemScheduled` event from [`ArbRetryableTx`](./precompiles.md#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 / execution. It also will convert the provided `beneficiary` or `credit-back-address` to its address alias (see below) if either is a contract, providing a path for the L1 contract to recover funds. A power-user may by-pass 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](../sequencer.md)). 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 - -All messages submitted via the Delayed Inbox get their sender's addressed "aliased": when these unsigned 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`: - -``` -L2_Alias = L1_Contract_ Address + 0x1111000000000000000000000000000000001111 -``` - -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"); - _; - } -``` diff --git a/docs/arbos/l2-to-l1-messaging.md b/docs/arbos/l2-to-l1-messaging.md deleted file mode 100644 index 46176675a8..0000000000 --- a/docs/arbos/l2-to-l1-messaging.md +++ /dev/null @@ -1,24 +0,0 @@ -# L2-to-L1-Messages and the Outbox - -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](./l1-to-l2-messaging.md) (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`](./precompiles.md#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 preform 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](../inside-arbitrum-nitro/inside-arbitrum-nitro.md) - -\** 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/docs/arbos/precompiles.md b/docs/arbos/precompiles.md deleted file mode 100644 index d5200de98e..0000000000 --- a/docs/arbos/precompiles.md +++ /dev/null @@ -1,491 +0,0 @@ -# Overview -ArbOS provides L2-specific precompiles with methods smart contracts can call the same way they can solidity functions. This reference exhaustively documents the specific calls ArbOS makes available. For more details on the infrastructure that makes this possible, please refer to the [ArbOS documentation](arbos.md). For an abbreviated reference on the precompiles we expect users to most often use, please see the [common precompiles documentation](common-precompiles.md). - -From the perspective of user applications, precompiles live as contracts at the following addresses. Click on any to jump to their section. - -| Precompile | Address   | Purpose | -|:-----------------------------------------------|:---------------|:---------------------------------------------------| -| [`ArbAddressTable`](#ArbAddressTable) | `0x66` | Supporting compression of addresses | -| [`ArbAggregator`](#ArbAggregator) | `0x6d` | Configuring transaction aggregation | -| [`ArbBLS`](#ArbBLS) | `0x67` | Managing BLS keys | -| [`ArbDebug`](#ArbDebug) | `0xff` | Testing tools | -| [`ArbFunctionTable`](#ArbFunctionTable)   | `0x68` | No longer used | -| [`ArbGasInfo`](#ArbGasInfo) | `0x6c` | Info about gas pricing | -| [`ArbInfo`](#ArbInfo) | `0x65` | Info about accounts | -| [`ArbOwner`](#ArbOwner) | `0x70` | Chain administration, callable only by chain owner | -| [`ArbOwnerPublic`](#ArbOwnerPublic) | `0x6b` | Info about chain owners | -| [`ArbosTest`](#ArbosTest) | `0x69` | No longer used | -| [`ArbRetryableTx`](#ArbRetryableTx) | `0x6e` | Managing retryables | -| [`ArbStatistics`](#ArbStatistics) | `0x6f` | Info about the pre-Nitro state | -| [`ArbSys`](#ArbSys) | `0x64` | System-level functionality | - -[ArbAddressTable_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbAddressTable.go -[ArbAggregator_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbAggregator.go -[ArbBLS_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbBLS.go -[ArbDebug_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbDebug.go -[ArbFunctionTable_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbFunctionTable.go -[ArbInfo_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbInfo.go -[ArbGasInfo_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbGasInfo.go -[ArbosTest_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbosTest.go -[ArbOwner_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbOwner.go -[ArbOwnerPublic_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbOwnerPublic.go -[ArbRetryableTx_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbRetryableTx.go -[ArbStatistics_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbStatistics.go -[ArbSys_link]: https://github.com/OffchainLabs/nitro/blob/master/precompiles/ArbSys.go - -# ArbAddressTable -[ArbAddressTable][ArbAddressTable_link] provides the ability to create short-hands for commonly used accounts. - -| Methods | | -|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------| -| [![](e.png)][ATs0] [`AddressExists`][AT0]`(address)` | Checks if an address exists in the table | -| [![](e.png)][ATs1] [`Compress`][AT1]`(address)` | Gets bytes that represent the address | -| [![](e.png)][ATs2] [`Decompress`][AT2]`(buffer, offset)`   | Replaces the compressed bytes at the given offset with those of the corresponding account | -| [![](e.png)][ATs3] [`Lookup`][AT3]`(address)` | Gets the index of an address in the table | -| [![](e.png)][ATs4] [`LookupIndex`][AT4]`(index)` | Gets the address at an index in the table | -| [![](e.png)][ATs5] [`Register`][AT5]`(address)` | Adds an address to the table, shrinking its compressed representation | -| [![](e.png)][ATs6] [`Size`][AT6]`()` | Gets the number of addresses in the table | - -[AT0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L18 -[AT1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L23 -[AT2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L28 -[AT3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L41 -[AT4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L53 -[AT5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L68 -[AT6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbAddressTable.go#L74 - -[ATs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L31 -[ATs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L38 -[ATs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L46 -[ATs3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L55 -[ATs4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L61 -[ATs5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L68 -[ATs6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbAddressTable.sol#L73 - - -# ArbAggregator -[ArbAggregator][ArbAggregator_link] provides aggregators and their users methods for configuring how they participate in L1 aggregation. Arbitrum One's default aggregator is the Sequencer, which a user will prefer unless `SetPreferredAggregator` is invoked to change it. - -Compression ratios are measured in basis points. Methods that are checkmarked are access-controlled and will revert if not called by the aggregator, its fee collector, or a chain owner. - -| Methods | | | -|:--------------------------------------------------------------------------|:--------------------------------------------------------|:---| -| [![](e.png)][As0] [`GetPreferredAggregator`][A0]`(account)` | Gets an account's preferred aggregator | | -| [![](e.png)][As1] [`SetPreferredAggregator`][A1]`(aggregator)` | Sets the caller's preferred aggregator to that provided | | -| [![](e.png)][As2] [`GetDefaultAggregator`][A2]`()` | Gets the chain's default aggregator | | -| [![](e.png)][As3] [`SetDefaultAggregator`][A3]`(default)` | Sets the chain's default aggregator | ✔️ | -| [![](e.png)][As4] [`GetCompressionRatio`][A4]`(aggregator)` | Gets the aggregator's compression ratio | | -| [![](e.png)][As5] [`SetCompressionRatio`][A5]`(aggregator, ratio)` | Set the aggregator's compression ratio | ✔️ | -| [![](e.png)][As6] [`GetFeeCollector`][A6]`(aggregator)` | Gets an aggregator's fee collector | | -| [![](e.png)][As7] [`SetFeeCollector`][A7]`(aggregator, collector)`   | Sets an aggregator's fee collector | ✔️ | - -[A0]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L25 -[A1]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L42 -[A2]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L51 -[A3]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L56 -[A4]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L73 -[A5]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L79 -[A6]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L91 -[A7]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L96 - -[As0]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L28 -[As1]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L32 -[As2]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L35 -[As3]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L40 -[As4]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L45 -[As5]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L51 -[As6]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L56 -[As7]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L62 - -| Deprecated Methods | | -|:------------------------------------------------------------|:-------------| -| [![](e.png)][Ads0] [`GetTxBaseFee`][Ad0]`(aggregator)` | Returns 0 | -| [![](e.png)][Ads1] [`SetTxBaseFee`][Ad1]`(aggregator, fee)` | Does nothing | - -[Ad0]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L108 -[Ad1]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/precompiles/ArbAggregator.go#L114 - -[Ads0]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L67 -[Ads1]: https://github.com/OffchainLabs/nitro/blob/ba3a86afb2e7057bdc3cce54b28be4c1c0579180/solgen/src/precompiles/ArbAggregator.sol#L75 - -# ArbBLS -[ArbBLS][ArbBLS_link] provides a registry of BLS public keys for accounts. - -| Methods | | -|:--------------------------------------------------------------------|:------------------------------------------------------------| -| [![](e.png)][Bs0] [`RegisterAltBN128`][B0]`(x0, x1, y0, y1)`   | Associate an AltBN128 public key with the caller's address | -| [![](e.png)][Bs1] [`GetAltBN128`][B1]`(account)` | Gets the AltBN128 public key associated with an address | -| [![](e.png)][Bs2] [`RegisterBLS12381`][B2]`(key)` | Associate a BLS 12-381 public key with the caller's address | -| [![](e.png)][Bs3] [`GetBLS12381`][B3]`(account)` | Gets the BLS 12-381 public key associated with an address | - -[B0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L27 -[B1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L32 -[B2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L37 -[B3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L46 - -[Bs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L44 -[Bs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L52 -[Bs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L63 -[Bs3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L66 - -| Deprecated Methods | | -|:--------------------------------------------------------------|:-------------------------------| -| [![](e.png)][Bds0] [`Register`][Bd0]`(x0, x1, y0, y1)`   | equivalent to registerAltBN128 | -| [![](e.png)][Bds1] [`GetPublicKey`][Bd1]`(account)` | equivalent to getAltBN128 | - -[Bd0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L17 -[Bd1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbBLS.go#L22 - -[Bds0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L25 -[Bds1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbBLS.sol#L33 - - -# ArbDebug -[ArbDebug][ArbDebug_link] provides mechanisms useful for testing. The methods of `ArbDebug` are only available for chains with the `AllowDebugPrecompiles` chain parameter set. Otherwise, calls to this precompile will revert. - -| Methods | | -|:-------------------------------------------------------|:---------------------------------------------------| -| [![](e.png)][Ds0] [`BecomeChainOwner`][D0]`()` | Caller becomes a chain owner | -| [![](e.png)][Ds1] [`Events`][D1]`(flag, value)`   | Emit events with values based on the args provided | - -[D0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbDebug.go#L38 -[D1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbDebug.go#L19 - -[Ds0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbDebug.sol#L27 -[Ds1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbDebug.sol#L30 - - -| Events | | -|:-----------------------------------------|:-------------------------------------------| -| [![](e.png)][Des0] [`Basic`][De0]   | Emitted in `Events` for testing | -| [![](e.png)][Des1] [`Mixed`][De1] | Emitted in `Events` for testing | -| [![](e.png)][Des2] [`Store`][De2] | Never emitted (used for testing log sizes) | - -[De0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbDebug.go#L24 -[De1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbDebug.go#L29 -[De2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbDebug.go#L13 - -[Des0]:https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbDebug.sol#L33 -[Des1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbDebug.sol#L34 -[Des2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbDebug.sol#L41 - - -# ArbFunctionTable -[ArbFunctionTable][ArbFunctionTable_link] provides aggregators the ability to manage function tables, to enable one form of transaction compression. The Nitro aggregator implementation does not use these, so these methods have been stubbed and their effects disabled. They are kept for backwards compatibility. - -| Methods | | -|:---------------------------------------------------------|:-------------------------------------------| -| [![](e.png)][FTs0] [`Get`][FT0]`(address, index)`   | Reverts since the table is empty | -| [![](e.png)][FTs1] [`Size`][FT1]`(address)` | Returns the empty table's size, which is 0 | -| [![](e.png)][FTs2] [`Upload`][FT2]`(bytes)` | Does nothing | - -[FT0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbFunctionTable.go#L30 -[FT1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbFunctionTable.go#L25 -[FT2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbFunctionTable.go#L20 - -[FTs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbFunctionTable.sol#L35 -[FTs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbFunctionTable.sol#L32 -[FTs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbFunctionTable.sol#L29 - - -# ArbGasInfo -[ArbGasInfo][ArbGasInfo_link] orovides insight into the cost of using the chain. These methods have been adjusted to account for Nitro's heavy use of calldata compression. Of note to end-users, we no longer make a distinction between non-zero and zero-valued calldata bytes. - -| Methods | | -|:--------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------| -| [![](e.png)][GIs0] [`GetPricesInWeiWithAggregator`][GI0]`(aggregator)` | Get prices in wei when using the provided aggregator | -| [![](e.png)][GIs1] [`GetPricesInWei`][GI1]`()` | Get prices in wei when using the caller's preferred aggregator | -| [![](e.png)][GIs2] [`GetPricesInArbGasWithAggregator`][GI2]`(aggregator)` | Get prices in ArbGas when using the provided aggregator | -| [![](e.png)][GIs3] [`GetPricesInArbGas`][GI3]`()` | Get prices in ArbGas when using the caller's preferred aggregator | -| [![](e.png)][GIs4] [`GetGasAccountingParams`][GI4]`()` | Get the chain speed limit, pool size, and tx gas limit | -| [![](e.png)][GIs5] [`GetMinimumGasPrice`][GI5]`()` | Get the minimum gas price needed for a transaction to succeed | -| [![](e.png)][GIs6] [`GetGasPoolSeconds`][GI6]`()` | Get the number of seconds worth of the speed limit the gas pool contains | -| [![](e.png)][GIs7] [`GetGasPoolTarget`][GI7]`()` | Get the target fullness in bips the pricing model will try to keep the pool at | -| [![](e.png)][GIs8] [`GetGasPoolWeight`][GI8]`()` | Get the extent in bips to which the pricing model favors filling the pool over increasing speeds | -| [![](e.png)][GIs9] [`GetRateEstimate`][GI9]`()` | Get ArbOS's estimate of the amount of gas being burnt per second | -| [![](e.png)][GIs10] [`GetRateEstimateInertia`][GI10]`()` | Get how slowly ArbOS updates its estimate the amount of gas being burnt per second | -| [![](e.png)][GIs11] [`GetL1BaseFeeEstimate`][GI11]`()` | Get ArbOS's estimate of the L1 basefee in wei | -| [![](e.png)][GIs12] [`GetL1BaseFeeEstimateInertia`][GI12]`()` | Get how slowly ArbOS updates its estimate of the L1 basefee | -| [![](e.png)][GIs13] [`GetL1GasPriceEstimate`][GI13]`()` | Deprecated -- Same as getL1BaseFeeEstimate() | -| [![](e.png)][GIs14] [`GetCurrentTxL1GasFees`][GI14]`()` | Get L1 gas fees paid by the current transaction | - - -[GI0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L27 -[GI1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L63 -[GI2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L75 -[GI3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L99 -[GI4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L111 -[GI5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L120 -[GI6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L125 -[GI7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L130 -[GI8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L135 -[GI9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L140 -[GI10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L145 -[GI11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L150 -[GI12]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L155 -[GI13]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L160 -[GI14]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbGasInfo.go#L165 -[GI15]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/precompiles/ArbGasInfo.go#L170 -[GI16]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/precompiles/ArbGasInfo.go#L175 -[GI17]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/precompiles/ArbGasInfo.go#L180 - -[GIs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L36 -[GIs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L58 -[GIs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L72 -[GIs3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L83 -[GIs4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L94 -[GIs5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L104 -[GIs6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L107 -[GIs7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L110 -[GIs8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L113 -[GIs9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L116 -[GIs10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L119 -[GIs11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L122 -[GIs12]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L125 -[GIs13]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L128 -[GIs14]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbGasInfo.sol#L131 -[GIs15]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/contracts/src/precompiles/ArbGasInfo.sol#L123 -[GIs16]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/contracts/src/precompiles/ArbGasInfo.sol#L126 -[GIs17]: https://github.com/OffchainLabs/nitro/blob/6b6de9068662883518cff13c67b885161763f52c/contracts/src/precompiles/ArbGasInfo.sol#L129 - - -# ArbInfo -[ArbInfo][ArbInfo_link] provides the ability to lookup basic info about accounts and contracts. - -| Methods | | -|:-------------------------------------------------------|:-------------------------------------| -| [![](e.png)][Is0] [`GetBalance`][I0]`(account)`   | Retrieves an account's balance | -| [![](e.png)][Is1] [`GetCode`][I1]`(account)` | Retrieves a contract's deployed code | - -[I0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbInfo.go#L18 -[I1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbInfo.go#L26 - -[Is0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbInfo.sol#L25 -[Is1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbInfo.sol#L28 - - -# ArbosTest -[ArbosTest][ArbosTest_link] Ppovides a method of burning arbitrary amounts of gas, which exists for historical reasons. In Classic, `ArbosTest` had additional methods only the zero address could call. These have been removed since users don't use them and calls to missing methods revert. - -| Methods | | Nitro changes | -|:------------------------------------------------------|:----------------------------------------------------|---------------| -| [![](e.png)][Ts0] [`BurnArbGas`][T0]`(amount)`   | unproductively burns the amount of L2 ArbGas   | Now pure | - -[T0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbosTest.go#L17 - -[Ts0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbosTest.sol#L27 - - -# ArbOwner -[ArbOwner][ArbOwner_link] provides owners with tools for managing the rollup. Calls by non-owners will always revert. - -Most of Arbitrum Classic's owner methods have been removed since they no longer make sense in Nitro: - -- What were once chain parameters are now parts of ArbOS's state, and those that remain are set at genesis. -- ArbOS upgrades happen with the rest of the system rather than being independent -- Exemptions to address aliasing are no longer offered. Exemptions were intended to support backward compatibility for contracts deployed before aliasing was introduced, but no exemptions were ever requested. - -| Methods | | -|:-----------------------------------------------------------------|:-------------------------------------------------------------------------------------------------| -| [![](e.png)][Os0] [`AddChainOwner`][O0]`(account)` | Add account as a chain owner | -| [![](e.png)][Os1] [`RemoveChainOwner`][O1]`(account)` | Remove account from the list of chain owners | -| [![](e.png)][Os2] [`IsChainOwner`][O2]`(account)` | See if account is a chain owner | -| [![](e.png)][Os3] [`GetAllChainOwners`][O3]`()` | Retrieves the list of chain owners | -| [![](e.png)][Os4] [`SetL1BaseFeeEstimate`][O4]`(price)` | Set the L1 basefee estimate directly, bypassing the autoregression | -| [![](e.png)][Os5] [`SetL1BaseFeeEstimateInertia`][O5]`(inertia)` | Set how slowly ArbOS updates its estimate of the L1 basefee | -| [![](e.png)][Os6] [`SetL2GasPrice`][O6]`(price)` | Set the L2 gas price directly, bypassing the pool calculus | -| [![](e.png)][Os7] [`SetMinimumGasPrice`][O7]`(price)` | Set the minimum gas price needed for a transaction to succeed | -| [![](e.png)][Os8] [`SetSpeedLimit`][O8]`(limit)` | Set the computational speed limit for the chain | -| [![](e.png)][Os9] [`SetGasPoolSeconds`][O9]`(seconds)` | Set the number of seconds worth of the speed limit the gas pool contains | -| [![](e.png)][Os10] [`SetGasPoolTarget`][O10]`(target)` | Set the target fullness in bips the pricing model will try to keep the pool at | -| [![](e.png)][Os11] [`SetGasPoolWeight`][O11]`(weight)` | Set the extent in bips to which the pricing model favors filling the pool over increasing speeds | -| [![](e.png)][Os12] [`SetRateEstimateInertia`][O12]`(inertia)` | Set how slowly ArbOS updates its estimate the amount of gas being burnt per second | -| [![](e.png)][Os13] [`SetMaxTxGasLimit`][O13]`(limit)` | Set the maximum size a tx (and block) can be | -| [![](e.png)][Os14] [`GetNetworkFeeAccount`][O14]`()` | Get the network fee collector | -| [![](e.png)][Os15] [`SetNetworkFeeAccount`][O15]`(account)` | Set the network fee collector | - -[O0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L24 -[O1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L29 -[O2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L38 -[O3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L43 -[O4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L48 -[O5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L53 -[O6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L58 -[O7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L63 -[O8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L68 -[O9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L73 -[O10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L78 -[O11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L83 -[O12]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L88 -[O13]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L93 -[O14]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L98 -[O15]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwner.go#L103 - -[Os0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L30 -[Os1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L33 -[Os2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L36 -[Os3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L39 -[Os4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L42 -[Os5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L45 -[Os6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L48 -[Os7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L51 -[Os8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L54 -[Os9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L57 -[Os10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L60 -[Os11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L63 -[Os12]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L66 -[Os13]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L69 -[Os14]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L72 -[Os15]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L75 - -| Events | | -|:---------------------------------------------|:----------------------------------------------------------| -| [![](e.png)][Oes0] [`OwnerActs`][Oe0]   | Emitted when a successful call is made to this precompile | - -[Oe0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/wrapper.go#L105 - -[Oes0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwner.sol#L78 - - -# ArbOwnerPublic -[ArbOwnerPublic][ArbOwnerPublic_link] provides non-owners with info about the current chain owners. - -| Methods | | -|:-----------------------------------------------------------|:--------------------------------| -| [![](e.png)][OPs0] [`IsChainOwner`][OP0]`(account)`   | See if account is a chain owner | -| [![](e.png)][OPs1] [`GetAllChainOwners`][OP1]`()` | Gets the list of chain owners | -| [![](e.png)][OPs2] [`GetNetworkFeeAccount`][OP2]`()` | Gets the network fee collector | - -[OP0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwnerPublic.go#L24 -[OP1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwnerPublic.go#L19 -[OP2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbOwnerPublic.go#L29 - -[OPs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwnerPublic.sol#L25 -[OPs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwnerPublic.sol#L28 -[OPs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbOwnerPublic.sol#L31 - - -# ArbRetryableTx -[ArbRetryableTx][ArbRetryableTx_link] 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](arbos.md#Retryables). - - -| Methods | | Nitro changes | -|:------------------------------------------------------------|:-----------------------------------------------------------------------------------|:-----------------------| -| [![](e.png)][RTs0] [`Cancel`][RT0]`(ticket)` | Cancel the ticket and refund its callvalue to its beneficiary | | -| [![](e.png)][RTs1] [`GetBeneficiary`][RT1]`(ticket)`   | Gets the beneficiary of the ticket | | -| [![](e.png)][RTs2] [`GetLifetime`][RT2]`()` | Gets the default lifetime period a retryable has at creation | Reverts when not found | -| [![](e.png)][RTs3] [`GetTimeout`][RT3]`(ticket)` | Gets the timestamp for when ticket will expire | | -| [![](e.png)][RTs4] [`Keepalive`][RT4]`(ticket)` | Adds one lifetime period to the ticket's expiry | Doesn't add callvalue | -| [![](e.png)][RTs5] [`Redeem`][RT5]`(ticket)` | Schedule an attempt to redeem the retryable, donating all of the call's gas   | Happens in a future tx | - -[RT0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L184 -[RT1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L171 -[RT2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L110 -[RT3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L115 -[RT4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L132 -[RT5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L36 - -[RTs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L70 -[RTs1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L63 -[RTs2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L38 -[RTs3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L45 -[RTs4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L55 -[RTs5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L32 - -| Events | | Nitro Changes | -|:------------------------------------------------------|:---------------------------------------------------|:------------------------------------| -| [![](e.png)][RTes0] [`TicketCreated`][RTe0] | Emitted when creating a retryable | | -| [![](e.png)][RTes1] [`LifetimeExtended`][RTe1]   | Emitted when extending a retryable's expiry   | | -| [![](e.png)][RTes2] [`RedeemScheduled`][RTe2] | Emitted when scheduling a retryable | Replaces [Redeemed][old_event_link] | -| [![](e.png)][RTes3] [`Canceled`][RTe3] | Emitted when cancelling a retryable | | - -[RTe0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/arbos/tx_processor.go#L143 -[RTe1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L163 -[RTe2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/arbos/tx_processor.go#L186 -[RTe3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbRetryableTx.go#L209 - -[RTes0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L72 -[RTes1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L73 -[RTes2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L74 -[RTes3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbRetryableTx.sol#L81 - -[old_event_link]: https://github.com/OffchainLabs/arb-os/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/arb_os/arbretryable.mini#L90 - -# ArbStatistics -[ArbStatistics][ArbStatistics_link] provides statistics about the chain as of just before the Nitro upgrade. In Arbitrum Classic, this was how a user would get info such as the total number of accounts, but there are better ways to get that info in Nitro. - -| Methods | | -|:------------------------------------------------|:----------------------------------------------------------------------------------------| -| [![](e.png)][STs0] [`GetStats`][ST0]`()`   | Returns the current block number and some statistics about the rollup's pre-Nitro state | - -[ST0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbStatistics.go#L19 - -[STs0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbStatistics.sol#L32 - - -# ArbSys -[ArbSys][ArbSys_link] provides system-level functionality for interacting with L1 and understanding the call stack. - -| Methods | | Nitro changes | -|:-----------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------|:------------------| -| [![](e.png)][Ss0] [`ArbBlockNumber`][S0]`()` | Gets the current L2 block number | | -| [![](e.png)][Ss1] [`ArbBlockHash`][S1]`(blocknum)` | Gets the L2 block hash at blocknum, if blocknum is sufficiently recent | | -| [![](e.png)][Ss2] [`ArbChainID`][S2]`()` | Gets the chain's ChainID | | -| [![](e.png)][Ss3] [`ArbOSVersion`][S3]`()` | Gets the current ArbOS version | Now view | -| [![](e.png)][Ss4] [`GetStorageGasAvailable`][S4]`()` | Returns 0 since Nitro has no concept of storage gas | Now always 0 | -| [![](e.png)][Ss5] [`IsTopLevelCall`][S5]`()` | Checks if the caller is top-level (i.e. if the caller was called directly by an EOA or an L1 contract) | | -| [![](e.png)][Ss6] [`MapL1SenderContractAddressToL2Alias`][S6]`(contract, unused)`   | Gets contract's L2 alias | 2nd arg is unused | -| [![](e.png)][Ss7] [`WasMyCallersAddressAliased`][S7]`()` | Checks if the caller's caller was aliased | | -| [![](e.png)][Ss8] [`MyCallersAddressWithoutAliasing`][S8]`()` | Gets the caller's caller without any potential address aliasing | New outbox scheme | -| [![](e.png)][Ss9] [`SendTxToL1`][S9]`(destination, calldataForL1)` | Sends a transaction to L1, adding it to the outbox; callvalue is sent to L1 attached to the sent transaction | New outbox scheme | -| [![](e.png)][Ss10] [`SendMerkleTreeState`][S10]`()` | Gets the root, size, and partials of the outbox Merkle tree state | New outbox scheme | -| [![](e.png)][Ss11] [`WithdrawEth`][S11]`(destination)` | Send callvalue to the destination address on L1 | | - -[S0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L30 -[S1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L35 -[S2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L50 -[S3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L55 -[S4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L61 -[S5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L66 -[S6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L71 -[S7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L76 -[S8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L82 -[S9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L98 -[S10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L171 -[S11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L187 - -[Ss0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L31 -[Ss1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L37 -[Ss2]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L43 -[Ss3]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L49 -[Ss4]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L55 -[Ss5]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L61 -[Ss6]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L69 -[Ss7]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L78 -[Ss8]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L84 -[Ss9]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L100 -[Ss10]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L111 -[Ss11]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L92 - - -| Events | | -|:-----------------------------------------------------|:----------------------------------------------------------------| -| [![](e.png)][Ses0] [`L2ToL1Transaction`][Se0]   | Logs a send tx from L2 to L1, including data for outbox proving | -| [![](e.png)][Ses1] [`SendMerkleUpdate`][Se1] | Logs a new merkle branch needed for constructing outbox proofs | - -[Se0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L152 -[Se1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/precompiles/ArbSys.go#L138 - -[Ses0]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L124 -[Ses1]: https://github.com/OffchainLabs/nitro/blob/3f504c57fba8ddf0759b7a55b4108e0bf5a078b3/solgen/src/precompiles/ArbSys.sol#L143 - -| Removed Methods | | -|:------------------------------------------------------------------|:------------------------------------------------------------------| -| [![](e.png)][Srs0] [`GetStorageAt`][Sr0]`(account, index)`   | Nitro doesn't need this introspection, and users couldn't call it | -| [![](e.png)][Srs1] [`GetTransactionCount`][Sr1]`(account)` | Nitro doesn't need this introspection, and users couldn't call it | - -[Sr0]: https://github.com/OffchainLabs/arb-os/blob/89e36db597c4857a4dac3efd7cc01b13c7845cc0/arb_os/arbsys.mini#L335 -[Sr1]: https://github.com/OffchainLabs/arb-os/blob/89e36db597c4857a4dac3efd7cc01b13c7845cc0/arb_os/arbsys.mini#L315 - -[Srs0]: https://github.com/OffchainLabs/arb-os/blob/89e36db597c4857a4dac3efd7cc01b13c7845cc0/contracts/arbos/builtin/ArbSys.sol#L51 -[Srs1]: https://github.com/OffchainLabs/arb-os/blob/89e36db597c4857a4dac3efd7cc01b13c7845cc0/contracts/arbos/builtin/ArbSys.sol#L42 diff --git a/docs/assertion-tree.md b/docs/assertion-tree.md deleted file mode 100644 index 6956127b68..0000000000 --- a/docs/assertion-tree.md +++ /dev/null @@ -1,29 +0,0 @@ -# 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](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](./proving/challenge-manager.md) 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](./arbos/l2-to-l1-messaging.md) (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](inside-arbitrum-nitro#arbitrum#rollup#protocol). diff --git a/docs/assertionTree.png b/docs/assertionTree.png deleted file mode 100644 index cbed9fdf50..0000000000 Binary files a/docs/assertionTree.png and /dev/null differ diff --git a/docs/asset-bridging.md b/docs/asset-bridging.md deleted file mode 100644 index 4ec43e7ddf..0000000000 --- a/docs/asset-bridging.md +++ /dev/null @@ -1,150 +0,0 @@ -# Token Bridging - -The Arbitrum protocol's ability to pass messages between L1 and L2 (see [L1 to L2 messaging](./arbos/l1-to-l2-messaging.md)) can be leveraged to trustlessly move assets from Ethereum to an Arbitrum chain and back. Any asset / asset type can in principle be bridged, including Ether, ERC20 tokens, ERC-721 tokens, etc. - -## Depositing And Withdrawing Ether - -To move Ether from Ethereum onto the Arbitrum chain, you execute a deposit transaction via `Inbox.depositEth`. This transfers funds to the Bridge contract on the L1 and credits the same funds to you inside the Arbitrum chain at the specified address. - -```sol -function depositEth(address destAddr) external payable override returns (uint256) -``` - -As far as Ethereum knows, all deposited funds are held by Arbitrum's Bridge contract. - -Withdrawing ether can be done using the [ArbSys](./arbos/precompiles.md#ArbSys) withdrawEth method: - -```sol -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 is 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"](./arbos/l2-to-l1-messaging.md)). Once the withdrawal is executed from the Outbox, the user's Ether balance will be credited on L1. - -## Bridging ERC20 Tokens - -### Overview - -The Arbitrum protocol itself technically has no native notion of any token standards, and gives no built-in advantage or special recognition to any particular token bridge. Described here is the "Canonical Bridge," which we at Offchain Labs implemented, and which should be the primary bridge most users and applications use; it is (effectively) a DApp with contracts on both Ethereum and Arbitrum that leverages Arbitrum's cross-chain message passing system to achieve basic desired token-bridging functionality. We recommend that you use it! - -### Design Rationale - -In our token bridge design, we use the term "Gateway" as per [this proposal](https://ethereum-magicians.org/t/outlining-a-standard-interface-for-cross-domain-erc20-transfers/6151); i.e., one of a pair of contracts on two different domains (i.e., Ethereum and an Arbitrum chain), used to facilitate cross-domain asset transfers. - -Some core goals that motivated the design of our bridging system: - -#### Custom Gateway functionality - -For many ERC20 tokens, "standard" bridging functionality is sufficient, which entails the following: a token contract on Ethereum is associated with a "paired" token contract on Arbitrum. Depositing a token entails escrowing some amount of the token in an L1 bridge contract, and minting the same amount at the paired token contract on L2. On L2, the paired contract behaves much like a normal ERC20 token contract. - -Withdrawing entails burning some amount of the token in the L2 contract, which then can later be claimed from the L1 bridge contract. - -Many tokens, however, require custom Gateway systems, the possibilities of which are hard to generalize. E.g: - -- Tokens which accrue interest to their holders need to ensure that the interest is dispersed properly across layers, and doesn't simply accrue to the bridge contracts -- Our cross-domain WETH implementations requires tokens be wrapped and unwrapped as they move across layers. - -Thus, our bridge architecture must allow not just the standard deposit/withdraw functionality, but for new, custom Gateways to be dynamically added over time. - -#### Canonical L2 Representation Per L1 Token Contract - -...having multiple custom Gateways is well and good, but we also want to avoid a situation in which a single L1 token that uses our bridging system can be represented at multiple addresses/contracts on the L2 (as this adds significant friction and confusion for users and developers). Thus, we need a way to track which L1 token uses which gateway, and in turn, to have a canonical address oracle that maps the tokens addresses across the Ethereum and Arbitrum domains. - -### Canonical Token Bridge Implementation - -With this in mind, we provide an overview of our token bridging architecture: - -Our architecture consists of three types of contracts: - -1. **Asset contracts**: these are the token contracts themselves, i.e., an ERC20 on L1 and it's counterpart on Arbitrum. -2. **Gateways**: Pairs of contracts (one on L1, one on L2) that implement a particular type of cross chain asset bridging. -3. **Routers**: Exactly two contracts - (one on L1, one on L2) that route each asset to its designated Gateway. - -![img](./gatewayUML.svg) - -All Ethereum to Arbitrum token transfers are initiated via the `L1GatewayRouter` contract. `L1GatewayRouter` forwards the token's deposit-call to it's appropriate `L1ArbitrumGateway` contract. `L1GatewayRouter` is responsible for mapping L1 token addresses to L1Gateway, thus acting as L1/L2 address oracle and ensuring that each token corresponds to only one gateway. The `L1ArbitrumGateway` communicates to an `L2ArbitrumGateway` (typically/expectedly via retryable tickets (see [Retryable Tickets](./arbos/l1-to-l2-messaging.md)). - -Similarly, Arbitrum to Ethereum transfers are initiated via the `L2GatewayRouter` contract, which forwards calls the token's `L2ArbitrumGateway`, which in turn communicates to its corresponding `L1ArbitrumGateway` (typically/expectedly via sending messages to the Outbox.) - -For any given gateway pairing, we require that calls be initiated through the `GatewayRouter`, and that the gateways conform to the `TokenGateway` interfaces; the `TokenGateway` interfaces should be flexible and extensible enough to support any bridging functionality a particular token may require. - -### Default Standard Bridging - -By default, any ERC20 token on L1 that isn't registered to a Gateway can be permissionlessly bridged to the Standard ERC20 Gateway. - -You can use the [bridge UI](https://bridge.arbitrum.io/) or [this script](https://github.com/OffchainLabs/arbitrum-sdk/blob/master/scripts/deployStandard.ts) to a bridge a token to L2. - -#### Example: Standard Arb-ERC20 Deposit/Withdraw - -To help illustrate what this all looks like in practice, let's go through the steps of what depositing and withdrawing `SomeERC20Token` via our Standard ERC20 gateway looks like. Here, we're assuming that `SomeERC20Token` has already been registered in the `L1GatewayRouter` to use the Standard ERC20 Gateway. - -#### Deposits - -1. A user calls `GatewayRouter.outBoundTransfer` (with `SomeERC20Token`'s L1 address as an argument). -1. `GatewayRouter` looks up `SomeERC20Token`'s gateway, and finding that it's Standard ERC20 gateway (the `L1ERC20Gateway` contract). -1. `GatewayRouter` calls `L1ERC20Gateway.outBoundTransfer`, forwarding the appropriate parameters. -1. `L1ERC20Gateway` escrows tokens, and creates a retryable ticket to trigger `L2ERC20Gateway`'s `finalizeInboundTransfer` method on L2. -1. `finalizeInboundTransfer` mints the appropriate amount of tokens at the `arbSomeERC20Token` contract on L2. - -Note that arbSomeERC20Token is an instance of StandardArbERC20, which includes `bridgeMint` and `bridgeBurn` methods only callable by the L2ERC20Gateway. - -![img](./bridge_deposits.png) - -#### Withdrawals - -1. On Arbitrum, a user calls `L2GatewayRouter.outBoundTransfer`, which in turn calls `outBoundTransfer` on arbSomeERC20Token's gateway (i.e., L2ERC20Gateway). -1. This burns arbSomeERC20Token tokens, and calls ArbSys with an encoded message to `L1ERC20Gateway.finalizeInboundTransfer`, which will be eventually executed on L1. -1. After the dispute window expires and the assertion with the user's transaction is confirmed, a user can call `Outbox.executeTransaction`, which in turn calls the encoded `L1ERC20Gateway.finalizeInboundTransfer` message, releasing the user's tokens from the L1ERC20Gateway contract's escrow. - -![img](./bridge_withdrawals.png) - -### The Arbitrum Generic Custom Gateway - -Just because a token has requirements beyond what are offered via the StandardERC20 gateway, that doesn't necessarily mean that a unique Gateway needs to be tailor-made for the token in question. Our Generic-Custom Gateway is designed to be flexible enough to be suitable for most (but not necessarily all) custom fungible token needs. As a general rule: - -**If your custom token has the ability to increase its supply (i.e, mint) directly on the L2, and you want the L2-minted tokens be withdrawable back to L1 and recognized by the L1 contract, it will probably require its own special gateway. Otherwise, the Generic-Custom Gateway is likely the right solution for you!** - -Some examples of token features suitable for the Generic-Custom Gateway: - -- L2 token contract upgradable via a proxy -- L2 token contract includes address whitelisting /blacklisting -- Deployer determines address of L2 token contract - -#### Setting Up Your Token With The Generic Custom Gateway - -Follow the following steps to get your token set up to use the Generic Custom Gateway - -**0. Have an L1 token** - -- Your token on L1 should conform to the [ICustomToken](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/ethereum/ICustomToken.sol) interface; (see [TestCustomTokenL1](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/test/TestCustomTokenL1.sol) for an example implementation). Crucially, it must have an `isArbitrumEnabled` method in its interface. - -**1. Deploy your token on Arbitrum** - -- Your token should conform to the minimum [IArbToken](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/arbitrum/IArbToken.sol) - interface; i.e., it should have `bridgeMint` and `bridgeBurn` methods only callable by the L2CustomGateway contract, and the address of its corresponding Ethereum token accessible via `l1Address`. For an example implementation, see [TestArbCustomToken](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/test/TestArbCustomToken.sol). - -**2. Register Your Token on L1 to Your Token on L2 via the L1CustomGateway Contract** -Have your L1 token's contract make an external call to `L1CustomGateway.registerTokenToL2`. This registration can alternatively be performed as an admin registration. - -**3. Register Your Token on L1 To the L1Gateway Router** -After your token's registration to the Custom Gateway is complete, have your L1 token's contract make an external call to `L1GatewayRouter.setGateway`; this registration can also alternatively be performed as an admin registration. - -| If you have questions about your custom token needs, feel free to [reach out](https://discord.gg/ZpZuw7p). | -| ---------------------------------------------------------------------------------------------------------- | - -#### Other Flavors of Gateways - -Note that in the system described above, one pair of Gateway contracts handles the bridging of many ERC20s; i.e., many ERC20s on L1 are each paired with their own ERC20s on Arbitrum via a single gateway contract pairing. Other gateways may well bear different relations with the contracts that they bridge. - -Take our wrapped Ether implementation, for example: here, a single WETH contract on L1 is connected to a single WETH contract on L2. When transferring WETH from one domain to another, the L1/L2 Gateway architecture is used to unwrap the WETH on domain A, transfer the now-unwrapped Ether, and then re-wrap it on domain B. This ensures that WETH can behave on Arbitrum the way users are used to it behaving on Ethereum, while ensuring that all WETH tokens are always fully collateralized on the layer in which they reside. - -No matter the complexity of a particular token's bridging needs, a gateway can in principle be created to accommodate it within our canonical bridging system. - -### Demos - -See [token-deposit](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/token-deposit) and [token-withdraw](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/token-withdraw) for demos of interacting with the bridge architecture via the [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk). - -#### A Word of Caution on Bridges (aka, "I've Got a Bridge To Sell You") - -Cross chain bridging is an exciting design space; alternative bridge designs can potentially offer faster withdrawals, interoperability with other chains, different trust assumptions with their own potentially valuable UX tradeoffs, etc. They can also potentially be completely insecure and/or outright scams. Users should treat other, non-canonical bridge applications the same way they treat any application running on Arbitrum, and exercise caution and due diligence before entrusting them with their value. diff --git a/docs/bridge_deposits.png b/docs/bridge_deposits.png deleted file mode 100644 index dc6912b517..0000000000 Binary files a/docs/bridge_deposits.png and /dev/null differ diff --git a/docs/bridge_withdrawals.png b/docs/bridge_withdrawals.png deleted file mode 100644 index 62bc3ca12c..0000000000 Binary files a/docs/bridge_withdrawals.png and /dev/null differ diff --git a/docs/das/daserver-instructions.md b/docs/das/daserver-instructions.md deleted file mode 100644 index 9584fa2a3e..0000000000 --- a/docs/das/daserver-instructions.md +++ /dev/null @@ -1,423 +0,0 @@ -# Data Availability Server Instructions - -## Description -The Data Availability Server, `daserver`, allows storage and retrieval of transaction data batches for Arbitrum AnyTrust chains. It can be run in two modes: either committee member or mirror. Committee members accept time-limited requests to store data batches from an Arbitrum AnyTrust sequencer, and if they store the data then they return a signed certificate promising to store that data. Committee members and mirrors both respond to requests to retrieve the data batches. Mirrors exist to replicate and serve the data so that committee members to provide resiliency to the network in the case committee members going down, and to make it so committee members don't need to serve requests for the data directly. The data batches are addressed by a keccak256 hash of their contents. This document gives sample configurations for `daserver` in committee member and mirror mode. - -### Interfaces -There are two interfaces, a REST interface supporting only GET operations and intended for public use, and an RPC interface intended for use only by the AnyTrust sequencer. Mirrors listen on the REST interface only and respond to queries on `/get-by-hash/`. The response is always the same for a given hash so it is cacheable; it contains a `cache-control` header specifying the object is immutable and to cache for up to 28 days. The REST interface has a health check on `/health` which will return 200 if the underling storage is working, otherwise 503. - -Committee members listen on the REST interface and additionally listen on the RPC interface for `das_store` RPC messages from the sequencer. The sequencer signs its requests and the committee member checks the signature. The RPC interface also has a health check that checks the underlying storage that responds requests with RPC method `das_healthCheck`. - -### Storage -`daserver` can be configured to use one or more of three storage backends; S3, files on local disk, and database on disk (please give us feedback if there are other storage backends you would like supported). If more than one is selected, store requests must succeed to all of them for it to be considered successful, and retrieve requests only require one to succeed. - -### Caching -An in-memory cache can be enabled to avoid needing to access underlying storage for retrieve requests . - -### Synchronizing state -`daserver` also has an optional REST aggregator which, in the case that a data batch is not found in cache or storage, queries for that batch from a list other of REST servers, and then stores that batch locally. This is how committee members that miss storing a batch (not all committee members are required by the AnyTrust protocol to report success in order to post the batch's certificate to L1) can automatically repair gaps in data they store, and how mirrors can sync (a sync mode that eagerly syncs all batches is planned for a future release). A public list of REST endpoints is published online, which `daserver` can be configured to download and use, and additional endpoints can be specified in configuration. - -## Image: -`offchainlabs/nitro-node:v2.0.8-5b9fe9c` - -## Usage of daserver - -Options for both committee members and mirrors: -``` - # Server options - --enable-rest enable the REST server listening on rest-addr and rest-port - --log-level int log level; 1: ERROR, 2: WARN, 3: INFO, 4: DEBUG, 5: TRACE (default 3) - --rest-addr string REST server listening interface (default "localhost") - --rest-port uint REST server listening port (default 9877) - - # L1 options - --data-availability.l1-node-url string URL for L1 node, only used in standalone daserver; when running as part of a node that node's L1 configuration is used - --data-availability.sequencer-inbox-address string L1 address of SequencerInbox contract - - # Storage options - --data-availability.local-db-storage.data-dir string directory in which to store the database - --data-availability.local-db-storage.discard-after-timeout discard data after its expiry timeout - --data-availability.local-db-storage.enable enable storage/retrieval of sequencer batch data from a database on the local filesystem - - --data-availability.local-file-storage.data-dir string local data directory - --data-availability.local-file-storage.enable enable storage/retrieval of sequencer batch data from a directory of files, one per batch - - --data-availability.s3-storage.access-key string S3 access key - --data-availability.s3-storage.bucket string S3 bucket - --data-availability.s3-storage.discard-after-timeout discard data after its expiry timeout - --data-availability.s3-storage.enable enable storage/retrieval of sequencer batch data from an AWS S3 bucket - --data-availability.s3-storage.object-prefix string prefix to add to S3 objects - --data-availability.s3-storage.region string S3 region - --data-availability.s3-storage.secret-key string S3 secret key - - # Cache options - --data-availability.local-cache.enable Enable local in-memory caching of sequencer batch data - --data-availability.local-cache.expiration duration Expiration time for in-memory cached sequencer batches (default 1h0m0s) - - # REST fallback options - --data-availability.rest-aggregator.enable enable retrieval of sequencer batch data from a list of remote REST endpoints; if other DAS storage types are enabled, this mode is used as a fallback - --data-availability.rest-aggregator.online-url-list string a URL to a list of URLs of REST das endpoints that is checked at startup; additive with the url option - --data-availability.rest-aggregator.urls strings list of URLs including 'http://' or 'https://' prefixes and port numbers to REST DAS endpoints; additive with the online-url-list option - --data-availability.rest-aggregator.sync-to-storage.eager eagerly sync batch data to this DAS's storage from the rest endpoints, using L1 as the index of batch data hashes; otherwise only sync lazily - --data-availability.rest-aggregator.sync-to-storage.eager-lower-bound-block uint when eagerly syncing, start indexing forward from this L1 block -``` -``` -Options only for committee members: - --enable-rpc enable the HTTP-RPC server listening on rpc-addr and rpc-port - --rpc-addr string HTTP-RPC server listening interface (default "localhost") - --rpc-port uint HTTP-RPC server listening port (default 9876) - - --data-availability.key.key-dir string the directory to read the bls keypair ('das_bls.pub' and 'das_bls') from; if using any of the DAS storage types exactly one of key-dir or priv-key must be specified - --data-availability.key.priv-key string the base64 BLS private key to use for signing DAS certificates; if using any of the DAS storage types exactly one of key-dir or priv-key must be specified -```` - -Options generating/using JSON config: -``` - --conf.dump print out currently active configuration file - --conf.file strings name of configuration file -``` - -Options for producing Prometheus metrics: -``` - --metrics enable metrics - --metrics-server.addr string metrics server address (default "127.0.0.1") - --metrics-server.port int metrics server port (default 6070) - --metrics-server.update-interval duration metrics server update interval (default 3s) -``` - -Some options are not shown because they are only used by nodes, or they are experimental/advanced. A complete list of options can be found by running `daserver --help` - -## Sample Deployments - -### Sample Committee Member - -Using `daserver` as a committee member requires: -- A BLS private key to sign the Data Availability Certificates it returns to clients (the sequencer aka batch poster) requesting to Store data. -- The Ethereum L1 address of the sequencer inbox contract, in order to find the batch poster signing address. -- An Ethereum L1 RPC endpoint to query the sequencer inbox contract. -- A persistent volume to write the stored data to if using one of the local disk modes. -- A S3 bucket, and credentials (secret key, access key) of an IAM user that is able to read and write from it if you are using the S3 mode. - -Once the DAS is set up, the local public key in `das_bls.pub` should be communicated out-of-band to the operator of the chain, along with a protocol (http/https), host, and port of the RPC server that can be reached by the sequencer, so that it can be added to the committee keyset. - -#### Set up persistent volume - -This is the persistent volume for storing the DAS database and BLS keypair. - -``` -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: das-server -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 200Gi - storageClassName: gp2 -``` - -#### Generate key -The BLS keypair must be generated using the `datool keygen` utility. It can be passed to the `dasever` executable by file or on the command line. - -In this sample deployment we use a k8s deployment to run `datool keygen` to create it as a file on the volume that the DAS will use. After this deployment has run once, the deployment can be torn down and deleted. - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: das-server -spec: - replicas: 1 - selector: - matchLabels: - app: das-server - template: - metadata: - labels: - app: das-server - spec: - containers: - - command: - - bash - - -c - - | - mkdir -p /home/user/data/keys - /usr/local/bin/datool keygen --dir /home/user/data/keys - sleep infinity - image: offchainlabs/nitro-node:v2.0.8-5b9fe9c - imagePullPolicy: Always - resources: - limits: - cpu: "4" - memory: 10Gi - requests: - cpu: "4" - memory: 10Gi - ports: - - containerPort: 9876 - protocol: TCP - volumeMounts: - - mountPath: /home/user/data/ - name: data - volumes: - - name: data - persistentVolumeClaim: - claimName: das-server -``` - -#### Create DAS deployment - -This deployment sets up a DAS server using the Arbitrum Nova Mainnet. It uses the L1 inbox contract at 0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b. For the Arbitrum Nova Mainnet you must specify a Mainnet Ethereum L1 RPC endpoint. - -This configuration sets up all three storage types. To disable any of them, remove the --data-availability.(local-file-storage|local-disk-storage|s3-storage).enable option, and the other options for that storage type can also be removed. If updating an existing deployment from `offchainlabs/nitro-node:v2.0.8-5b9fe9c` that is using the local files on disk storage type, you should use at least `local-file-storage`. - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: das-server -spec: - replicas: 1 - selector: - matchLabels: - app: das-server - strategy: - rollingUpdate: - maxSurge: 0 - maxUnavailable: 50% - type: RollingUpdate - template: - metadata: - labels: - app: das-server - spec: - containers: - - command: - - bash - - -c - - | - mkdir -p /home/user/data/db - mkdir -p /home/user/data/badgerdb - /usr/local/bin/daserver --data-availability.l1-node-url ---enable-rpc --rpc-addr '0.0.0.0' --enable-rest --rest-addr '0.0.0.0' --log-level 3 --data-availability.local-file-storage.enable --data-availability.local-file-storage.data-dir /home/user/data/db --data-availability.local-db-storage.enable --data-availability.local-db-storage.data-dir /home/user/data/badgerdb --data-availability.s3-storage.enable --data-availability.s3-storage.access-key "" --data-availability.s3-storage.bucket --data-availability.s3-storage.region --data-availability.s3-storage.secret-key "" --data-availability.s3-storage.object-prefix "YOUR OBJECT KEY PREFIX/" --data-availability.key.key-dir /home/user/data/keys --data-availability.local-cache.enable --data-availability.rest-aggregator.enable --data-availability.rest-aggregator.online-url-list "https://nova.arbitrum.io/das-servers" --data-availability.sequencer-inbox-address '0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b' - image: offchainlabs/nitro-node:v2.0.8-5b9fe9c - imagePullPolicy: Always - resources: - limits: - cpu: "4" - memory: 10Gi - requests: - cpu: "4" - memory: 10Gi - ports: - - containerPort: 9876 - hostPort: 9876 - protocol: TCP - - containerPort: 9877 - hostPort: 9877 - protocol: TCP - volumeMounts: - - mountPath: /home/user/data/ - name: data - readinessProbe: - failureThreshold: 3 - httpGet: - path: /health/ - port: 9877 - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - volumes: - - name: data - persistentVolumeClaim: - claimName: das-server -``` - - -### Sample Mirror - -Using `daserver` as a mirror requires: -- The Ethereum L1 address of the sequencer inbox contract, for syncing all batch data (future capability) -- An Ethereum L1 RPC endpoint to query the sequencer inbox contract. -- A persistent volume to write the stored data to if using one of the local disk modes. -- A S3 bucket, and credentials (secret key, access key) of an IAM user that is able to read and write from it if you are uisng the S3 mode. - -The mirror does not require a BLS key since it will not be accepting store requests from the sequencer. - -Once the mirror is set up, please communicate a URL to reach it to the chain operator so they can add it to the public mirror list. - -#### Set up persistent volume - -This is the persistent volume for storing the DAS database. - -``` -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: das-mirror -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 200Gi - storageClassName: gp2 -``` - -#### Create DAS deployment - -This deployment sets up a DAS server using the Arbitrum Nova Mainnet. It uses the L1 inbox contract at 0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b. For the Arbitrum Nova Mainnet you must specify a Mainnet Ethereum L1 RPC endpoint. - -This configuration sets up all three storage types. To disable any of them, remove the --data-availability.(local-file-storage|local-disk-storage|s3-storage).enable option, and the other options for that storage type can also be removed. - -``` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: das-mirror -spec: - replicas: 1 - selector: - matchLabels: - app: das-mirror - strategy: - rollingUpdate: - maxSurge: 0 - maxUnavailable: 50% - type: RollingUpdate - template: - metadata: - labels: - app: das-mirror - spec: - containers: - - command: - - bash - - -c - - | - mkdir -p /home/user/data/db - mkdir -p /home/user/data/badgerdb - /usr/local/bin/daserver --data-availability.l1-node-url ---enable-rest --rest-addr '0.0.0.0' --log-level 3 --data-availability.local-file-storage.enable --data-availability.local-file-storage.data-dir /home/user/data/db --data-availability.local-db-storage.enable --data-availability.local-db-storage.data-dir /home/user/data/badgerdb --data-availability.s3-storage.enable --data-availability.s3-storage.access-key "" --data-availability.s3-storage.bucket --data-availability.s3-storage.region --data-availability.s3-storage.secret-key "" --data-availability.s3-storage.object-prefix "YOUR OBJECT KEY PREFIX/" --data-availability.local-cache.enable --data-availability.rest-aggregator.enable --data-availability.rest-aggregator.urls "http://your-committee-member.svc.cluster.local:9877" --data-availability.rest-aggregator.online-url-list "https://nova.arbitrum.io/das-servers" --data-availability.rest-aggregator.sync-to-storage.eager --data-availability.rest-aggregator.sync-to-storage.eager-lower-bound-block 15025611 --data-availability.sequencer-inbox-address '0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b' - image: offchainlabs/nitro-node:v2.0.8-5b9fe9c - imagePullPolicy: Always - resources: - limits: - cpu: "4" - memory: 10Gi - requests: - cpu: "4" - memory: 10Gi - ports: - - containerPort: 9877 - hostPort: 9877 - protocol: TCP - volumeMounts: - - mountPath: /home/user/data/ - name: data - readinessProbe: - failureThreshold: 3 - httpGet: - path: /health/ - port: 9877 - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - volumes: - - name: data - persistentVolumeClaim: - claimName: das-mirror -``` - - - -### Testing -#### Basic validation: health check data is present -In the docker image there is the `datool` utility that can be used to Store and Retrieve messages from a DAS. We will take advantage of a data hash that will always be present if the the health check is enabled. - -From the pod: -``` -$ /usr/local/bin/datool client rest getbyhash --url http://localhost:9877 --data-hash 0x8b248e2bd8f75bf1334fe7f0da75cc7c1a34e00e00a22a96b7a43d580d250f3d -Message: Test-Data - -$ /usr/local/bin/datool client rpc getbyhash --url http://localhost:9876 --data-hash 0x8b248e2bd8f75bf1334fe7f0da75cc7c1a34e00e00a22a96b7a43d580d250f3d -Message: Test-Data -``` - -If you do not have the health check configured yet, you can trigger one manually as follows: -``` -$ curl http://localhost:9877/health -``` - -Using curl to check the REST endpoint -``` -$ curl https:////get-by-hash/8b248e2bd8f75bf1334fe7f0da75cc7c1a34e00e00a22a96b7a43d580d250f3d -{"data":"VGVzdC1EYXRh"} -``` - -#### Further validation: using Store interface directly - -The Store interface of `daserver` validates that requests to store data are signed by the the Batch Poster's ECDSA key, identified via a call to the Sequencer Inbox contract on L1. It can also be configured to accept Store requests signed with another ECDSA key of your chosing. This could be useful for running load tests, canaries, or troubleshooting your own infrastructure. Using this facility, a load test could be constructed by writing a script to store arbitrary amounts of data at an arbitrary rate; a canary could be constructed to store and retrieve data on some interval. - -Generate an ECDSA keypair: -``` -$ /usr/local/bin/datool keygen --dir /dir-of-your-choice/ --ecdsa -``` - -Then add the following configuration option to `daserver`: -``` ---data-availability.extra-signature-checking-public-key /dir-of-your-choice/ecdsa.pub - -OR - ---data-availability.extra-signature-checking-public-key 0x -``` - -Now you can use the `datool` utility to send Store requests signed with the ecdsa private key: -``` -$ /usr/local/bin/datool rpc store --url http://localhost:9876 --message "Hello world" --signing-key /tmp/ecdsatest/ecdsa - -OR - -$ /usr/local/bin/datool client rpc store --url http://localhost:9876 --message "Hello world" --signing-key "0x" -``` - -The above command outputs the `Hex Encoded Data Hash: ` which can be used to retrieve the data: -``` -$ /usr/local/bin/datool client rpc getbyhash --url http://localhost:9876 --data-hash 0x052cca0e379137c975c966bcc69ac8237ac38dc1fcf21ac9a6524c87a2aab423 -Message: Hello world -$ /usr/local/bin/datool client rest getbyhash --url http://localhost:9877 --data-hash 0x052cca0e379137c975c966bcc69ac8237ac38dc1fcf21ac9a6524c87a2aab423 -Message: Hello world -``` - -The retention period defaults to 24h but can be configured for `datool client rpc store` with the option: -``` ---das-retention-period -``` - -### Deployment recommendations -The REST interface is cacheable, consider using a CDN or caching proxy in front of your REST endpoint. - -If you are running a mirror, the REST interface on your committee member does not have to be exposed publicly. Your mirrors can sync on your private network from the REST interface of your committee member and other public mirrors. - -### Metrics -If metrics are enabled in configuration, then several useful metrics are available at the configured port (default 6070), at path `debug/metrics` or `debug/metrics/prometheus`. - -| Metric | Description -| - | - | -| arb_das_rest_getbyhash_requests | Count of REST GetByHash calls | -| arb_das_rest_getbyhash_success | Successful REST GetByHash calls | -| arb_das_rest_getbyhash_failure | Failed REST GetByHash calls | -| arb_das_rest_getbyhash_bytes | Bytes retrieved with REST GetByHash calls | -| arb_das_rest_getbyhash_duration (p50, p75, p95, p99, p999, p9999) | Duration of REST GetByHash calls (ns) | -| arb_das_rpc_store_requests | Count of RPC Store calls | -| arb_das_rpc_store_success | Successful RPC Store calls | -| arb_das_rpc_store_failure | Failed RPC Store calls | -| arb_das_rpc_store_bytes | Bytes retrieved with RPC Store calls | -| arb_das_rpc_store_duration (p50, p75, p95, p99, p999, p9999) | Duration of RPC Store calls (ns) | \ No newline at end of file diff --git a/docs/faqs/anytrust-vs-rollup.md b/docs/faqs/anytrust-vs-rollup.md deleted file mode 100644 index 525f5c59ec..0000000000 --- a/docs/faqs/anytrust-vs-rollup.md +++ /dev/null @@ -1,15 +0,0 @@ -# Q: What's the difference between Arbitrum Rollup and Arbitrum AnyTrust? - -**A:** Arbitrum Rollup is an Optimistic Rollup protocol; it is trustless and permissionless. Part of how these properties are achieved is by requiring all chain data to be posted on layer 1. This means the availability of this data follows directly from the security properties of Ethereum itself, and, in turn, that any party can participate in validating the chain and ensuring its safely. - -By contrast, Arbitrum AnyTrust introduces a trust assumption in exchange for lower fees; data availability is managed by a Data Availability Committee (DAC), a fixed, permissioned set of entities. We introduced some threshold, K, with the assumption that at least K members of the committee are honest. For simplicity, we'll hereby assume a committee of size 20 and a K value of 2: - -If 19 out of the 20 committee members _and_ the Sequencer are malicious and colluding together, they can break the chain's safety (and, e.g., steal users' funds); this is the new trust assumption. - -If anywhere between 2 and 18 of the committee members are well behaved, the AnyTrust chain operates in "Rollup mode"; i.e., data gets posted on L1. - -In what should be the common and happy case, however, in which at least 19 of the 20 committee members are well behaved, the system operates without the posting the L2 chain's data on L1, and thus, users pay significantly lower fees. This is the core upside of AnyTrust chains over rollups. - -Variants of the AnyTrust model in which the new trust assumption is minimized are under consideration; stay tuned. - -For more, see [Inside AnyTrust](../inside-anytrust.md). diff --git a/docs/faqs/beta-status.md b/docs/faqs/beta-status.md deleted file mode 100644 index 96270c6fdc..0000000000 --- a/docs/faqs/beta-status.md +++ /dev/null @@ -1,5 +0,0 @@ -# Q: Is Arbitrum One a trustless, permissionless, decentralized chain? - -Arbitrum One runs on a [full, feature complete implementation](https://github.com/OffchainLabs/nitro) of the Arbitrum Rollup protocol; given that this is still fairly new technology, however, we currently maintain administrative "training wheels." The centralized components of the system will be progressively phased out over time. - -For status updates, see ["Mainnet Beta"](../mainnet-beta.md), or check out the work of our friends at [L2BEAT](https://l2beat.com/scaling/risk/). \ No newline at end of file diff --git a/docs/faqs/how-fees.md b/docs/faqs/how-fees.md deleted file mode 100644 index 056a0f87c1..0000000000 --- a/docs/faqs/how-fees.md +++ /dev/null @@ -1,13 +0,0 @@ -# Q: How do gas fees work on Arbitrum? - -Fees on Arbitrum chains are collected on L2 in the chains' native currency (ETH on Arbitrum One and Nova). - -A transaction fee is comprised of both an L1 and an L2 component: - -The L1 component is meant to compensate the Sequencer for the cost of posting transactions on L1 (but no more). (See [L1 Pricing](../arbos/l1-pricing.md).) - -The L2 component covers the cost of operating the L2 chain; it uses Geth for gas calculation and thus behaves nearly identically to L1 Ethereum (See [Gas](../arbos/gas.md)). - -L2 Gas price adjusts responsively to chain congestion, ala EIP 1559. - -Calling an Arbitrum node's `eth_estimateGas` RPC returns a value sufficient to cover both the L1 and L2 components of the fee for the current gas price; this is the value that, e.g., will appear in users' wallets. (See [2-D fees](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) for more.) diff --git a/docs/faqs/seq-or-val.md b/docs/faqs/seq-or-val.md deleted file mode 100644 index 54c2ffe27f..0000000000 --- a/docs/faqs/seq-or-val.md +++ /dev/null @@ -1,18 +0,0 @@ -# Q: Is the "Sequencer" the only "Validator" of the Arbitrum One chain? If the Sequencer is centralized, is everything centralized? Can a centralized Sequencer do bad things like steal all of my money? - -A: No, no, and no! - -An Arbitrum Chain's Sequencer(s) and Validators and completely distinct entities, with their own distinct roles. - -The [Sequencer](../sequencer.md) is the entity granted specific privileges over ordering transactions; once the Sequencer commits to an ordering (by posting a batch on Ethereum), it has no say over what happens next (i.e., execution). A malicious/faulty Sequencer can do things like reorder transactions or _temporarily_ delay a transaction's inclusion — things which could be, to be sure, annoying and bad — but can do nothing to compromise the chain's safety. - -The _Validators_ are the ones responsible for the safety of the chain; i.e., making staked claims about the chain state, disputing each other, etc. - -Currently, on Arbitrum One, the Sequencer is a centralized entity maintained by Offchain Labs. Eventually, the single Sequencer will be replaced by a distributed committee of Sequencers who come to consensus on transaction ordering. This upgrade be an improvement; we don't want you to have to trust us not to reorder your transactions. However, it also isn't _strictly_ necessary for Arbitrum One to achieve its most fundamental properties. - -In other words: -_**An Arbitrum Rollup chain with a centralized Sequencer could theoretically still be trustless!**_ - -Which is to say — the more important thing than decentralizing the Sequencer i.e., the thing you ought to care more about — is decentralizing the _Validators_. - -Arbitrum One's Validator set is currently whitelisted; overtime, the whitelist will grow and then be removed entirely. For more info see ["Mainnet Beta"](../mainnet-beta.md). diff --git a/docs/faqs/the-merge.md b/docs/faqs/the-merge.md deleted file mode 100644 index d2411598fb..0000000000 --- a/docs/faqs/the-merge.md +++ /dev/null @@ -1,9 +0,0 @@ -## As an Arbitrum user, do I need to do anything to prepare for 'The Merge'? - -_**Update 9/15/22: merge complete! 🎉🐼**_ - -In short, no. Arbitrum chains will simply continue to operate during/after [The Merge](https://ethereum.org/en/upgrades/merge/), Ethereum's transition to Proof of Stake. There is no anticipated L1 or L2 downtime, and no action is required. - -The Merge represents a major milestone for Ethereum; it also paves the way for [potential future L1 upgrades](https://notes.ethereum.org/@vbuterin/proto_danksharding_faq) which could significantly reduce fees for Arbitrum users. So we're excited. - -Certain exchanges and projects have specific actions they are encouraging users to take regarding The Merge; we advise users to check with projects they use and/or have funds with on any further required action. diff --git a/docs/faqs/what-if-dispute.md b/docs/faqs/what-if-dispute.md deleted file mode 100644 index 029d96dcc6..0000000000 --- a/docs/faqs/what-if-dispute.md +++ /dev/null @@ -1,22 +0,0 @@ -# FAQ: Delays Disputes and Reorgs - -## If there is a dispute, can my L2 transaction get reorged / throw out / "yeeted"? ? - -Nope; once an Arbitrum transaction is included on L1, there is no way it can be reorged (unless the L1 itself reorgs, of course). A "dispute" involves Validators disagreeing over execution, i.e., the outputted state of a chain. The inputs, however, can't be disputed; they are determined by the Inbox on L1. (See [Transaction Lifecycle](../tx-lifecycle.md)) - -## ...okay but if there's a dispute, will my transaction get delayed? - -The only thing that a dispute can add delay to is the confirmation of L2-to-L1 messages. All other transactions continue to be processed, even while a dispute is still undergoing. (Additionally: in practice, most L2 to L1 messages represent withdrawals of fungible assets; these can be trustlessly completed _even during a dispute_ via trustless fast "liquidity exit" applications. See [L2 To L1 Messages](../arbos/l2-to-l1-messaging.md)). - -## ...okay okay, but if we're just talking about an L2-to-L1 message, and assuming there's no disputes, how long between the time the message is initiated and when I can execute it on L1? Is it exactly one week? - -It will be roughly one week, but there's some variability in the exact wall-clock time of the dispute window, plus there's some expected additional "padding" time on both ends (no more than about an hour, typically). - -The variability of the dispute window comes from the slight variance of block times. Arbitrum One's dispute window is 45818 blocks; this converts to ~1 week assuming 13.2 seconds per block, which was the average block time when Ethereum used Proof of Work (with the switch to Proof of Stake, average block times are expected to be slightly lower — about 12 seconds.) - -The "padding on both ends" involves three events that have to occur between a client receiving their transaction receipt from a Sequencer and their L2 to L1 message being executable. After getting their receipt, - -1. The Sequencer posts their transaction in a batch (usually within a few minutes, though the Sequencer will wait a bit longer if the L1 is congested). Then, -1. A validator includes their transaction in an RBlock (usually within the hour). - Then, after the ~week long dispute window passes, the RBlock is confirmable, and -1. Somebody (anybody) confirms the RBlock on L1. (usually within ~15 minutes) diff --git a/docs/for-devs/cross-chain-messsaging.md b/docs/for-devs/cross-chain-messsaging.md deleted file mode 100644 index a49246bc28..0000000000 --- a/docs/for-devs/cross-chain-messsaging.md +++ /dev/null @@ -1,20 +0,0 @@ -# Cross Chain Messaging - -The Arbitrum protocol and related tooling makes it easy for developers to build cross-chain applications; i.e., applications that involve sending messages from Ethereum to an Arbitrum chain, and/or from an Arbitrum chain to Ethereum. - -## Ethereum to Arbitrum Messaging - -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`](../arbos/precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](../arbos/precompiles.md#ArbRetryableTx) precompile. - -For details and protocol specification, see [L1 to L2 Messages](../arbos/l1-to-l2-messaging.md). - -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). - - -## Arbitrum to Ethereum Messaging - -Similarly, L2 contracts can send Arbitrary messages for execution on L1. These are initiated via calls to the [`ArbSys`](../arbos/precompiles.md#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](../arbos/l2-to-l1-messaging.md). - -For a demo, see the [Outbox Tutorial](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/outbox-execute). diff --git a/docs/gatewayUML.svg b/docs/gatewayUML.svg deleted file mode 100644 index c93dc0a77e..0000000000 --- a/docs/gatewayUML.svg +++ /dev/null @@ -1,766 +0,0 @@ - - - - - - -UmlClassDiagram - - - -0 - -<<Interface>> -IArbToken - - - -External: -     bridgeMint(account: address, amount: uint256) -     bridgeBurn(account: address, amount: uint256) -     l1Address(): address - - - -1 - -StandardArbERC20 - - - -Public: -    bridgeInit(_l1Address: address, _data: bytes) - - - -1->0 - - - - - -15 - -<<Library>> -BytesParserWithDefault - - - -Internal: -    toUint8(input: bytes, defaultValue: uint8): uint8 -    toString(input: bytes, defaultValue: string): string - - - -1->15 - - - - - -23 - -<<Abstract>> -L2GatewayToken - -Public: -   l2Gateway: address -   l1Address: address - -Internal: -    initialize(_name: string, _symbol: string, _decimals: uint8, _l2Gateway: address, _l1Counterpart: address) -External: -    bridgeMint(account: address, amount: uint256) -    bridgeBurn(account: address, amount: uint256) -Public: -    <<modifier>> onlyGateway() - - - -1->23 - - - - - -2 - -<<Abstract>> -L2ArbitrumGateway - -Public: -   exitNum: uint256 - -Internal: -    <<abstract>> handleNoContract(_l1Token: address, expectedL2Address: address, _from: address, _to: address, _amount: uint256, gatewayData: bytes): (shouldHalt: bool) -    isSenderCounterpartGateway(): bool -    _initialize(_l1Counterpart: address, _router: address) -    createOutboundTx(_l1Token: address, _from: address, _to: address, _amount: uint256, _extraData: bytes): uint256 -    sendTxToL1(_from: address, _l1CallValue: uint256, _data: bytes): uint256 -    outboundEscrowTransfer(_l2Token: address, _from: address, _amount: uint256) -    parseOutboundData(_data: bytes): (_from: address, _extraData: bytes) -    inboundEscrowTransfer(_l2Address: address, _dest: address, _amount: uint256) -    isSenderRouter(): bool -External: -    <<payable>> finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -Public: -    <<payable>> outboundTransfer(_l1Token: address, _to: address, _amount: uint256, _data: bytes): bytes -    <<payable>> outboundTransfer(_l1Token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -    gasReserveIfCallRevert(): uint256 -    getOutboundCalldata(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): (outboundCalldata: bytes) - - - -2->0 - - - - - -25 - -<<Abstract>> -ArbitrumGateway - - - -Internal: -    <<abstract>> inboundEscrowTransfer(_l2Address: address, _dest: address, _amount: uint256) -    _initialize(_counterpartGateway: address, _router: address) -External: -    <<abstract>> outboundTransfer(_l1Token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -    <<abstract>> finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -    inboundEscrowAndCall(_l2Address: address, _amount: uint256, _from: address, _to: address, _data: bytes) -Public: -    <<abstract>> gasReserveIfCallRevert(): uint256 -    <<event>> TransferAndCallTriggered(success: bool, _from: address, _to: address, _amount: uint256, callHookData: bytes) - - - -2->25 - - - - - -27 - -<<Abstract>> -L2ArbitrumMessenger - -Internal: -   arbsysAddr: address - -Internal: -    sendTxToL1(_l1CallValue: uint256, _from: address, _to: address, _data: bytes): uint256 -Public: -    <<event>> TxToL1(_from: address, _to: address, _id: uint256, _data: bytes) - - - -2->27 - - - - - -3 - -L2CustomGateway - -Public: -   l1ToL2Token: mapping(address=>address) - -Internal: -    handleNoContract(_l1Token: address, expectedL2Address: address, _from: address, _to: address, _amount: uint256, gatewayData: bytes): (shouldHalt: bool) -    _calculateL2TokenAddress(l1ERC20: address): address -External: -    calculateL2TokenAddress(l1ERC20: address): address -    registerTokenFromL1(l1Address: address[], l2Address: address[]) -Public: -    initialize(_l1Counterpart: address, _router: address) - - - -3->2 - - - - - -4 - -L2ERC20Gateway - -Public: -   beaconProxyFactory: address - -Internal: -    _calculateL2TokenAddress(l1ERC20: address): address -    handleNoContract(l1ERC20: address, expectedL2Address: address, _from: address, _to: address, _amount: uint256, deployData: bytes): (shouldHalt: bool) -External: -    postUpgradeInit(_beaconProxyFactory: address) -    calculateL2TokenAddress(l1ERC20: address): address -Public: -    initialize(_l1Counterpart: address, _router: address, _beaconProxyFactory: address) -    cloneableProxyHash(): bytes32 -    getUserSalt(l1ERC20: address): bytes32 - - - -4->1 - - - - - -4->2 - - - - - -18 - -BeaconProxyFactory - -Public: -   cloneableProxyHash: bytes32 -   beacon: address - -External: -    initialize(_beacon: address) -    createProxy(userSalt: bytes32): address -Public: -    getSalt(user: address, userSalt: bytes32): bytes32 -    calculateExpectedAddress(user: address, userSalt: bytes32): address -    calculateExpectedAddress(salt: bytes32): address - - - -4->18 - - - - - -5 - -L2GatewayRouter - - - -Internal: -    isSenderCounterpartGateway(): bool -External: -    setGateway(_l1Token: address[], _gateway: address[]) -    setDefaultGateway(newL2DefaultGateway: address) -Public: -    <<payable>> outboundTransfer(_l1Token: address, _to: address, _amount: uint256, _data: bytes): bytes -    initialize(_counterpartGateway: address, _defaultGateway: address) - - - -28 - -<<Abstract>> -GatewayRouter - -Internal: -   ZERO_ADDR: address -   BLACKLISTED: address -Public: -   l1TokenToGateway: mapping(address=>address) -   defaultGateway: address - -Internal: -    _initialize(_counterpartGateway: address, _defaultGateway: address) -    isSenderRouter(): bool -    _calculateL2TokenAddress(l1ERC20: address): address -External: -    <<payable>> finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -Public: -    <<payable>> outboundTransfer(_token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -    <<event>> TransferRouted(token: address, _userFrom: address, _userTo: address, gateway: address) -    <<event>> GatewaySet(l1Token: address, gateway: address) -    <<event>> DefaultGatewayUpdated(newDefaultGateway: address) -    getOutboundCalldata(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -    getGateway(_token: address): (gateway: address) - - - -5->28 - - - - - -6 - -L2WethGateway - -Public: -   l1Weth: address -   l2Weth: address - -Internal: -    handleNoContract(l1ERC20: address, expectedL2Address: address, _from: address, _to: address, _amount: uint256, deployData: bytes): (shouldHalt: bool) -    _calculateL2TokenAddress(l1ERC20: address): address -    outboundEscrowTransfer(_l2TokenAddress: address, _from: address, _amount: uint256) -    inboundEscrowTransfer(_l2TokenAddress: address, _dest: address, _amount: uint256) -    createOutboundTx(_l1Token: address, _from: address, _to: address, _amount: uint256, _extraData: bytes): uint256 -External: -    <<payable>> null() -Public: -    initialize(_l1Counterpart: address, _router: address, _l1Weth: address, _l2Weth: address) -    gasReserveIfCallRevert(): uint256 - - - -6->2 - - - - - -22 - -<<Interface>> -IWETH9 - - - -External: -     deposit() -     withdraw(_amount: uint256) - - - -6->22 - - - - - -7 - -<<Interface>> -ICustomToken - - - -External: -     registerTokenOnL2(l2CustomTokenAddress: address, maxSubmissionCost: uint256, maxGas: uint256, gasPriceBid: uint256) -     transferFrom(sender: address, recipient: address, amount: uint256): bool -     balanceOf(account: address): uint256 - - - -8 - -<<Interface>> -ITradeableExitReceiver - - - -External: -     onExitTransfer(sender: address, exitNum: uint256, data: bytes): bool - - - -9 - -<<Abstract>> -L1ArbitrumExtendedGateway - -Internal: -   USED_ADDRESS: address -Public: -   redirectedExits: mapping(bytes32=>address) - -Internal: -    _initialize(_l2Counterpart: address, _router: address, _inbox: address) -    updateDestination(_exitNum: uint256, _initialDestination: address, _newDestination: address) -External: -    transferExitAndCall(_exitNum: uint256, _initialDestination: address, _newDestination: address, _data: bytes) -Public: -    <<event>> WithdrawRedirected(from: address, to: address, exitNum: uint256, data: bytes, madeExternalCall: bool) -    getCurrentDestination(_exitNum: uint256, _initialDestination: address): address -    encodeWithdrawal(_exitNum: uint256, _initialDestination: address): bytes32 - - - -9->8 - - - - - -10 - -<<Abstract>> -L1ArbitrumGateway - -Public: -   inbox: address - -Internal: -    isSenderCounterpartGateway(): bool -    _initialize(_l2Counterpart: address, _router: address, _inbox: address) -    inboundEscrowTransfer(_l1Token: address, _dest: address, _amount: uint256) -    createOutboundTx(_l1Token: address, _from: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256, _extraData: bytes): uint256 -    sendTxToL2(_user: address, _l2CallValue: uint256, _maxSubmissionCost: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): uint256 -    outboundEscrowTransfer(_l1Token: address, _from: address, _amount: uint256) -    parseOutboundData(_data: bytes): (_from: address, _maxSubmissionCost: uint256, _extraData: bytes) -    isSenderRouter(): bool -External: -    <<payable>> finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -    <<payable>> outboundTransfer(_l1Token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): (res: bytes) -Public: -    gasReserveIfCallRevert(): uint256 -    getCurrentDestination(_exitNum: uint256, _initialDestination: address): address -    parseInboundData(_data: bytes): (_exitNum: uint256, _extraData: bytes) -    getOutboundCalldata(_l1Token: address, _from: address, _to: address, _amount: uint256, _data: bytes): (outboundCalldata: bytes) - - - -9->10 - - - - - -10->25 - - - - - -26 - -<<Abstract>> -L1ArbitrumMessenger - - - -Internal: -    sendTxToL2(_inbox: address, _to: address, _user: address, _l2CallValue: uint256, _maxSubmissionCost: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): uint256 -Public: -    <<event>> TxToL2(_from: address, _to: address, _seqNum: uint256, _data: bytes) - - - -10->26 - - - - - -11 - -L1CustomGateway - -Public: -   l1ToL2Token: mapping(address=>address) -   owner: address - -Internal: -    _calculateL2TokenAddress(l1ERC20: address): address -External: -    calculateL2TokenAddress(l1ERC20: address): address -    registerTokenToL2(_l2Address: address, _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -    forceRegisterTokenToL2(_l1Addresses: address[], _l2Addresses: address[], _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -Public: -    <<event>> TokenSet(l1Address: address, l2Address: address) -    initialize(_l1Counterpart: address, _l1Router: address, _inbox: address, _owner: address) - - - -11->3 - - - - - -11->9 - - - - - -12 - -L1ERC20Gateway - -Public: -   cloneableProxyHash: bytes32 -   l2BeaconProxyFactory: address - -Internal: -    callStatic(targetContract: address, targetFunction: bytes4): bytes -    _calculateL2TokenAddress(l1ERC20: address): address -    getSalt(l1ERC20: address): bytes32 -External: -    postUpgradeInit(_cloneableProxyHash: bytes32, _l2BeaconProxyFactory: address) -    calculateL2TokenAddress(l1ERC20: address): address -Public: -    initialize(_l2Counterpart: address, _router: address, _inbox: address, _cloneableProxyHash: bytes32, _l2BeaconProxyFactory: address) -    getOutboundCalldata(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): (outboundCalldata: bytes) - - - -12->9 - - - - - -13 - -L1GatewayRouter - -Public: -   owner: address -   inbox: address - -Internal: -    sendTxToL2(_user: address, _l2CallValue: uint256, _maxSubmissionCost: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): uint256 -    _setGateways(_token: address[], _gateway: address[], _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -    isSenderCounterpartGateway(): bool -External: -    <<payable>> setDefaultGateway(newL1DefaultGateway: address, _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -    <<payable>> setGateway(_gateway: address, _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -    <<payable>> setGateways(_token: address[], _gateway: address[], _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256): uint256 -    setOwner(newOwner: address) -Public: -    <<payable>> outboundTransfer(_token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -    <<modifier>> onlyOwner() -    initialize(_owner: address, _defaultGateway: address, _whitelist: address, _counterpartGateway: address, _inbox: address) - - - -13->5 - - - - - -13->26 - - - - - -13->28 - - - - - -14 - -L1WethGateway - -Public: -   l1Weth: address -   l2Weth: address - -Internal: -    createOutboundTx(_l1Token: address, _from: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _maxSubmissionCost: uint256, _extraData: bytes): uint256 -    outboundEscrowTransfer(_l1Token: address, _from: address, _amount: uint256) -    inboundEscrowTransfer(_l1Token: address, _dest: address, _amount: uint256) -    _calculateL2TokenAddress(l1ERC20: address): address -External: -    <<payable>> null() -    calculateL2TokenAddress(l1ERC20: address): address -Public: -    initialize(_l1Counterpart: address, _l1Router: address, _inbox: address, _l1Weth: address, _l2Weth: address) - - - -14->9 - - - - - -14->22 - - - - - -16 - -<<Interface>> -ProxySetter - - - -External: -     beacon(): address - - - -17 - -ClonableBeaconProxy - - - -Public: -    constructor() - - - -18->16 - - - - - -19 - -<<Abstract>> -ERC677Token - - - -Private: -    contractFallback(_to: address, _value: uint256, _data: bytes) -    isContract(_addr: address): (hasCode: bool) -Public: -    transferAndCall(_to: address, _value: uint256, _data: bytes): (success: bool) - - - -20 - -<<Interface>> -IERC677 - - - -External: -     transferAndCall(to: address, value: uint256, data: bytes): (success: bool) -Public: -    <<event>> Transfer(from: address, to: address, value: uint256, data: bytes) - - - -19->20 - - - - - -21 - -<<Interface>> -IERC677Receiver - - - -External: -     onTokenTransfer(_sender: address, _value: uint256, _data: bytes) - - - -19->21 - - - - - -23->0 - - - - - -24 - -aeERC20 - - - -Public: -    initialize(name: string, symbol: string, decimals: uint8) - - - -23->24 - - - - - -24->19 - - - - - -30 - -<<Abstract>> -TokenGateway - -Public: -   counterpartGateway: address -   router: address - -Internal: -    <<abstract>> _calculateL2TokenAddress(l1ERC20: address): address -    <<abstract>> isSenderRouter(): bool -    <<abstract>> isSenderCounterpartGateway(): bool -    _initialize(_counterpartGateway: address, _router: address) -External: -    <<abstract>> outboundTransfer(_token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -    <<abstract>> finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -    calculateL2TokenAddress(l1ERC20: address): address -Public: -    <<abstract>> getOutboundCalldata(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -    <<modifier>> onlyCounterpartGateway() -    <<modifier>> onlyRouter() - - - -25->30 - - - - - -28->30 - - - - - -29 - -<<Interface>> -ITokenGateway - - - -External: -     outboundTransfer(_token: address, _to: address, _amount: uint256, _maxGas: uint256, _gasPriceBid: uint256, _data: bytes): bytes -     finalizeInboundTransfer(_token: address, _from: address, _to: address, _amount: uint256, _data: bytes): bytes -     calculateL2TokenAddress(l1ERC20: address): address -Public: -    <<event>> OutboundTransferInitiated(token: address, _from: address, _to: address, _transferId: uint256, _amount: uint256, _data: bytes) -    <<event>> InboundTransferFinalized(token: address, _from: address, _to: address, _transferId: uint256, _amount: uint256, _data: bytes) - - - -30->29 - - - - - diff --git a/docs/getting-started-devs.md b/docs/getting-started-devs.md deleted file mode 100644 index 1b4c6f5c13..0000000000 --- a/docs/getting-started-devs.md +++ /dev/null @@ -1,16 +0,0 @@ -# Start Developing on Arbitrum - -Developing on Arbitrum is as easy as developing on Ethereum — literally! - -To deploy contracts onto an Arbitrum chain, simple set the RPC endpoint (see [Public Chains)](public-chains) of your target Arbitrum chain and deploy using your favorite Ethereum development framework; - -- [Truffle](https://trufflesuite.com/) -- [Hardhat](https://hardhat.org/) -- [Foundry](https://github.com/foundry-rs/foundry) -- [Brownie](https://eth-brownie.readthedocs.io/en/stable/) - -...it all just works! - -For demos of deploying with hardhat see the [Pet Shop](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/demo-dapp-pet-shop) and [Election](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/demo-dapp-election) dapp tutorials. - -For info on new / different behavior between Arbitrum and Ethereum, see [Differences with Ethereum](arbitrum-ethereum-differences). diff --git a/docs/getting-started-users.md b/docs/getting-started-users.md deleted file mode 100644 index fcf04ba14f..0000000000 --- a/docs/getting-started-users.md +++ /dev/null @@ -1,36 +0,0 @@ -# Getting Started - -Welcome to Arbitrum! Here's how to become an Arbitrum user: - -_**Note: before interacting with a mainnet chain, users should familiarize themselves with the risks; see [Mainnet Beta](mainnet-beta)**_. - -#### Connect Your Wallet - -Connect [your wallet](https://portal.arbitrum.one/#wallets) to an Arbitrum chain, adding the chain's RPC endpoint if required. - -#### Get Some Native Currency - -You'll need a chain's native currency to transact. You can either acquire funds directly on an Arbitrum chain, or get funds on a chain's underlying L1 and bridge it across. You can get testnet Ether from the following faucets: - -- [Goerli](https://goerlifaucet.com/) -- [Rinkeby](https://faucet.rinkeby.io/) -- [Nitro Goerli Rollup](https://twitter.com/intent/tweet?text=ok%20I%20need%20@arbitrum%20to%20give%20me%20Nitro%20testnet%20gas.%20like%20VERY%20SOON.%20I%20cant%20take%20this,%20I%E2%80%99ve%20been%20waiting%20for%20@nitro_devnet%20release.%20I%20just%20want%20to%20start%20developing.%20but%20I%20need%20the%20gas%20IN%20MY%20WALLET%20NOW.%20can%20devs%20DO%20SOMETHING??%20%20SEND%20HERE:%200xAddA0B73Fe69a6E3e7c1072Bb9523105753e08f8) - -[Supported centralized exchanges](https://portal.arbitrum.one/#centralizedexchanges) allow you to purchase (mainnet) Ether and withdraw it directly onto Arbitrum one. - -#### Deposit And Withdraw - -To move your Ether and Tokens between Arbitrum and Ethereum chains, visit [bridge.arbitrum.io](https://bridge.arbitrum.io/). - -#### Use L2 Dapps! - -Interacting with Arbitrum chains will feel very similar to using Ethereum, just cheaper and faster! To get a sense of what's out there, you can check out our [portal page](https://portal.arbitrum.one/), where we showcase some of the dApps, wallets, and infrastructure currently live on Arbitrum One. - -#### Build on Arbitrum - -Dapp developers can [build on Arbitrum seamlessly](getting-started-devs) using their favorite Ethereum tooling. - -#### What's Next - -The team working on Arbitrum is always interested and looking forward to engage with its users. -Why not follow us on [Twitter](https://twitter.com/arbitrum) or join our community on [Discord](https://discord.gg/5KE54JwyTs)? \ No newline at end of file diff --git a/docs/inside-anytrust.md b/docs/inside-anytrust.md deleted file mode 100644 index aec816b3e2..0000000000 --- a/docs/inside-anytrust.md +++ /dev/null @@ -1,67 +0,0 @@ -# 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, ensuring that the data will be available so that the rollup protocol can function correctly. - -## 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/docs/inside-arbitrum-nitro/geth-sandwich.png b/docs/inside-arbitrum-nitro/geth-sandwich.png deleted file mode 100644 index 87352bc398..0000000000 Binary files a/docs/inside-arbitrum-nitro/geth-sandwich.png and /dev/null differ diff --git a/docs/inside-arbitrum-nitro/inside-arbitrum-nitro.md b/docs/inside-arbitrum-nitro/inside-arbitrum-nitro.md deleted file mode 100644 index 385ce10eee..0000000000 --- a/docs/inside-arbitrum-nitro/inside-arbitrum-nitro.md +++ /dev/null @@ -1,600 +0,0 @@ ---- - ---- - -# Inside Arbitrum Nitro - -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: - -![img](https://lh4.googleusercontent.com/qwf_aYyB1AfX9s-_PQysOmPNtWB164_qA6isj3NhkDnmcro6J75f6MC2_AjlN60lpSkSw6DtZwNfrt13F3E_G8jdvjeWHX8EophDA2oUM0mEpPVeTlMbsjUCMmztEM0WvDpyWZ6R) - -People 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](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](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 (roughly a week) 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 stakers--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 staked, since as long as it's staked on the current outcome, and there are no conflicting claims, there's no need for other parties to stake / take any action. The protocol allows for these roles to be permissionless in principle; currently on Arbitrum One, validators/stakers are whitelisted (see ["mainnet beta status"](../mainnet-beta.md)). "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. - -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 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 RBlock 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 staker. (More on challenges below.) - -In the normal case, the rollup chain will look like this: - -![img](https://lh3.googleusercontent.com/vv118kJMXj76PG6J-Jv4BC9KTpe72mdfD1uWoqhKXvKKfPWHW6wMMCvJ9KKQx_VXIw34XfzT4yfyNVtQVstYRczLk6kLKvBv8Pbl-0MjSzGxz1Z_8T5Y_6UcDMWpy7_D9PxQYKdT) - -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. - -![img](https://lh3.googleusercontent.com/IKBNeX9IVAD5Vom8vqYER4CEZhTecJJrp51ddlEGYiZrdV6y9zaG0Ip8HuKgfJ-eS9_TN_C2I0EPl-7H5ITRgSQqJONnSE7X0P62sRbGoiv_shmijBxsVDJL9RhWbyDjs2lKxU-M) - -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 the X drawn in it). -- 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 105. 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 staked on wrong RBlocks, and when the dust settles at least four parties will have lost their stakes. 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 stakers, and some will not. Stakers deposit funds that are held by the Arbitrum Layer 1 contracts and will be confiscated if the staker loses a challenge. Nitro chains accept stakes in ETH. - -A single stake can cover a chain of RBlocks. Every staker is staked on the latest confirmed RBlock; and if you’re staked on an RBlock, you can also stake on one successor of that RBlock. So you might be staked on a sequence of RBlocks that represent a single coherent claim about the correct history of the chain. A single stake suffices to commit you to that sequence of RBlocks. - -In order to create a new RBlock, you must be a staker, and you must already be staked on the predecessor of the new RBlock you’re creating. The stake 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 stake amount. Normally this will equal the base stake amount, which is a parameter of the Nitro chain. But if the chain has been slow to make progress lately, the required stake will increase, as described in more detail below. - -The rules for staking are as follows: - -- If you’re not staked, you can stake on the latest confirmed RBlock. When doing this, you deposit the current minimum stake amount. -- If you’re staked on an RBlock, you can also add your stake to any one successor of that RBlock. (The protocol tracks the maximum RBlock number you’re staked on, and lets you add your stake to any successor of that RBlock, updating your maximum to that successor.) This doesn’t require you to place a new stake. - - A special case of adding your stake to a successor RBlock is when you create a new RBlock as a successor to an RBlock you’re already staked on. -- If you’re staked only on the latest confirmed RBlock (and possibly earlier RBlocks), you or anyone else can ask to have your stake refunded. Your staked funds will be returned to you, and you will no longer be a staker. -- If you lose a challenge, your stake is removed from all RBlocks and you forfeit your staked funds. - -Notice that once you are staked on an RBlock, there is no way to unstake. You are committed to that RBlock. Eventually one of two things will happen: that RBlock will be confirmed, or you will lose your stake. The only way to get your stake back is to wait until all of the RBlocks you are staked on are confirmed. - -#### Setting the current minimum stake amount - -One detail we deferred earlier is how the current minimum stake amount is set. Normally, this is just equal to the base stake amount, which is a parameter of the Nitro chain. However, if the chain has been slow to make progress in confirming RBlocks, the stake requirement will escalate temporarily. Specifically, the base stake 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 stakes to try to delay progress (despite the fact that they’re losing those stakes), the stake requirement goes up so that the cost of such a delay attack increases exponentially. As RBlock resolution starts advancing again, the stake 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 staker, and -- all stakers are staked on 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 staker, and - - no staker is staked 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 staker staked on something other than the latest confirmed RBlock), the only way the RBlock can be unresolvable is if at least one staker is staked on it and at least one staker is staked on a different RBlock with the same predecessor. If this happens, the two stakers 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 staked on 93 and Bob is staked 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 staked on 95, and 95 implicitly claims that 92 is the last correct RBlock before it, which implies that 93 must be incorrect.) - -Whenever two stakers are staked on sibling RBlocks, and neither of those stakers 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 stake. The loser will be removed as a staker. - -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 ArbGas units 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 staked, because creating an RBlock requires being staked. 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 stake. 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 stakes in order to defend the correct outcome. -- The _watchtower validator_ strategy never stakes. 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 stake will be willing to intervene in order to take some of the dishonest proposer’s stake, 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 stake (i.e., active and defensive validators) are whitelisted; see ["mainnet beta status"](../mainnet-beta.md). - -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. - -- Some validators will be paid, by the party that created the chain or someone else. A portion of the funds from user transaction fees are used to pay validators on Arbitrum One and Arbitrum Nova. -- Parties who have significant assets at stake 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. - -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 24 hours) 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 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 is 7 million 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 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, ensuring that the data will be available so that the rollup protocol can function correctly. - -### 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/docs/inside-arbitrum-nitro/seq-then-exec.png b/docs/inside-arbitrum-nitro/seq-then-exec.png deleted file mode 100644 index 4e043c12ae..0000000000 Binary files a/docs/inside-arbitrum-nitro/seq-then-exec.png and /dev/null differ diff --git a/docs/intro/glossary.md b/docs/intro/glossary.md deleted file mode 100644 index 4791dbe19a..0000000000 --- a/docs/intro/glossary.md +++ /dev/null @@ -1,98 +0,0 @@ - -# Glossary of Arbitrum Terms - -- **Arbitrum Chain**: A Layer 2 EVM environment running on Ethereum using Arbitrum technologies. Arbitrum Chains come in two forms, rollup and anytrust, depending on a user's needs. - -- **Arbitrum AnyTrust**: Arbitrum protocol in which data availability is managed by a permissioned set of parties; compared to Arbitrum Rollup, not strictly trustless, but offers lower fees (e.g., Arbitrum Nova). - -- **Arbitrum Rollup**: Trustless Arbitrum L2 protocol in which participation is permissionless and underlying layer is used for data availability (e.g., Arbitrum One). - -- **Arbitrum One**: The first Arbitrum Rollup chain running on Ethereum mainnet! (Currently in Beta). - -- **Arbitrum Nova**: The first Arbitrum AnyTrust chain running on Ethereum mainnet! (Currently in Beta). - -- **Arbitrum Full Node**: A party who keeps track of the state of an Arbitrum chain and receives remote procedure calls (RPCs) from clients. Analogous to a non-staking L1 Ethereum node. - -- **ArbOS**: Layer 2 "operating system" that trustlessly handles system-level operations; includes the ability to emulate the EVM. - -- **Arbitrum Classic**: [Old Arbitrum stack](https://github.com/OffchainLabs/arbitrum) that used custom virtual machine ("AVM"); no public Arbitrum chain uses the classic stack as of 8/31/2022 (they instead use Nitro). - -- **Nitro**: Current Arbitrum tech stack; runs a fork of Geth directly on L2 and uses WebAssembly as its underlying VM for fraud proofs. - -- **Data Availability Committee (DAC)**: Permissioned set of parties responsibly for data availability in an AnyTrust chain. - -- **Data Availability Certificate**: Signed promise from a DAC of relevant data's availability as of a given Inbox hash. - -- **Chain state**: A particular point in the history of an Arbitrum Chain. A chain state corresponds to a sequence of assertions that have been made, and a verdict about whether each of those assertions was accepted. - -- **Client**: A program running on a user's machine, often in the user's browser, that interacts with contracts on an Arbitrum chain and provides a user interface. - -- **Rollup Protocol**: Protocol for tracking the tree of assertions in an Arbitrum chain and their confirmation status. - -- **Speed Limit**: Target L2 computation limit for an Arbitrum chain. When computation exceeds this limit, fees rise, ala [EIP-1559](https://notes.ethereum.org/@vbuterin/eip-1559-faq). - -## Proving Fraud - -- **RBlock**: An assertion by an Arbitrum validator that represents a claim about an Arbitrum chain's state. - -- **L2 Block**: Data structure that represents a group of L2 transactions (analogous to L1 blocks). - -- **Challenge**: When two stakers disagree about the correct verdict on an assertion, those stakers can be put in a challenge. The challenge is refereed by the contracts on L1. Eventually one staker wins the challenge. The loser forfeits their stake. Half of the loser's stake is given to the winner, and the other half is burned. - -- **Confirmation**: The final decision by an Arbitrum chain to accept an RBlock as being a settled part of the chain's history. Once an assertion is confirmed, any L2 to L1 messages (i.e., withdrawals) can be executed. - -- **Challenge Period**: Window of time (1 week on Arbitrum One) over which an asserted RBlock can be challenged, and after which the RBlock can be confirmed. - -- **Dissection**: Process by which two challenging parties interactively narrow down their disagreement to a single computational step. - -- **One Step Proof**: Final step in a challenge; a single operation of the L2 VM (Wasm) is executed on L1, and the validity of its state transition is verified. - - -- **Staker**: A party who deposits a stake, in ETH, to vouch for a particular RBlock in an Arbitrum Chain. A party who stakes on a false RBlock can expect to lose their stake. An honest staker can recover their stake once the node they are staked on has been confirmed. - - -- **Active Validator**: A party who makes staked, disputable assertions about the state of the Arbitrum chain; i.e., proposing state updates or challenging the validity of assertions. (Not to be confused with the Sequencer) - -- **Defensive Validator**: A validator that watches the Arbitrum chain and takes action (i.e., stake and challenges) only when and if an invalid assertion occurs. - -- **Watchtower Validator**: A validator that never stakes / never takes on chain action, who raises the alarm (by whatever off-chain means it chooses) if it witnesses an invalid assertion. - -## Cross Chain Communication - -- **Address Alias**: A deterministically generated address to be used on L2 that corresponds to an address on L1 for the purpose of L1 to L2 cross-chain messaging. - -- **Fast Exit / Liquidity Exit**: A means by which a user can bypass Arbitrum's challenge period when withdrawing fungible assets (or more generally, executing some "fungible" L2 to L1 operation); a liquidity provider facilitates an atomic swap of the asset on L2 directly to L1. - -- **Outbox**: An L1 contract responsible for tracking outgoing (Arbitrum to Ethereum) messages, including withdrawals, which can be executed by users once they are confirmed. The outbox stores a Merkle Root of all outgoing messages. - -- **Retryable Ticket**: An L1 to L2 cross chain message initiated by an L1 transaction sent to an Arbitrum chain for execution (e.g., a token deposit). - -- **Retryable Autoredeem**: The "automatic" execution of a retryable ticket on Arbitrum (using provided ArbGas). - -## Token Bridging - -- **Arb Token Bridge**: A series of contracts on Ethereum and Arbitrum for trustlessly moving tokens between the L1 and L2. - -- **Token Gateway**: A pair of contracts in the token bridge — one on L1, one on L2 — that provide a particular mechanism for handling the transfer of tokens between layers. Token gateways currently active in the bridge are the StandardERC20 Gateway, the CustomERC20 Gateway, and the WETH Gateway. - -- **Gateway Router**: Contracts in the token bridge responsible for mapping tokens to their appropriate gateways. - -- **Standard Arb-Token**: An L2 token contract deployed via the StandardERC20 gateway; offers basic ERC20 functionality in addition to deposit / withdrawal affordances. - -- **Custom Arb-Token**: Any L2 token contract registered to the Arb Token Bridge that isn't a standard arb-token (i.e., a token that uses any gateway other than the StandardERC20 Gateway). - -## Transaction Ordering - -- **Batch**: A group of L2 transactions posted in a single L1 transaction by the Sequencer. - -- **Fair Ordering Algorithm**: BFT algorithm in which a committee comes to consensus on transaction ordering; current single-party Sequencer on Arbitrum one will eventually be replaced by a fair-ordering committee. - -- **Forced-Inclusion**: Censorship resistant path for including a message into L2; bypasses any Sequencer involvement. - -- **Sequencer**: An entity (currently a single-party on Arbitrum One) given rights to reorder transactions in the Inbox over a small window of time, who can thus give clients sub-blocktime soft confirmations. (Not to be confused with a validator) - -- **Soft Confirmation**: A semi-trusted promise from the Sequencer to post a user's transaction in the near future; soft-confirmations happen prior to posting on L1, and thus can be given near-instantaneously (i.e., faster than L1 block times) - -- **Slow Inbox**: Sequence of L1 initiated message that offer an alternative path for inclusion into the fast Inbox. - -- **Fast Inbox**: Contract that holds a sequence of messages sent by clients to the contracts on an Arbitrum Chain; message can be put into the Inbox directly by the Sequencer or indirectly through the slow inbox. diff --git a/docs/intro/intro.md b/docs/intro/intro.md deleted file mode 100644 index 78c5516e1d..0000000000 --- a/docs/intro/intro.md +++ /dev/null @@ -1,61 +0,0 @@ -# A Gentle Introduction to Arbitrum - -#### Q: Hello! What’s Arbitrum? -Hi! Arbitrum is a technology suite designed to scale Ethereum. You can use Arbitrum chains to do all things you do on Ethereum — use Web3 apps, deploy smart contracts, etc., but your transactions will be cheaper and faster. Our flagship product — Arbitrum Rollup — is an Optimistic rollup protocol that inherits Ethereum-level security. - -#### Q: What, what’s “Ethereum”? What's a “smart contract”? Where am I? -If you aren’t yet familiar with the Ethereum ecosystem, you can check out [ethereum.org](https://ethereum.org/en/learn/) for an intro. Come back whenever you're ready, no rush. - -#### Q: You said Arbitrum exists to “scale” Ethereum; why does Ethereum need this help? Is there something wrong with Ethereum? -Ethereum is awesome; on its own, however, it’s also very limited. The Ethereum blockchain only allows about 20-40 transactions per second (TPS) (that’s in total, for all Ethereum users); when the limit is reached, users are forced to compete against each other for their transactions to be included, which causes fees to go up. - -#### Q: Why does Ethereum have such low TPS? -This was a deliberate decision in Ethereum’s design. Ethereum requires that its nodes (computers running the Ethereum software) have a way of coming to consensus on the current state of things; the way they do this is by processing every transaction in Ethereum’s history; i.e., if you’ve ever used Ethereum, every Ethereum full node has a copy of your transactions in its blockchain ledger. - -One of the Ethereum community’s precepts, being an open, decentralized, peer to peer system, is that it should be reasonably accessible for anyone to run an Ethereum node and validate the chain for themselves; i.e., if it gets too expensive (in terms of hardware requirements / computational resources), this undercuts the fundamental goal of decentralization. -The combination of these two factors — every node has to process every transaction, and we want it to be relatively feasible to run a node — means Ethereum transaction throughput has to be capped fairly low. - - -#### Q: And Arbitrum Rollup fixes this? -Arbitrum rollup fixes this! The basic idea is this: an Arbitrum Rollup chain runs as a sort of sub-module within Ethereum. Unlike regular, layer 1 ( “L1”) Ethereum transactions, we don’t require Ethereum nodes to process every Arbitrum transaction; rather, Ethereum adopts an [“innocent until proven guilty"](https://insights.deribit.com/market-research/making-sense-of-rollups-part-2-dispute-resolution-on-arbitrum-and-optimism/) attitude to Arbitrum. Layer 1 initially “optimistically assumes” activity on Arbitrum is following the proper rules. If a violation occurs (i.e., somebody claims “now I have all of your money”), this claim can be disputed back on L1; fraud will be proven, the invalid claim disregarded, and the malicious party will be financially penalized. - -This ability to adjudicate and prove fraud on L1 is Arbitrum’s key, fundamental feature, and is how and why the system inherits Ethereum’s security. - -#### Q: So we can use Ethereum to prove fraud on Arbitrum; cool! But if fraud is committed, can we be absolutely sure that we'll be able to prove it? -Yes, indeed we can be. This is where the “rollup” part comes in. The data that gets fed into an Arbitrum Rollup chain (i.e., user’s transaction data) is posted directly on Ethereum. Thus, as long as Ethereum itself is running securely, anybody who’s interested has visibility into what’s going on in Arbitrum, and has the ability to detect and prove fraud. - -#### Q: Who actually does this work (of checking for fraud, proving it, etc?) -The parties who move the Arbitrum chain state forward on L1 — i.e., making claims about the chain’s state, disputing other’s claims, etc. — are called validators. -In practice, we don’t expect the average Arbitrum user to be interested in running a -validator, just like the average Ethereum user typically doesn’t run their own layer 1 staking node. The crucial property, however, is that anybody can; becoming an Arbitrum validator requires no special permission ([post-mainnet beta](../mainnet-beta.md)), only that a user runs the [open source validator software](https://github.com/OffchainLabs/nitro) (and stakes Ether when/if they need to take action). - -Additionally, as long as there’s even just one honest validator, the chain will remain secure; i.e., it only takes one non-malicious fraud-prover to catch any number of malicious trouble-makers. These properties together make the system “trustless”; users are not relying on any special designated party for their funds to be secure. - - -#### 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](../proving/challenge-manager.md). - -#### Q: This dispute game obviously takes some time; does this impose any sort of delay on Arbitrum users' transactions? -The only delay that's felt by a user is in "withdrawing" — moving their funds from Arbitrum back to Ethereum; if users are withdrawing directly from Arbitrum to Ethereum, they must typically wait 1 week before receiving their funds on L1. If users use a fast-bridge application, however, they can bypass this delay period entirely (likely for a small fee). Anything else a user does — i.e., depositing funds from Ethereum onto Arbitrum, or using a dapp deployed on an Arbitrum chain — doesn't incur this delay period. - - -#### Q: Okay, so backing up: the “optimistic execution” part is how and why Arbitrum is able to offer low fees, yes? -Primarily, yes, this is the heart of where the savings come from. However, there are a number of other means by which Arbitrum alleviates the burden on L1, all of which translate to lower transaction costs for end users. -For one, Arbitrum transactions are submitted on the L1 in batches; typically, a single batch (submitted in a single L1 transaction) will contain several hundred L2 transactions. Batching amortizes the overhead cost of interacting with the L1, and thus offers significant savings over posting individual transactions at a time. Furthermore, the transaction data is posted on L1 in compressed form (and only decompressed within the L2 environment), further minimizing the transaction’s L1 footprint. - -#### Q: As far as the experience of using Arbitrum: when you said that it’s very similar to using Ethereum… -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](../arbos/geth.md) — 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 it sounds like Arbitrum Rollup is an ideal solution that solves any and all scaling problems…? -Arbitrum Rollup is very awesome and cool; its design is geared heavily toward avoidance of introducing any centralization or trust assumptions, and it is thus a clear, strict net-win for the Ethereum ecosystem. Decentralization, however, comes at a (literal) price, and not all applications and users necessarily want or need to pay that price. For dapp use-cases with different security considerations, different tools in the Arbitrum suite are appropriate; i.e., Arbitrum AnyTrust chains! - -#### Q: What’s an AnyTrust chain? -An Arbitrum AnyTrust chain doesn’t have the same decentralization / trustlessness / permissionless security guarantees of a Rollup chain, and thus can offer lower fees. Rollup and AnyTrust are similar in many ways, though have one key difference: whereas in Rollup, all data is posted on L1 (which allows anyone to permissionless join as a validator), in AnyTrust, data is managed off-chain. In the case of a challenge, an AnyTrust chain reverts back to “rollup mode”; the security assumption here is that at least 2 of the committee members are honest (i.e., they will provide the data when it’s necessary). Keeping the data off-chain in the happy/common case means the system can charge the user significantly lower fees. -For applications that require high transaction throughput and don’t require the full decentralization that rollups provide, AnyTrust could be a sensible tradeoff. - -#### Q: So there's more than one Arbitrum chain out there? -Yep! The fact that multiple chains can run in parallel is a crucial perk to off-chain scaling technology. Currently, on Ethereum mainnet, there are 2 Arbitrum chains: one Arbitrum Rollup chain, called ["Arbitrum One,"](https://portal.arbitrum.one/) and one AnyTrust chain, called ["Nova"](https://nova.arbitrum.io/); users and developers can pick whatever suits their security / transaction cost needs. diff --git a/docs/mainnet-beta.md b/docs/mainnet-beta.md deleted file mode 100644 index f45d730e53..0000000000 --- a/docs/mainnet-beta.md +++ /dev/null @@ -1,19 +0,0 @@ -# What is "Mainnet Beta"? - -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](./inside-anytrust.md) chain! We're sure you're (almost) as excited as we are; here's what you need to know before using the system: - -### Some Words of Caution - -- **Why Mainnet "Beta?"; Current De/Centralization State**: Arbitrum One and Nova have their full feature sets that you'll see described in these docs fully implemented and live: fraud proofs, permissionless usage, you name it. That said, given that Arbitrum is still a new, cutting edge, and complex system, we currently maintain various levels of control over the chains while they're still in this early Beta phase; this includes contract upgradeability, the ability to pause the system, and validator whitelisting. We believe temporarily maintaining these capabilities is the only responsible way to launch while we continue to harden our system; as we progressively decentralized, these controls will be phased and eventually eliminated entirely. - - To track the status of the Arbitrum chains' decentralization, you can follow updates from [us](https://offchain.medium.com/) (obviously) or, for Arbitrum One, check out [L2Beat](https://l2beat.com/projects/arbitrum/), who have thus far done a great job of thoroughly and responsibly providing information on Arbitrum Rollup and other L2 systems. - -- **Why Mainnet "Beta?": Undiscovered Bugs 😱**: Despite the fact that we at Offchain Labs have thoroughly thought through the many design choices in our protocol, have been as careful implementing them as we could be, have been audited by several independent firms, have a team of engineers that are all very smart and cool, etc.,... there remains a non-zero chance that our codebase contains some undiscovered vulnerabilities that put user funds at risk. Users should carefully factor in this risk in their decision to use Arbitrum one / in deciding how much of their value to entrust into the system. (And should you yourself happen to discover one such bug, might we kindly direct you to our [bug bounty program](https://immunefi.com/bounty/arbitrum/)?) - -- **General Caution: Beware of Scams and Rugs**: Arbitrum, like Ethereum, is permissionless; on both platforms, anybody can deploy any smart contract code they want. Users should treat interacting with contracts on Arbitrum exactly as they do with Ethereum, i.e., they should only do so if they have good reason to trust that the application is secure. - -## Getting Started - -..okay, with that out of the way, let's talk about getting started! To get a sense of what's out there, you can check out our [portal page](https://portal.arbitrum.one/), where we showcase some of the dApps, wallets, and infrastructure currently live. - -See [Public Chains](./public-chains.md) for more info on getting started. diff --git a/docs/migration/dapp_migration.md b/docs/migration/dapp_migration.md deleted file mode 100644 index 4a0b068ab2..0000000000 --- a/docs/migration/dapp_migration.md +++ /dev/null @@ -1,81 +0,0 @@ -# Nitro Migration Notes for Solidity Devs: Living Document - -The following is a summary of changes in the Arbitrum One chain's 8/31/22 Nitro upgrade that dapp devs should be aware of. - -_Last Updated 31 Aug 2022_ - -## Cool New Stuff - -For starters, here's a sampling of exciting perks dapps get with the Nitro upgrade: - -- **Ethereum L1 Gas Compatibility 🥳**: Gas pricing and accounting for EVM operations is be perfectly in line with L1; no more ArbGas. - - Although in Nitro L2 opcodes are now priced the exact same as Eth L1 - you still have to pay for the L1 calldata (which gets represented as L2 gas units). Your transactions' gas used won't be the same as Eth L1 but during contract execution the opcode cost and `gasleft` interactions are now better aligned with Eth L1. Read more [here](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9). - -- **Safer Retryable tickets 🥳**: Retryable tickets' submission cost is collected in the L1 Inbox contract; if the submission cost is too low, the transaction will simply revert on the L1 side, eliminating the [failure mode](https://developer.offchainlabs.com/docs/l1_l2_messages#important-note-about-base-submission-fee) in which a retryable ticket fails to get created. - -- **Calldata compression 🥳**: Compression takes place protocol level; dapps don't need to change anything, data will just get cheaper! (You are charged even less if your calldata is highly compressible with brotli.) - -- **Support for All Ethereum L1 precompiles 🥳**: (`blake2f`, `ripemd160`, etc) - -- **Tighter Synchronization with L1 Block Numbers 🥳**: L1 block numbers (accessed via `block.number` on L2) are updated more frequently in Nitro than in Arbitrum classic; expect them to be nearly real-time/ in sync with L1. - -- **Frequent Timestamps 🥳**: Timestamps (accessed via `block.timestamp` on L2) are updated every block based on the sequencer’s clock; i.e., it is no longer linked to the timestamp of the last L1 block. - -- **L2 Block hash EVM Consistency 🥳**: L2 block hashes take the same format as on Ethereum (if you query it from the ArbSys precompile, not the one in `block.hash(uint256)`). - -- **Geth tracing 🥳**: `debug_traceTransaction` RPC endpoint is supported; this includes tracing of ArbOS internal bookkeeping actions. - -## Breaking changes - -#### Dapps - -- **Gas Accounting**: It's now consistent with the L1 EVM; L2 gas usage will change due to different accounting from ArbGas. Any hard-coded gas values should be changed accordingly (the same applies to any gas amount used in conjunction with `gasleft`). That said, you shouldn't be hard-coding any gas values anyway, just like you shouldn't in L1 Ethereum, since both the L1 and L2 gas schedule may change in the future. - - Although in Nitro L2 opcodes are now priced the exact same as Eth L1 - you still have to pay for the L1 calldata (which gets represented as L2 gas units). Your transactions' gas used won't be the same as Eth L1 but during contract execution the opcode cost and `gasleft` interactions are now better aligned with Eth L1. Read more [here](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9). - -- **No more storage gas**: there is no more concept of a separate pool of storage gas, and opcodes are priced identically to the L1 EVM. - -- **New L2 to L1 event signature**: The function signature for the [L2 to L1 event](../../contracts/src/precompiles/ArbSys.sol#L110) emitted by ArbSys has now changed. - -- **Lower contract code size limit**: Contracts of up to 48KB were deployable, but now only up to 24KB are deployable (as specified in [EIP 170](https://eips.ethereum.org/EIPS/eip-170)). Previously deployed contracts above the limit will be maintained (but contracts deployed by these legacy contracts are capped by the new size). - -- **Retryable Tickets**: - - The submission cost is now enforced in the L1 inbox and checked against the L1 transaction's `msg.value`; contracts shouldn't rely on funds pooled in the L2 destination to cover this cost. - - The current submission price is now not available in the L2 ArbRetryableTx precompile, instead it can be queried in the L1 Delayed Inbox [`calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)`](https://github.com/OffchainLabs/nitro/blob/01412b3cd0fca28bf9931407ca1ccfeb8714d478/contracts/src/bridge/Inbox.sol#L262) - - For the redemption of retryable tickets, the calculation of the L2 transaction ID changed and differs between different redeem attempts (i.e. after failed attempts). See [arbitrum-sdk](https://github.com/offchainlabs/arbitrum-sdk/tree/c-nitro-stable) for a reference implementation on the new client-side flow. - - A retryable ticket now isn't redeemed in the same transaction as when the `redeem` function was called. The user's transaction causes the retryable to be scheduled to be executed after the current transaction is complete. More information on this available in [here](../arbos/arbos.md#redeeming-a-retryable). - - Auto-redeem will not be created if the user does not have enough balance to pay for `gasFeeCap * gasLimit` (meaning you can no longer set a max gas fee cap). - - Deposited gas will be refunded to `excessFeeRefundAddress` if it cannot create an auto-redeem. - - The user will be refunded the submission cost of their retryable if it is auto-redeemed. - - The lifecycle of retryable tickets are now tracked differently. Previously there was a retryable ticket ID, which could be used to deterministically generate the expected tx hash. In Nitro, you instead have a retryable creation tx hash (which can be retrieved by the SDK's `L1ToL2Message.retryableCreationId` or calculated by calling [L1ToL2Message.calculateSubmitRetryableId](https://github.com/OffchainLabs/arbitrum-sdk/blob/105bf73cb788231b6e63c510713f460b36699fcd/src/lib/message/L1ToL2Message.ts#L109-L155)). This value does not directly map into an expected tx hash where it was redeemed. You need to instead listen to the [RedeemScheduled](https://github.com/OffchainLabs/nitro/blob/ec70ed7527597e7e1e8380a59c07e8449885e408/contracts/src/precompiles/ArbRetryableTx.sol#L85-L93) event, which tells you the expected `retryTxHash` of that attempt. - - The retryTxHash is no longer deterministic solely based on the retryable ticket id; it is now a hash of the transaction input like a normal transaction (following the [Typed Tx Envelope standard](https://eips.ethereum.org/EIPS/eip-2718)) - - If the retryable `to` address is set to the zero address, it will now function as a contract deployment. -- **Arbitrum blockhash**: `blockhash(x)` returns a cryptographically insecure, pseudo-random hash for `x` within the range `block.number - 256 <= x < block.number`. If `x` is outside of this range, `blockhash(x)` will return `0`. This includes `blockhash(block.number)`, which always returns `0` just like on Ethereum. The hashes returned do not come from L1. -- **ArbSys precompile**: `ArbSys.getTransactionCount` and `ArbSys.getStorageAt` are removed in nitro - -#### Protocol Contracts - -- **New Contract Deployments**: For the Nitro upgrade, these contracts were redeployed on L1 to new addresses: - - SequencerInbox - - RollupCore - - Outbox - - Bridge - - bridge.sol contract will be redeployed and not the whole Bridge. - - The [bridge contract address](https://etherscan.io/address/0x011B6E24FfB0B5f5fCc564cf4183C5BBBc96D515) was be changed. - -For addresses of protocol contracts, see [Useful Addresses](../useful-addresses.md). - -Also, worth mentioning that the address of the Delayed Inbox contract and Token Bridge contracts (Router and Gateways) weren't changed after the migration. - -- **Sequencer Inbox changes**: The Sequencer inbox has a new interface and requires a new approach to determining a transaction's inclusion on L1 (see "Batch Info In Receipts" below). - -- **Outbox Changes**: The Outbox has a new (simplified!) architecture; in short, all outgoing messages will be included in a single Merkle tree (opposed to Arbitrum classic, in which many outbox entries, each with its own Merkle root). See [arbitrum-sdk](https://github.com/offchainlabs/arbitrum-sdk/tree/c-nitro) for a reference implementation on how to handle the new flow of interacting with the outbox. - -#### RPCs - -- **No Parity tracing**: Initially `trace_filter` RPCs won't be available; they will be in the coming months. (Note that the Geth tracing APIs _are_ available). - -- **Gas Info in Transaction Receipts**: Arbitrum transaction receipts return data about gas in a new format; receipts will have `gasUsed` (total) and `gasUsedForL1` fields (instead of the `feeStats` field in Arbitrum classic). - -- **Batch Info In Receipts**: Arbitrum transaction receipts no longer include the `l1SequenceNumber` field; the `findBatchContainingBlock` or `getL1Confirmations` methods in the [NodeInterface precompile](../../contracts/src/node-interface/NodeInterface.sol) can be used to determine a transaction's inclusion in L1. - -- **Estimate Retryable Ticket**: Use `eth_estimateGas` on `NodeInterface.estimateRetryableTicket` to estimate the gas limit of a retryable; the function itself no longer return the gas used and gas price. The gas price can be estimated using `eth_gasPrice`. diff --git a/docs/migration/state-migration.md b/docs/migration/state-migration.md deleted file mode 100644 index 8568dd3d1b..0000000000 --- a/docs/migration/state-migration.md +++ /dev/null @@ -1,51 +0,0 @@ -# Migrating State and History from a Classic Arbitrum Node to Nitro - -Nitro software has the option to initialize a chain with state imported from a classic arbitrum node. Rinkeby Testnet, and later Arbitrum One, use this option. Entire state of the rollup is preserved when it is upgraded from classic to nitro. - -When importing history - Nitro's genesis block will be the block that follows the imported history, and not block 0. - -The recommended way to initialize a nitro node is to point it to a pre-initialized database using the `--init.url` option. This documentation is for users who wish to create the full state on their own classic node. - -## Exporting Data from a Classic Arbitrum Node - -Launch node with the option `--node.rpc.nitroexport.enable=true` note: this is only recommended for nodes with no public/external interfaces. All exported data will be written to directory "nitroexport" under the classic instance directory - e.g. `${HOME}/.arbitrum/rinkeby/nitroexport`. -Make sure the classic node has read the entire rollup state. - -**Important Note:** Exporting the state on your own classic node should produce the same state as using files supplied by offchain labs (e.g. the same genesis blockhash). However, multiple exports of the same state will not necessarily create identical intermediate files. For example - state export is done in parallel, so order of entries in the file is not deterministic. - -### Exporting Block & Transaction History - -These are block-headers, transactions and receipts executed in the classic node. Nitro node uses the history to be able to answer simple requests, like eth_getTransactionReceipt, from the classic history. The last block in the chain is the only one that affects the genesis block: timestamp is copied from the last block, and parentHash is taken from the last block's blockHash. - -- RPC call `arb_exportHistory` with parameter `"latest"` will initiate history export. It will return immediately. -- `arb_exportHistoryStatus` will return the latest block exported, or an error if export failed. -- Data will be stored in dir `nitroexport/nitro/l2chaindata/ancient`. - -### Exporting Outbox Messages - -This data does not impact consensus and is optional. It allows a nitro node to provide the information required when redeeming a withdrawal made on the classic rollup. - -- RPC call `arb_exportOutbox` with parameter `"0xffffffffffffffff"` will initiate block export. It will return immediately. -- `arb_exportOutboxStatus` will return the latest outbox batch exported, or an error is export failed. -- Data will be stored in dir `nitroexport/nitro/classic-msg`. - - -### Exporting State - -Rollup state is exported as a series of json files. State read from these json files will be added to nitro's genesis block. - -- RPC call `arb_exportState` with parameter `"latest"` will initiate stet export. Unless disconnected - this will only return after state export is done. -- Data will be created in dir `nitroexport/state//`. - - -## Running Nitro Node Initialization - -State Import requires more resources than normal run of a nitro node. - -- Place l2chaindata and classic-msg (optional) directories in nitro's instance directory - e.g. ${HOME}/.arbitrum/rinkeby-nitro/ -- Launch the node with argument `--init.import-file=/path/to/state/index.json` - -### Other Nitro Options -- `--init.accounts-per-sync` allows the node to make partial database writes to hard-disk during initialization, allowing memory to be freed. This should be used if memory load is very high. A reasonable initial value to try would be 100000. Systems with constrained memory might require a lower value. -- `--init.then-quit` causes the node to quit after initialization is done. -- `--init.force` for an already-initialized node, forces the node to recalculate nitro's genesis block. If the genesis blockhash does not match what's in the database - the node will panic. diff --git a/docs/node-running/node-providers.md b/docs/node-running/node-providers.md deleted file mode 100644 index db168d3cfa..0000000000 --- a/docs/node-running/node-providers.md +++ /dev/null @@ -1,59 +0,0 @@ -To interact with Arbitrum One and the RinkArby testnet, you can rely on the same popular node providers that you are already using on Ethereum. There are multiple providers that may help you build on Arbitrum, here is a list of a couple of them. - -## [Alchemy](https://alchemy.com/?a=arbitrum-docs) - -Alchemy provides endpoints for both Arbitrum One (Mainnet) and Rinkeby testnet. To use these RPC endpoints, you need to create a free account and set up an **_Alchemy API key_** to authenticate your requests. For detailed steps of how to create one, see the [Alchemy's documentation](https://docs.alchemy.com/alchemy/introduction/getting-started#1.create-an-alchemy-key). - -Next, depending one which network you want to interact with, you can choose between the two RPC endpoints provided by Alchemy and interact with them directly in the command line or point your [wallet's](https://portal.arbitrum.one/#wallets) RPC endpoints to them. For more information on Arbitrum JSON-RPC API methods supported by Alchemy, check out the [official documentation](https://docs.alchemy.com/alchemy/apis/arbitrum). - -Alchemy provides 300M compute units per month for free, or the equivalent of 30M getLatestBlock requests. Additional usage requires upgrading to a Growth Tier (\$49 / month). - -## [Infura](https://infura.io/) - -Infura is a node service provider that provides access to Arbitrum One (Mainnet), Goerli testnet and Rinkeby testnet. To start interacting with either of these networks, you need to [set up an Infura account](https://infura.io/register) and create a new key. For detailed steps, check out Infura's [documentation](https://docs.infura.io/infura/networks/arbitrum). - -Infura provides access to Arbitrum across all their subscription plans (Core Free, Developer, Team & Growth). See [pricing & daily request rate limits](https://infura.io/pricing). - -## [QuickNode](https://www.quicknode.com/) - -QuickNode supports Arbitrum One Mainnet, Goerli and Rinkeby testnet, as well as Arbitrum Nova Mainnet, and allows users to interact with these chains. - -For a full guide of how to use an Arbitrum node on QuickNode, see the [QuickNode's Arbitrum](https://www.quicknode.com/docs/arbitrum) and [Quicknode's Arbitrum Nova documentation](https://www.quicknode.com/docs/arbitrum-nova). - -[Quicknode](https://www.quicknode.com/accounts/new-signup) provides 10 million API credits per month for Arbitrum for free. - -## [Moralis](https://moralis.io/) - -Moralis allows Arbitrum users and developers to connect to both Arbitrum One (Mainnet) and Rinkeby testnet. To do so, you need to create an account and choose the node you wish to connect to. You will then be presented with a pair of URLs (Arbitrum Mainnet and Testnet) with which you can interact with directly in the command line or point your [wallet's](https://portal.arbitrum.one/#wallets) RPC endpoints to them. For full guide and more explanation, see the [Moralis's documentation](https://moralis.io/full-guide-how-to-connect-to-arbitrum-nodes/). - -## [Ankr](https://www.ankr.com/) - -Ankr provides access to both Arbitrum One (Mainnet) and Rinkeby Testnet. After creating an Ankr account, you will be presented with a pair of URLs which you can use to connect to any of our networks. For more details, see the [Ankr's documentation](https://docs.ankr.com/blockchains/arbitrum/develop-on-arbitrum). - -## [BlockVision](https://blockvision.org/) - -BlockVision provides Arbitrum One mainnet and Rinkeby node services. See [BlockVision Dashboard](https://dashboard.blockvision.org/connect). - -## [GetBlock](https://getblock.io/) - -GetBlock is a service that provides instant API connection to full nodes of many leading blockchain networks, including Arbitrum One (Mainnet) available for shared and dedicated nodes and Rinkeby Testnet available for dedicated nodes. - -For detailed steps on how to set up an Arbitrum node, check [GetBlock’s documentation](https://getblock.io/docs/) - -GetBlock’s pricing for Arbitrum starts from \$6 for 500K requests. Also, GetBlock offers a free plan for testing. - -## [Chainstack](https://chainstack.com/) - -Chainstack currently supports the Arbitrum Goerli testnet and Arbitrum One Mainnet (including Archive node support). - -See [documentation](https://chainstack.com/build-better-with-arbitrum/) for more info. - -## [NOWNodes](https://nownodes.io/?utm_source=arbitrum&utm_medium=referral&utm_campaign=arbitrum) - -[NOWNodes](https://nownodes.io/?utm_source=arbitrum&utm_medium=referral&utm_campaign=arbitrum) is a blockchain-as-a-service that provides access to almost 60 full nodes and explorers via API. Through the usage of NOWNodes you can easily connect to [full Arbitrum nodes](https://nownodes.io/nodes/arbitrum?utm_source=arbitrum&utm_medium=referral&utm_campaign=arbitrum) and block explorer. - -In order to establish a connection, you just need to utilize specific codes and commands that can be found in [the official documentation](https://documenter.getpostman.com/view/13630829/TVmFkLwy?utm_source=arbitrum&utm_medium=referral&utm_campaign=arbitrum). - - -Find list of node services [here](https://nownodes.io/nodes?utm_source=arbitrum&utm_medium=referral&utm_campaign=arbitrum) - diff --git a/docs/node-running/running-a-classic-node.md b/docs/node-running/running-a-classic-node.md deleted file mode 100644 index ee4da62257..0000000000 --- a/docs/node-running/running-a-classic-node.md +++ /dev/null @@ -1,86 +0,0 @@ -# Running a Classic Node - -Note: If you’re interested in accessing the Arbitrum network but you don’t want to setup your own node, see our [Node Providers](./node-providers.md) to get RPC access to fully-managed nodes hosted by a third party provider - -### Do You Need To Run A Classic Node? - -Arbitrum One and Arbitrum Rinkeby testnet have both been upgraded to Nitro, the latest Arbitrum tech stack; "Arbitrum Classic" is our term for the old, pre-Nitro tech stack. -The Nitro node databases have the raw data of all blocks, including pre-Nitro -blocks. However, Nitro nodes cannot execute anything on pre-Nitro blocks. - -Arbitrum Nova started as a Nitro chain, so has no classic blocks. - -The following commands require an Arbitrum Classic node to execute data -on pre-Nitro blocks. - -* `eth_call` -* `eth_estimateGas` -* `eth_getBalance` -* `eth_getCode` -* `eth_getTransactionCount` -* `eth_getStorageAt` - -### Required Artifacts - -- Latest Docker Image: offchainlabs/arb-node:v1.4.5-e97c1a4 - -### Required parameters - -- `--l1.url=` - - Must provide standard Ethereum node RPC endpoint. -- `--node.chain-id=` - - Must use `42161` for Arbitrum One, or `421611` for Arbitrum Rinkeby Testnet - -### Important ports - -- RPC: `8547` -- WebSocket: `8548` - -### Putting it all together - -- When running docker image, an external volume should be mounted to persist the database across restarts. The mount point should be `/home/user/.arbitrum/mainnet` or `/home/user/.arbitrum/rinkeby` depending on what chain you are connecting to. - - Here is an example of how to run arb-node for mainnet (only good for archive requests on pre-Nitro blocks, so probably want to enable archive as well): - ```shell - docker run --rm -it -v /some/local/dir/arbitrum-mainnet/:/home/user/.arbitrum/mainnet -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/arb-node:v1.4.5-e97c1a4 --l1.url=https://l1-node:8545 --node.chain-id=42161 --l2.disable-upstream - ``` - - Here is an example of how to run arb-node for rinkeby (only good for archive requests on pre-Nitro blocks, so probably want to enable archive as well): - ```shell - docker run --rm -it -v /some/local/dir/arbitrum-rinkeby/:/home/user/.arbitrum/rinkeby -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/arb-node:v1.4.5-e97c1a4 --l1.url=https://l1-rinkeby-node:8545 --node.chain-id=421611 - ``` - -### Note on permissions - -- The Docker image is configured to run as non-root UID 1000. This means if you are running in Linux and you are getting permission errors when trying to run the docker image, run this command to allow all users to update the persistent folders - - For mainnet: - ```shell - mkdir /some/local/dir/arbitrum-mainnet - chmod -fR 777 /some/local/dir/arbitrum-mainnet - ``` - - For Rinkeby: - ```shell - mkdir /some/local/dir/arbitrum-rinkeby - chmod -fR 777 /some/local/dir/arbitrum-rinkeby - ``` - -### Optional parameters - -- `--core.cache.timed-expire` - - Defaults to `20m`, or 20 minutes. Age of oldest blocks to hold in cache so that disk lookups are not required -- `--node.rpc.max-call-gas` - - Maximum amount of gas that a node will use in call, default is `5000000` -- `--core.checkpoint-gas-frequency` - - Defaults to `1000000000`. Amount of gas between saving checkpoints to disk. When making archive queries node has to load closest previous checkpoint and then execute up to the requested block. The farther apart the checkpoints, the longer potential execution required. However, saving checkpoints more often slows down the node in general. -- `--node.cache.allow-slow-lookup` - - When this option is present, will load old blocks from disk if not in memory cache - - If archive support is desired, recommend using `--node.cache.allow-slow-lookup --core.checkpoint-gas-frequency=156250000` -- `--node.rpc.tracing.enable` - - Note that you also need to have a database populated with an archive node if you want to trace previous transactions - - This option enables the ability to call a tracing api which is inspired by the parity tracing API with some differences - - Example: `curl http://arbnode -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"arbtrace_call","params":[{"to": "0x6b175474e89094c44da98b954eedeac495271d0f","data": "0x70a082310000000000000000000000006E0d01A76C3Cf4288372a29124A26D4353EE51BE"},["trace"], "latest"],"id":67}'` - - The `trace_*` methods are renamed to `arbtrace_*`, except `trace_rawTransaction` is not supported - - Only `trace` type is supported. `vmTrace` and `stateDiff` types are not supported - - The self-destruct opcode is not included in the trace. To get the list of self-destructed contracts, you can provide the `deletedContracts` parameter to the method - -### Arb-Relay - -- Arbitrum classic does not communicate with Nitro sequencer, so classic relay is no longer used. \ No newline at end of file diff --git a/docs/node-running/running-a-node.md b/docs/node-running/running-a-node.md deleted file mode 100644 index 25b9754139..0000000000 --- a/docs/node-running/running-a-node.md +++ /dev/null @@ -1,118 +0,0 @@ - -# Running a Node - -Note: If you’re interested in accessing an Arbitrum chain, but you don’t want to set up your own node, see our [Node Providers](./node-providers.md) to get RPC access to fully-managed nodes hosted by a third party provider - -### Required Artifacts - -- Latest Docker Image: `offchainlabs/nitro-node:v2.0.8-5b9fe9c` - -- Arbitrum One Nitro Genesis Database Snapshot - - Use the parameter `--init.url="https://snapshot.arbitrum.io/mainnet/nitro.tar"` on first startup to initialize Nitro database - - If running more than one node, easiest to manually download image from https://snapshot.arbitrum.io/mainnet/nitro.tar and host it locally for your nodes - - Or use `--init.url="file:///path/to/snapshot/in/container/nitro.tar"` to use a local snapshot archive - - sha256 checksum: `a609773c6103435b8a04d32c63f42bb5fa0dc8fc38a2acee4d2ab2d05880205c` - - size: 33.5573504 GB - -- Rinkeby Nitro Genesis Database Snapshot - - Use the parameter `--init.url="https://snapshot.arbitrum.io/rinkeby/nitro.tar"` on first startup to initialize Nitro database - - If running more than one node, easiest to manually download image from https://snapshot.arbitrum.io/rinkeby/nitro.tar and host it locally for your nodes - - Or use `--init.url="file:///path/to/snapshot/in/container/nitro.tar"` to use a local snapshot archive - -- Other chains do not have classic blocks, and do not require an initial genesis database - -### Required parameter - -- `--l1.url=` - - Must provide standard layer 1 node RPC endpoint that you run yourself or from a node provider -- `--l2.chain-id=` - - See [public chains](../public-chains.md) for a list of Arbitrum chains and the respective L2 Chain Ids - -### Important ports - -- RPC: `8547` -- Sequencer Feed: `9642` -- WebSocket: `8548` - - WS port `8548` needs extra args to be opened. Please use these flags: - - --ws.port=8548 - - --ws.addr=0.0.0.0 - - --ws.origins=* - -### Putting it all together - -- When running docker image, an external volume should be mounted to persist the database across restarts. The mount point inside the docker image should be `/home/user/.arbitrum` -- Here is an example of how to run nitro-node: - - - Note that is important that `/some/local/dir/arbitrum` already exists, otherwise the directory might be created with `root` as owner, and the docker container won't be able to write to it - - ```shell - docker run --rm -it -v /some/local/dir/arbitrum:/home/user/.arbitrum -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/nitro-node:v2.0.8-5b9fe9c --l1.url https://l1-node:8545 --l2.chain-id= --http.api=net,web3,eth,debug --http.corsdomain=* --http.addr=0.0.0.0 --http.vhosts=* - ``` - - - Note that if you are running L1 node on localhost, you may need to add `--network host` right after `docker run` to use docker host-based networking - - - When shutting down docker image, it is important to allow for a graceful shutdown so that the current state can be saved to disk. Here is an example of how to do a graceful shutdown of all docker images currently running - ```shell - docker stop --time=300 $(docker ps -aq) - ``` - -### Note on permissions - -- The Docker image is configured to run as non-root UID 1000. This means if you are running in Linux or OSX and you are getting permission errors when trying to run the docker image, run this command to allow all users to update the persistent folders - ```shell - mkdir /data/arbitrum - chmod -fR 777 /data/arbitrum - ``` - -### Optional parameters - -- `--init.url="https://snapshot.arbitrum.io/mainnet/nitro.tar"` - - URL to download genesis database from. Only needed when starting Arbitrum One without database -- `--init.url="https://snapshot.arbitrum.io/rinkeby/nitro.tar"` - - URL to download genesis database from. Only needed when starting Rinkeby Testnet without database -- `--node.rpc.classic-redirect=` - - If set, will redirect archive requests for pre-nitro blocks to the designated RPC, which should be an Arbitrum Classic node with archive database. Only valid for Arbitrum One or Rinkeby Testnet -- `--http.api` - - APIs offered over the HTTP-RPC interface (default `net,web3,eth`) - - Add `debug` to enable tracing -- `--http.corsdomain` - - Comma separated list of domains from which to accept cross origin requests (browser enforced) -- `--http.vhosts` - - Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts `*` wildcard (default `localhost`) -- `--http.addr` - - Address to bind RPC to. May need to be set to `0.0.0.0` for docker networking to work properly -- `--node.caching.archive` - - Retain past block state -- `--node.feed.input.url=` - - Defaults to `wss://.arbitrum.io/feed`. If running more than a couple nodes, you will want to provide one feed relay per datacenter, see further instructions below -- `--node.forwarding-target=` - - Defaults to appropriate L2 Sequencer RPC depending on L1 and L2 chain IDs provided -- `--node.rpc.evm-timeout` - - Defaults to `5s`, timeout used for `eth_call` (0 == no timeout) -- `--node.rpc.gas-cap` - - Defaults to `50000000`, cap on computation gas that can be used in `eth_call`/`estimateGas` (0 = no cap) -- `--node.rpc.tx-fee-cap` - - Defaults to `1`, cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap) - -### Arb-Relay - -- When running more than one node, you want to run a single arb-relay per datacenter, which will reduce ingress fees and improve stability -- The arb-relay is in the same docker image -- Here is an example of how to run nitro-relay for Arbitrum One: - ```shell - docker run --rm -it -p 0.0.0.0:9642:9642 --entrypoint relay offchainlabs/nitro-node:v2.0.8-5b9fe9c --node.feed.output.addr=0.0.0.0 --node.feed.input.url=wss://arb1.arbitrum.io/feed - ``` -- Here is an example of how to run nitro-node for Arbitrum One with custom relay: - ```shell - docker run --rm -it -v /some/local/dir/arbitrum:/home/user/.arbitrum -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/nitro-node:v2.0.8-5b9fe9c --l1.url=https://l1-mainnet-node:8545 --l2.chain-id=42161 --http.api=net,web3,eth,debug --http.corsdomain=* --http.addr=0.0.0.0 --http.vhosts=* --node.feed.input.url=ws://local-relay-address:9642 - ``` - -### Running a Validator - -- Currently, the ability to post assertions on-chain for mainnet Arbitrum chains is whitelisted. However, anyone can run a validator in `Watchtower` mode which will immediately log an error if an on-chain assertion deviates from the locally computed chain state -- Running a validator in `Watchtower` mode is the same as running an archive Nitro node with `--node.validator.enable --node.validator.strategy=Watchtower` -- Here is an example of how to run validator for Arbitrum One: - ```shell - docker run --rm -it -v /some/local/dir/arbitrum:/home/user/.arbitrum offchainlabs/nitro-node:v2.0.8-5b9fe9c --l1.url=https://l1-mainnet-node:8545 --l2.chain-id=42161 --node.caching.archive --node.validator.enable --node.validator.strategy=Watchtower - ``` -- If a deviation is detected, a validator in Watchtower mode will log an error containing the string `found incorrect assertion in watchtower mode` diff --git a/docs/notice.md b/docs/notice.md new file mode 100644 index 0000000000..3cd21d8adb --- /dev/null +++ b/docs/notice.md @@ -0,0 +1 @@ +**Note**: Docs now live at https://github.com/OffchainLabs/nitro-docs \ No newline at end of file diff --git a/docs/proving/challenge-manager.md b/docs/proving/challenge-manager.md deleted file mode 100644 index 2da57c6f0a..0000000000 --- a/docs/proving/challenge-manager.md +++ /dev/null @@ -1,76 +0,0 @@ -# ChallengeManager - -The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: - -```mermaid -flowchart TD - B[Block challenge] - E[Execution challenge] - W[Waiting for timeout to allow for an emergency upgrade] - Start --> B - B -->|bisectExecution| B - B -->|challengeExecution| E - B -->|challengeExecution from non-finished state| W - E -->|bisectExecution| E - E -->|oneStepProveExecution| W - W -->|timeout| End - B -->|timeout| End - E -->|timeout| End -``` - -## 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/docs/proving/osp-assumptions.md b/docs/proving/osp-assumptions.md deleted file mode 100644 index 5383047912..0000000000 --- a/docs/proving/osp-assumptions.md +++ /dev/null @@ -1,74 +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/docs/proving/wasm-to-wavm.md b/docs/proving/wasm-to-wavm.md deleted file mode 100644 index 83231cc26e..0000000000 --- a/docs/proving/wasm-to-wavm.md +++ /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.md`. - -## `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/docs/proving/wavm-custom-opcodes.md b/docs/proving/wavm-custom-opcodes.md deleted file mode 100644 index 291d0e902d..0000000000 --- a/docs/proving/wavm-custom-opcodes.md +++ /dev/null @@ -1,73 +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 `osp-assumptions.md`.) - -## 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.md`. - -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/docs/proving/wavm-floats.md b/docs/proving/wavm-floats.md deleted file mode 100644 index 3262b12604..0000000000 --- a/docs/proving/wavm-floats.md +++ /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/docs/proving/wavm-modules.md b/docs/proving/wavm-modules.md deleted file mode 100644 index 5ae1bd7d3a..0000000000 --- a/docs/proving/wavm-modules.md +++ /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.md) 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. diff --git a/docs/public-chains.md b/docs/public-chains.md deleted file mode 100644 index da83496c89..0000000000 --- a/docs/public-chains.md +++ /dev/null @@ -1,37 +0,0 @@ -# Public Chains - -The following is a comprehensive list of all of the currently live Arbitrum chains: - -| Name | RPC Url(s) | ID | Native Currency | Explorer(s) | Underlying L1 | Current Tech Stack | Sequencer Feed | Nitro Seed Database URLs | -| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------ | --------------- | -------------------------------------------------------------------- | ------------- | ------------------- | -------------------------------------- | ---------------------------------------- | -| Arbitrum One | `https://arb1.arbitrum.io/rpc` `https://arbitrum-mainnet.infura.io/v3/YOUR-PROJECT-ID` `https://arb-mainnet.g.alchemy.com/v2/-KEY` | 42161 | ETH | `https://arbiscan.io/` `https://explorer.arbitrum.io/` | Ethereum | Nitro Rollup (8/31) | `wss://arb1.arbitrum.io/feed` | `snapshot.arbitrum.io/mainnet/nitro.tar` | -| Arbitrum Nova | `https://nova.arbitrum.io/rpc` | 42170 | ETH | `https://nova.arbiscan.io/` `https://nova-explorer.arbitrum.io/` | Ethereum | Nitro AnyTrust | `wss://nova.arbitrum.io/feed` | N/A | -| RinkArby^ | `https://rinkeby.arbitrum.io/rpc` | 421611 | RinkebyETH | `https://testnet.arbiscan.io` `https://rinkeby-explorer.arbitrum.io` | Rinkeby | Nitro Rollup | `wss://rinkeby.arbitrum.io/feed` | `snapshot.arbitrum.io/rinkeby/nitro.tar` | -| Nitro Goerli Rollup Testnet^ | `https://goerli-rollup.arbitrum.io/rpc` | 421613 | GoerliETH | `https://goerli.arbiscan.io` `https://goerli-rollup-explorer.arbitrum.io` | Goerli | Nitro Rollup | `wss://goerli-rollup.arbitrum.io/feed` | N/A | - -^ Testnet - -All chains use [bridge.arbitrum.io/](https://bridge.arbitrum.io/) for bridging assets and [retryable-dashboard.arbitrum.io](https://retryable-dashboard.arbitrum.io/) for executing [retryable tickets](l1-to-l2-messagaing) if needed. - -For a list of useful contract addresses, see [here](useful-addresses). - -### Arbitrum Chains Summary - -**Arbitrum One**: Arbitrum One is the flagship Arbitrum mainnet chain; it is an Optimistic Rollup chain running on top of Ethereum Mainnet, and is open to all users. In an upgrade on 8/31, the Arbitrum One chain is/was upgraded to use the [Nitro](https://medium.com/offchainlabs/its-nitro-time-86944693bf29) tech stack, maintaining the same state. -Users can now use [Alchemy](https://alchemy.com/?a=arbitrum-docs), [Infura](https://infura.io/), [QuickNode](https://www.quicknode.com), [Moralis](https://moralis.io/), [Ankr](https://www.ankr.com/), [BlockVision](https://blockvision.org/), and [GetBlock](https://getblock.io/) to interact with the Arbitrum One. See [node providers](node-providers) for more. - -**Arbitrum Nova**: Arbitrum Nova is the first mainnet [AnyTrust](inside-anytrust) chain. The following are the members of the initial data availability committee (DAC): -- Consensys -- Google Cloud -- Offchain Labs -- P2P -- Quicknode -- Reddit - -Users can now use [QuickNode](https://www.quicknode.com) to interact with the Arbitrum Nova chain. For a full guide of how to set up an Arbitrum node on QuickNode, see the QuickNode's Arbitrum RPC documentation. - -**RinkArby**: RinkArby is the longest running Arbitrum testnet. It previously ran on the classic stack, but at block 7/28/2022 it was migrated use the Nitro stack! Rinkarby will be deprecated [when Rinkeby itself gets deprecated](https://blog.ethereum.org/2022/06/21/testnet-deprecation/); plan accordingly! -Users can now use [Alchemy](https://alchemy.com/?a=arbitrum-docs), [Infura](https://infura.io/), [QuickNode](https://www.quicknode.com), [Moralis](https://moralis.io/), [Ankr](https://www.ankr.com/), [BlockVision](https://blockvision.org/), and [GetBlock](https://getblock.io/) to interact with the Arbitrum One. See [node providers](node-providers) for the full guide. - -**Nitro Goerli Rollup Testnet**: This testnet (421613) uses the Nitro rollup tech stack; it is expected to be the primary, stable Arbitrum testnet moving forward. -Users can now use [Alchemy](https://alchemy.com/?a=arbitrum-docs), [Infura](https://infura.io/), and [QuickNode](https://www.quicknode.com) to interact with the Arbitrum One. See [node providers](./node-running/node-providers.md) for more. diff --git a/docs/sequencer.md b/docs/sequencer.md deleted file mode 100644 index f22fa85480..0000000000 --- a/docs/sequencer.md +++ /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 L2. In principle, a chain’s Sequencer can take different forms; as Arbitrum One currently stands, the Sequencer is a single, centralized entity; eventually, sequencing affordances will 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](./arbos/l1-to-l2-messaging.md) message and to preserve atomicity between the two — the textbook example here being a token deposit via a [bridge](./asset-bridging.md) (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 term 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 24 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/docs/solidity-support.md b/docs/solidity-support.md deleted file mode 100644 index 838197870b..0000000000 --- a/docs/solidity-support.md +++ /dev/null @@ -1,13 +0,0 @@ -# Solidity Support - -Arbitrum Nitro chains are Ethereum compatible, and therefore allow you to trustlessly deploy Solidity contracts (as well as Vyper or any other language that compiles to EVM bytecode). - -# Differences from Solidity on Ethereum - -Although Arbitrum supports Solidity code, there are differences in the effects of a few operations, including language features that don't make much sense in the Layer 2 context: - -- `blockhash(x)` returns a cryptographically insecure, pseudo-random hash for `x` within the range `block.number - 256 <= x < block.number`. If `x` is outside of this range, `blockhash(x)` will return `0`. This includes `blockhash(block.number)`, which always returns `0` just like on Ethereum. The hashes returned do not come from L1. -- `block.coinbase` returns zero -- `block.difficulty` returns the constant 2500000000000000 -- `block.number` / `block.timestamp` return an "estimate" of the L1 block number / timestamp at which the Sequencer received the transaction (see [Time in Arbitrum](./time.md)) -- `msg.sender` works the same way it does on Ethereum for normal L2-to-L2 transactions; for L1-to-L2 "retryable ticket" transactions, it will return the L2 address alias of the L1 contract that triggered the message. See [retryable ticket address aliasing](./arbos/l1-to-l2-messaging.md#address-aliasing) for more. diff --git a/docs/time.md b/docs/time.md deleted file mode 100644 index ab2e256dd4..0000000000 --- a/docs/time.md +++ /dev/null @@ -1,70 +0,0 @@ -# Block Numbers and Time - -As in Ethereum, Arbitrum clients submit transactions, and the system (usually) executes those transactions at some later time. -In Arbitrum Rollup, clients submit transactions by posting messages to the Ethereum chain, either [through the Sequencer](./sequencer.md) or via the chain's [delayed inbox](./sequencer.md). - -Once in the chain's core inbox contract, transactions are processed in order. Generally, some time will elapse between the time when a message is put into the inbox (and timestamped) and the time when the contract processes the message and carries out the transaction requested by the message. - -## Block Numbers: Arbitrum vs. Ethereum - -Arbitrum blocks are assigned their own L2 block numbers, distinct from Ethereum's L1 block numbers. - -A single Ethereum block could include within it multiple Arbitrum blocks (if, say, the Arbitrum chain is getting heavy activity); however, an Arbitrum block cannot span across multiple Ethereum blocks. Thus, any given Arbitrum transaction is associated with exactly one Ethereum block and one Arbitrum block. - -## Ethereum Block Numbers Within Arbitrum - -Accessing block numbers within an Arbitrum smart contract (i.e., `block.number` in Solidity) will return a value _close to_ (but not necessarily exactly) the L1 block number at which the Sequencer received the transaction. - -```sol -// some Arbitrum contract: -block.number // => returns L1 block number ("ish") -``` - -As a general rule, any timing assumptions a contract makes about block numbers and timestamps should be considered generally reliable in the longer term (i.e., on the order of at least several hours) but unreliable in the shorter term (minutes). (It so happens these are generally the same assumptions one should operate under when using block numbers directly on Ethereum!) - -## Arbitrum Block Numbers - -Arbitrum blocks have their own block numbers, starting at 0 at the Arbitrum genesis block and updating sequentially. - -ArbOS and the Sequencer are responsible for delineating when one Arbitrum block ends and the next one begins; one should expect to see Arbitrum blocks produced at a relatively steady rate. - -A client that queries an Arbitrum node's RPC interface (for, ie., transaction receipts) will receive the transaction's Arbitrum block number as the standard block number field. The L1 block number will also be included in the added `l1BlockNumber field`. - -```ts -const txnReceipt = await arbitrumProvider.getTransactionReceipt('0x...') -/** - txnReceipt.blockNumber => Arbitrum block number - txnReceipt.l1BlockNumber => L1 block number ("ish") -*/ -``` - -The Arbitrum block number can also be retrieved within an Arbitrum contract via [ArbSys](./arbos/precompiles.md#ArbSys): - -```sol - ArbSys(100).arbBlockNumber() // returns Arbitrum block number -``` - -## Example - -| Wall Clock time | 12:00 am | 12:00:15 am | 12:00:30 am | 12:00:45 am | 12:01 am | 12:01:15 am | -|-----------------------------|----------|----------|----------|----------|---------|---------| -| L1 `block.number` | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | -| L2 `block.number` | 1000 | 1000 | 1000 | 1000 | 1004 | 1004 | -| Arbitrum Block number (from RPCs) | 370000 | 370005 | 370006 | 370008 | 370012 | 370015 | - -_**L2 `block.number`:** updated to sync with L1 `block.number` ~ every minute; thus over time, it will, like the L1 `block.number`, average to ~15 seconds per block._ -_**Arbitrum Block number from RPCs:** note that this can be updated multiple times per L1 block (this lets the sequencer give sub-L1-block-time tx receipts.)_ - - - -## Case Study: Multicall - -The Multicall contract offers a great case study for the difference between L1 and L2 block numbers. - -The [canonical implementation](https://github.com/makerdao/multicall/) of Multicall returns the value of `block.number`. If attempting to use out-of-the-box, some applications might face unintended behaviour. - -You can find a deployed version of the adapted Multicall2 at [0x842eC2c7D803033Edf55E478F461FC547Bc54EB2](https://arbiscan.io/address/0x842eC2c7D803033Edf55E478F461FC547Bc54EB2#code). - -By default the `getBlockNumber`, `tryBlockAndAggregate`, and `aggregate` functions return the L2 block number. This allows you to use this value to compare your state against the tip of the chain. - -The `getL1BlockNumber` function can be queried if applications need to surface the L1 block number. diff --git a/docs/tx-lifecycle.md b/docs/tx-lifecycle.md deleted file mode 100644 index 7d9bc52b96..0000000000 --- a/docs/tx-lifecycle.md +++ /dev/null @@ -1,139 +0,0 @@ -# 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. - -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](./inside-anytrust.md) for differences in the Arbitrum AnyTrust protocol. Also, for convenience/simplicity, we'll be describing the system in its final form in terms of things like permissionless validation and contract upgradability; for the current state of the Arbitrum Mainnet chains, see ["Mainnet Beta"](./mainnet-beta.md). - -For clarity on any terminology that may be unfamiliar, see our [glossary](./intro/glossary.md). - -### 1. Sequencer receives transaction - -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: - -##### 1a. Directly / Offchain - -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. - -##### 1b. ... or from L1 (via the Delayed Inbox). - -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. - -**See**: - -- [Retryables](./arbos/l1-to-l2-messaging.md) -- [The Sequencer](./sequencer.md) -- [Token Bridge](./asset-bridging.md) - -### 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 shouldn't take more than a second or two). - -**See**: - -- [ArbOS](./arbos/arbos.md) -- [Geth](./arbos/geth.md) -- [L1 pricing](./arbos/l1-pricing.md) / [L2 Gas](./arbos/gas.md) - -#### ~ ~ ~ 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 ~24 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."](./sequencer.md) - -#### ~ ~ ~ FINALITY CHECK: Ethereum-Equivalent Finality! ~ ~ ~ - -At this stage, assuming that a client believes there to be at least one well behaved active Arbitrum validator (recall that in Arbitrum Rollup, validating is permissionless), 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 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](./arbos/arbos.md) -- [Geth](./arbos/geth.md) -- [L1 pricing](./arbos/l1-pricing.md) / [L2 Gas](./arbos/gas.md) - -:::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](./arbos/l2-to-l1-messaging.md) - -#### 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](./proving/challenge-manager.md) -- [Wasm/WAVM](./proving/wasm-to-wavm.md) - -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](./assertion-tree.md) - -#### ~ ~ ~ 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. - -::: diff --git a/docs/useful-addresses.md b/docs/useful-addresses.md deleted file mode 100644 index daea385d9f..0000000000 --- a/docs/useful-addresses.md +++ /dev/null @@ -1,86 +0,0 @@ -# Useful Addresses - -Here's some contract addresses that may be useful and/or of interest to those exploring the Arbitrum protocol: - -### Protocol (L1) - - - -| | Mainnet: Arbitrum One | Arbitrum Nova | Arb-Rinkeby | Nitro Goerli Rollup | | -| -------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --- | -| Rollup | [0x5ef0d09d1e6204141b4d37530808ed19f60fba35](https://etherscan.io/address/0x5ef0d09d1e6204141b4d37530808ed19f60fba35) | [0xfb209827c58283535b744575e11953dcc4bead88](https://etherscan.io/address/0xfb209827c58283535b744575e11953dcc4bead88) | [0x71c6093C564EDDCFAf03481C3F59F88849F1e644](https://rinkeby.etherscan.io/address/0x71c6093C564EDDCFAf03481C3F59F88849F1e644) | [0x45e5cAea8768F42B385A366D3551Ad1e0cbFAb17](https://goerli.etherscan.io/address/0x45e5cAea8768F42B385A366D3551Ad1e0cbFAb17) | | -| Delayed Inbox | [0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f](https://etherscan.io/address/0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f) | [0xc4448b71118c9071bcb9734a0eac55d18a153949](https://etherscan.io/address/0xc4448b71118c9071bcb9734a0eac55d18a153949) | [0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e](https://rinkeby.etherscan.io/address/0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e) | [0x6BEbC4925716945D46F0Ec336D5C2564F419682C](https://goerli.etherscan.io/address/0x6BEbC4925716945D46F0Ec336D5C2564F419682C) | | -| Sequencer Inbox | [0x1c479675ad559dc151f6ec7ed3fbf8cee79582b6](https://etherscan.io/address/0x1c479675ad559dc151f6ec7ed3fbf8cee79582b6) | [0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b](https://etherscan.io/address/0x211e1c4c7f1bf5351ac850ed10fd68cffcf6c21b) | [0x957C9c64f7c2cE091E56aF3F33AB20259096355F](https://rinkeby.etherscan.io/address/0x957C9c64f7c2cE091E56aF3F33AB20259096355F) | [0x0484A87B144745A2E5b7c359552119B6EA2917A9](https://goerli.etherscan.io/address/0x0484A87B144745A2E5b7c359552119B6EA2917A9) | | -| Bridge | [0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a](https://etherscan.io/address/0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a) | [0xc1ebd02f738644983b6c4b2d440b8e77dde276bd](https://etherscan.io/address/0xc1ebd02f738644983b6c4b2d440b8e77dde276bd) | [0x85C720444e436E1F9407E0C3895d3fE149f41168](https://rinkeby.etherscan.io/address/0x85C720444e436E1F9407E0C3895d3fE149f41168) | [0xaf4159a80b6cc41ed517db1c453d1ef5c2e4db72](https://goerli.etherscan.io/address/0xaf4159a80b6cc41ed517db1c453d1ef5c2e4db72) | | -| Outbox | [0x0B9857ae2D4A3DBe74ffE1d7DF045bb7F96E4840](https://etherscan.io/address/0x0B9857ae2D4A3DBe74ffE1d7DF045bb7F96E4840) | [0xD4B80C3D7240325D18E645B49e6535A3Bf95cc58](https://etherscan.io/address/0xD4B80C3D7240325D18E645B49e6535A3Bf95cc58) | [0x36648F69cEb55Ce1B2920Bf2de321FBc9c378f0E](https://rinkeby.etherscan.io/address/0x36648F69cEb55Ce1B2920Bf2de321FBc9c378f0E) | [0x45Af9Ed1D03703e480CE7d328fB684bb67DA5049](https://goerli.etherscan.io/address/0x45Af9Ed1D03703e480CE7d328fB684bb67DA5049) | | -| ChallengeManager | [0xe5896783a2f463446e1f624e64aa6836be4c6f58](https://etherscan.io/address/0xe5896783a2f463446e1f624e64aa6836be4c6f58) | [0xa59075221b50c598aed0eae0bb9869639513af0d](https://etherscan.io/address/0xa59075221b50c598aed0eae0bb9869639513af0d) | [0xdd5d619823ef1fc7075a219a830ec8032e2c488e](https://rinkeby.etherscan.io/address/0xdd5d619823ef1fc7075a219a830ec8032e2c488e) | [0xcaa89a8a8771dfd95020fba805f2dc774d2e625d](https://goerli.etherscan.io/address/0xcaa89a8a8771dfd95020fba805f2dc774d2e625d) | | -| OneStepProver0 | [0x499a4f574f2e4f8837e242adec86223ef7deefcc](https://etherscan.io/address/0x499a4f574f2e4f8837e242adec86223ef7deefcc) | [0x8323b58c522690e6afae94044825f0c79a93d236](https://etherscan.io/address/0x8323b58c522690e6afae94044825f0c79a93d236) | [0x554e12DBAa0fBeB8A35583a6Fd9D04BaA4ff597f](https://rinkeby.etherscan.io/address/0x554e12DBAa0fBeB8A35583a6Fd9D04BaA4ff597f) | [0xD7422f07fe48f6e82E40587feb2acaE1451f08A6](https://goerli.etherscan.io/address/0xD7422f07fe48f6e82E40587feb2acaE1451f08A6) | | -| OneStepProverMemory | [0xb556f3bb0fdcfeaf81a1c393e024a69a3327b676](https://etherscan.io/address/0xb556f3bb0fdcfeaf81a1c393e024a69a3327b676) | [0x7a6c0503107858f82a790e481024134092e19979](https://etherscan.io/address/0x7a6c0503107858f82a790e481024134092e19979) | [0x25453614F57c026166De653351b3AcC1f45c4763](https://rinkeby.etherscan.io/address/0x25453614F57c026166De653351b3AcC1f45c4763) | [0x9221854E95283670E58738805a2d20405d17682E](https://goerli.etherscan.io/address/0x9221854E95283670E58738805a2d20405d17682E) | | -| OneStepProverMath | [0xd315ac3a82e8edaa84b347f478e0f59801747970](https://etherscan.io/address/0xd315ac3a82e8edaa84b347f478e0f59801747970) | [0x1efb116ebc38ce895eb2e5e009234e0e0836f2f5](https://etherscan.io/address/0x1efb116ebc38ce895eb2e5e009234e0e0836f2f5) | [0x2E117B00f1DA98CD7165BAb6388539ce65bE0E6c](https://rinkeby.etherscan.io/address/0x2E117B00f1DA98CD7165BAb6388539ce65bE0E6c) | [0xFe18aB9B105a8C13Fbd67a0DaCb1C70e84Bb5d5E](https://goerli.etherscan.io/address/0xFe18aB9B105a8C13Fbd67a0DaCb1C70e84Bb5d5E) | | -| OneStepProverHostIo | [0xb965b08a826d4c7634e0df4c5ef5e1d1f9b5d13a](https://etherscan.io/address/0xb965b08a826d4c7634e0df4c5ef5e1d1f9b5d13a) | [0x9cbc3f14a57ce6ead0e770f528e2f1e8b8c37613](https://etherscan.io/address/0x9cbc3f14a57ce6ead0e770f528e2f1e8b8c37613) | [0x686861ff78A55076237C8BDA698E86815f8E2fA7](https://rinkeby.etherscan.io/address/0x686861ff78A55076237C8BDA698E86815f8E2fA7) | [0x5518772ddb8e65416c6572E28BE58dAfc8A3834c](https://goerli.etherscan.io/address/0x5518772ddb8e65416c6572E28BE58dAfc8A3834c) | | -| OneStepProofEntry | [0x3e1f62aa8076000c3218493fe3e0ae40bcb9a1df](https://etherscan.io/address/0x3e1f62aa8076000c3218493fe3e0ae40bcb9a1df) | [0x7adca86896c4220f19b2f7f9746e7a99e57b0fc5](https://etherscan.io/address/0x7adca86896c4220f19b2f7f9746e7a99e57b0fc5) | [0x190274fEa8f30e3f48CE43aDCBd9a74110118284](https://rinkeby.etherscan.io/address/0x190274fEa8f30e3f48CE43aDCBd9a74110118284) | [0xe46a0585C3Cb05AaE200161534Af1aE5Dff61294](https://goerli.etherscan.io/address/0xe46a0585C3Cb05AaE200161534Af1aE5Dff61294) | | -| Classic Outbox\*\*\* | [0x760723CD2e632826c38Fef8CD438A4CC7E7E1A40](https://etherscan.io/address/0x760723CD2e632826c38Fef8CD438A4CC7E7E1A40) | | [0x2360A33905dc1c72b12d975d975F42BaBdcef9F3](https://rinkeby.etherscan.io/address/0x2360A33905dc1c72b12d975d975F42BaBdcef9F3) | | | - - - -\*\*\*Migrated Network Only - -### Token Bridge - -**IMPORTANT**: _Do **not** simply transfer tokens or Ether to any of the addresses below; it will result in loss of funds._ - -_Users should only interact with the token bridge via dapp interfaces like https://bridge.arbitrum.io_. - - - -| | Mainnet: Arbitrum One | Mainnet: Arbitrum Nova | Arb-Rinkeby | Nitro Goerli Rollup | -| --------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| L1 Gateway Router | [0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef](https://etherscan.io/address/0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef) | [0xC840838Bc438d73C16c2f8b22D2Ce3669963cD48](https://etherscan.io/address/0xC840838Bc438d73C16c2f8b22D2Ce3669963cD48) | [0x70C143928eCfFaf9F5b406f7f4fC28Dc43d68380](https://rinkeby.etherscan.io/address/0x70C143928eCfFaf9F5b406f7f4fC28Dc43d68380) | [0x4c7708168395aEa569453Fc36862D2ffcDaC588c](https://goerli.etherscan.io/address/0x4c7708168395aEa569453Fc36862D2ffcDaC588c) | -| L2 Gateway Router | [0x5288c571Fd7aD117beA99bF60FE0846C4E84F933](https://arbiscan.io/address/0x5288c571Fd7aD117beA99bF60FE0846C4E84F933) | [0x21903d3F8176b1a0c17E953Cd896610Be9fFDFa8](https://nova.arbiscan.io/address/0x21903d3F8176b1a0c17E953Cd896610Be9fFDFa8) | [0x9413AD42910c1eA60c737dB5f58d1C504498a3cD](https://testnet.arbiscan.io/address/0x9413AD42910c1eA60c737dB5f58d1C504498a3cD) | [0xE5B9d8d42d656d1DcB8065A6c012FE3780246041](https://goerli.arbiscan.io/address/0xE5B9d8d42d656d1DcB8065A6c012FE3780246041) | -| L1 ERC20 Gateway | [0xa3A7B6F88361F48403514059F1F16C8E78d60EeC](https://etherscan.io/address/0xa3A7B6F88361F48403514059F1F16C8E78d60EeC) | [0xB2535b988dcE19f9D71dfB22dB6da744aCac21bf](https://etherscan.io/address/0xB2535b988dcE19f9D71dfB22dB6da744aCac21bf) | [0x91169Dbb45e6804743F94609De50D511C437572E](https://rinkeby.etherscan.io/address/0x91169Dbb45e6804743F94609De50D511C437572E) | [0x715D99480b77A8d9D603638e593a539E21345FdF](https://goerli.etherscan.io/address/0x715D99480b77A8d9D603638e593a539E21345FdF) | -| L2 ERC20 Gateway | [0x09e9222E96E7B4AE2a407B98d48e330053351EEe](https://arbiscan.io/address/0x09e9222E96E7B4AE2a407B98d48e330053351EEe) | [0xcF9bAb7e53DDe48A6DC4f286CB14e05298799257](https://nova.arbiscan.io/address/0xcF9bAb7e53DDe48A6DC4f286CB14e05298799257) | [0x195C107F3F75c4C93Eba7d9a1312F19305d6375f](https://testnet.arbiscan.io/address/0x195C107F3F75c4C93Eba7d9a1312F19305d6375f) | [0x2eC7Bc552CE8E51f098325D2FcF0d3b9d3d2A9a2](https://goerli.arbiscan.io/address/0x2eC7Bc552CE8E51f098325D2FcF0d3b9d3d2A9a2) | -| L1 Arb-Custom Gateway | [0xcEe284F754E854890e311e3280b767F80797180d](https://etherscan.io/address/0xcEe284F754E854890e311e3280b767F80797180d) | [0x23122da8C581AA7E0d07A36Ff1f16F799650232f](https://etherscan.io/address/0x23122da8C581AA7E0d07A36Ff1f16F799650232f) | [0x917dc9a69F65dC3082D518192cd3725E1Fa96cA2](https://rinkeby.etherscan.io/address/0x917dc9a69F65dC3082D518192cd3725E1Fa96cA2) | [0x9fDD1C4E4AA24EEc1d913FABea925594a20d43C7](https://goerli.etherscan.io/address/0x9fDD1C4E4AA24EEc1d913FABea925594a20d43C7) | -| L2 Arb-Custom Gateway | [0x096760F208390250649E3e8763348E783AEF5562](https://arbiscan.io/address/0x096760F208390250649E3e8763348E783AEF5562) | [0xbf544970E6BD77b21C6492C281AB60d0770451F4](https://nova.arbiscan.io/address/0xbf544970E6BD77b21C6492C281AB60d0770451F4) | [0x9b014455AcC2Fe90c52803849d0002aeEC184a06](https://testnet.arbiscan.io/address/0x9b014455AcC2Fe90c52803849d0002aeEC184a06) | [0x8b6990830cF135318f75182487A4D7698549C717](https://goerli.arbiscan.io/address/0x8b6990830cF135318f75182487A4D7698549C717) | -| L1 Weth Gateway | [0xd92023E9d9911199a6711321D1277285e6d4e2db](https://etherscan.io/address/0xd92023E9d9911199a6711321D1277285e6d4e2db) | [0xE4E2121b479017955Be0b175305B35f312330BaE](https://etherscan.io/address/0xE4E2121b479017955Be0b175305B35f312330BaE) | [0x81d1a19cf7071732D4313c75dE8DD5b8CF697eFD](https://rinkeby.etherscan.io/address/0x81d1a19cf7071732D4313c75dE8DD5b8CF697eFD) | [0x6e244cD02BBB8a6dbd7F626f05B2ef82151Ab502](https://goerli.etherscan.io/address/0x6e244cD02BBB8a6dbd7F626f05B2ef82151Ab502) | -| L2 Weth Gateway | [0x6c411aD3E74De3E7Bd422b94A27770f5B86C623B](https://arbiscan.io/address/0x6c411aD3E74De3E7Bd422b94A27770f5B86C623B) | [0x7626841cB6113412F9c88D3ADC720C9FAC88D9eD](https://nova.arbiscan.io/address/0x7626841cB6113412F9c88D3ADC720C9FAC88D9eD) | [0xf94bc045c4E926CC0b34e8D1c41Cd7a043304ac9](https://testnet.arbiscan.io/address/0xf94bc045c4E926CC0b34e8D1c41Cd7a043304ac9) | [0xf9F2e89c8347BD96742Cc07095dee490e64301d6](https://goerli.arbiscan.io/address/0xf9F2e89c8347BD96742Cc07095dee490e64301d6) | -| L1 Weth | [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) | [0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2](https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) | [0xc778417E063141139Fce010982780140Aa0cD5Ab](https://rinkeby.etherscan.io/address/0xc778417E063141139Fce010982780140Aa0cD5Ab) | [0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6](https://goerli.etherscan.io/address/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6) | -| L2 Weth | [0x82aF49447D8a07e3bd95BD0d56f35241523fBab1](https://arbiscan.io/address/0x82aF49447D8a07e3bd95BD0d56f35241523fBab1) | [0x722E8BdD2ce80A4422E880164f2079488e115365](https://nova.arbiscan.io/address/0x722E8BdD2ce80A4422E880164f2079488e115365) | [0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681](https://testnet.arbiscan.io/address/0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681) | [0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3](https://goerli.arbiscan.arbitrum.io/address/0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3) | - - -#### Third party gateways - -These are the addresses of some other gateways that have been deployed and integrated with the token bridge - -| | Mainnet: Arbitrum One | Arb-Rinkeby | -| ------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| L1 Dai Gateway | [0xD3B5b60020504bc3489D6949d545893982BA3011](https://etherscan.io/address/0xD3B5b60020504bc3489D6949d545893982BA3011) | [0x10E6593CDda8c58a1d0f14C5164B376352a55f2F](https://rinkeby.etherscan.io/address/0x10E6593CDda8c58a1d0f14C5164B376352a55f2F) | -| L2 Dai Gateway | [0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65](https://arbiscan.io/address/0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65) | [0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65](https://testnet.arbiscan.io/address/0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65) | -| L1 Livepeer Gateway | [0x6142f1C8bBF02E6A6bd074E8d564c9A5420a0676](https://etherscan.io/address/0x6142f1C8bBF02E6A6bd074E8d564c9A5420a0676) | [0x831C51Cd8A38C3E42D98Acd77F06BF537D29800e](https://rinkeby.etherscan.io/address/0x831C51Cd8A38C3E42D98Acd77F06BF537D29800e) | -| L2 Livepeer Gateway | [0x6d2457a4ad276000a615295f7a80f79e48ccd318](https://arbiscan.io/address/0x6D2457a4ad276000A615295f7A80F79E48CcD318) | [0x7e0ba3791b23d0d577cf8d09c4fdd5821222208c](https://testnet.arbiscan.io/address/0x7e0ba3791b23d0d577cf8d09c4fdd5821222208c) | - -### Arbitrum Precompiles (L2, same on all Arb-chains) - - - -| | Address | -| ---------------- | -------------------------------------------------------------------------------------------------------------------- | -| ArbSys | [0x0000000000000000000000000000000000000064](https://arbiscan.io/address/0x0000000000000000000000000000000000000064) | -| ArbRetryableTx | [0x000000000000000000000000000000000000006E](https://arbiscan.io/address/0x000000000000000000000000000000000000006E) | -| ArbGasInfo | [0x000000000000000000000000000000000000006C](https://arbiscan.io/address/0x000000000000000000000000000000000000006C) | -| ArbAddressTable | [0x0000000000000000000000000000000000000066](https://arbiscan.io/address/0x0000000000000000000000000000000000000066) | -| ArbStatistics | [0x000000000000000000000000000000000000006F](https://arbiscan.io/address/0x000000000000000000000000000000000000006F) | -| NodeInterface | [0x00000000000000000000000000000000000000C8](https://arbiscan.io/address/0x00000000000000000000000000000000000000C8) | -| ArbBLS | [0x0000000000000000000000000000000000000067](https://arbiscan.io/address/0x0000000000000000000000000000000000000067) | -| ArbInfo | [0x0000000000000000000000000000000000000065](https://arbiscan.io/address/0x0000000000000000000000000000000000000065) | -| ArbAggregator | [0x000000000000000000000000000000000000006D](https://arbiscan.io/address/0x000000000000000000000000000000000000006D) | -| ArbFunctionTable | [0x0000000000000000000000000000000000000068](https://arbiscan.io/address/0x0000000000000000000000000000000000000068) | - - -### Misc - - -| | Mainnet: Arbitrum One | Mainnet: Arbitrum Nova | Arb-Rinkeby | Nitro Goerli Rollup | -| ------------ | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| L2 Multicall | [0x7ecfbaa8742fdf5756dac92fbc8b90a19b8815bf](https://arbiscan.io/address/0x7ecfbaa8742fdf5756dac92fbc8b90a19b8815bf) | [0x5e1eE626420A354BbC9a95FeA1BAd4492e3bcB86](https://nova.arbiscan.io/address/0x5e1eE626420A354BbC9a95FeA1BAd4492e3bcB86) | [0x7ecfbaa8742fdf5756dac92fbc8b90a19b8815bf](https://testnet.arbiscan.io/address/0x7ecfbaa8742fdf5756dac92fbc8b90a19b8815bf) | [0x108B25170319f38DbED14cA9716C54E5D1FF4623](https://goerli-rollup-explorer.arbitrum.io/address/0x108B25170319f38DbED14cA9716C54E5D1FF4623) | - - diff --git a/docs/why-nitro.md b/docs/why-nitro.md deleted file mode 100644 index 2b2c6bbbcc..0000000000 --- a/docs/why-nitro.md +++ /dev/null @@ -1,49 +0,0 @@ -# ~~Wen~~ 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. - -### Nitro vs. Classic - -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 called the Arbitrum Virtual Machine (or AVM). The implementation of Arbitrum’s L2 state machine— known as [“ArbOS”](./arbos/arbos.md) — was effectively a program that gets 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) include [Geth itself](./arbos/geth.md), 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](./proving/challenge-manager.md), 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, translations 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](./arbos/l1-pricing.md) 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”, who’s units didn’t correspond to Ethereum L1 gas; e.g,. a simple transfer takes 21,000 gas on L1 but over 100,000 ArbGas on 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](./arbos/gas.md) 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.