How to customize your Orbit chain's behavior
Orbit chains are now Mainnet ready! Note that Orbit is still a public preview capability - the Orbit product and its supporting documentation may change significantly as we capture feedback from readers like you.
To provide feedback, click the Request an update button at the top of this document, join the Arbitrum Discord, or reach out to our team directly by completing this form.
Preface
Before customizing your Orbit chain, it's important to understand what the State Transition Function (aka the STF) is. The STF defines how new blocks are produced from input messages (i.e. transactions). This guide is only necessary for changes that modify the State Transition Function. To customize other node behavior, such as RPC behavior or the sequencer's ordering policy, you can simply build your own node without worrying about the rest of this guide. However, changes that modify the STF require updating the fraud proving system to recognize the new behavior as correct. Otherwise, the fraud prover would side with unmodified nodes, which would win fraud proofs against your modified node.
Here's some examples of modifications that affect the STF:
- Adding a new EVM opcode or precompile: This modifies the STF because a node with this change would disagree about the outcome of EVM execution compared to an unmodified Nitro node when the new opcode or precompile is invoked.
- Rewarding the deployer of a smart contract with a portion of gas spent in the smart contract's execution: This modifies the STF because a node which has this change applied would disagree about the balance of the deployer after transactions. Such changes would lead to disagreements about block hashes compared to unmodified Nitro nodes.
Here's some examples of modifications that don't affect the STF:
- Adding a new RPC method to query an address's balance across multiple blocks: This doesn't modify the STF because it doesn't change on-chain balances or block hashes.
- Changing the sequencer to order blocks by tip: The sequencer is trusted to order transactions in Arbitrum Nitro, and it can choose any ordering it wants. Nodes (and the fraud proofs) will simply accept the new transaction ordering as there is no single ordering they think is correct.
Modification compatibility with Arbitrum Nitro
Some potential modifications are incompatible with Arbitrum Nitro and would not result in a functioning blockchain. Here are some requirements for the Arbitrum Nitro State Transition Function:
- The STF must be deterministic. For instance, if you gave an address a random balance using the Go randomness library, every node would disagree on the correct amount of balance and the blockchain would not function correctly. However, it is acceptable to take a non-deterministic path to a deterministic output. For instance, if you randomly shuffled a list of addresses, and then gave them each 1 Ether, that would be fine, because no matter how the list of addresses is shuffled the result is the same and all addresses are given 1 Ether.
- The STF must not reach a new result for old blocks. For instance, if you have been running an Arbitrum Nitro chain for a while, and then you decide to modify the STF to not charge for gas, a new node that syncs the blockchain will reach a different result for historical blocks. It's also important to synchronize between nodes when an upgrade takes effect. A common mechanism for doing this is having an upgrade take effect at a certain timestamp, by which all nodes must be upgraded.
- The STF must be "pure" and not use external resources. For instance, it must not use the filesystem, make external network calls, or launch processes. That's because the fraud proving system does not (and for the most part, cannot) support these resources. For instance, it's impossible to fraud prove what the result of an external network call is, because the fraud prover smart contracts on L1 are unable to do networking.
- The STF must not carry state between blocks outside of the "global state". In practice this means persistent state must be stored within block headers or the Ethereum state trie. For instance, ArbOS stores all retryables in contract storage under a special ArbOS address.
- The STF must not modify Ethereum state outside of a transaction. This is important to ensure that replaying old blocks reaches the same result, both for tracing and for validation. The ArbOS internal transaction is useful to modify state at the start of blocks.
- The STF must reach a result in under a second. This is a rough rule, but for nodes to keep in sync it's highly recommended to keep blocks quick. It's also important for the fraud proofs that execution reliably finishes in a relatively short amount of time. A block gas limit of 32 million gas should safely fit within this limit.
- The STF must not fail or panic. It's important that the STF always produces a new block, even if user input is malformed. For instance, if the STF receives an invalid transaction as input, it'll still produce an empty block.
Building the modified node
To modify the State Transition Function, you'll need to build a modified Arbitrum Nitro node Docker image. This guide covers how to build the node and enable fraud proofs by building a new replay binary.
Step 1. Download the Nitro source code
Clone the Nitro repository before you begin:
git clone --branch v3.1.0 https://github.com/OffchainLabs/nitro.git
cd nitro
git submodule update --init --recursive --force
Step 2. Apply modifications
Next, make your changes to the State Transition Function. For example, you could add a custom precompile.