Precompiles are predefined smart contracts that have a special address and provide specific functionality which is not executed at the EVM bytecode level but natively by the client.
They are primarily used to add specific functions that would be computationally expensive if executed in the EVM bytecode. They also serve a purpose to facilitate the interaction between a parent chain and a child chain. In cases like having them with Arbitrum client, they can be optimized for performance.
Rollups like arbitrum provide addition child chain specific precompiles that smart contracts can call the same way they can do for solidity functions, in addition to supporting all Ethereum precompiles.
Precompiles are not written in Solidity, they are written in the language in which the client is written such as Go, Rust, etc. They are much more gas efficient than their equivalent EVM code. They have fixed addresses which are known in advance.
Here are a few examples of common Ethereum precompiles with their addresses
0x01: ECRecover (to recover signature)
0x02: SHA256 hash
0x08: bn256 pairing for zk-SNARKs
When you are deploying your own rollup, providers such as Arbitrum allow users to create new precompiles. This requires changes to the State Transition Function (STF), it defines how new blocks are produced from the input transactions.
Updates to State Transition Function requires updating the fraud proving system to recognize that the new behavior is right, else the prover would take the side of unmodified nodes which will win the fraud proofs against the modified node precompile.
Adding a new precompile requires modification in the STF because a node with this change would disagree about the outcome of the EVM execution compared to the unmodified node when the new precompile is invoked.
But there are specific requirements which should be kept in mind before updating the STF
the resulting STF must be deterministic. It is ok to take a non-deterministic path to get a deterministic output, example you shuffle the order of addresses randomly and give each 1 ETH, this is ok as still all addresses got 1 ETH.
The STF must not reach a new result for the old blocks. Example if you modify the STF to not charge gas, a new node will reach a different result state for the historical blocks.
The STF must be pure and not use external resources. For example it should not use filesystem, make external network calls, launch processes etc. Because the fraud proving system does not and cannot support these resources.
The STF must not carry the state between the blocks outside of the global state. Meaning persistent state must be stored within block headers of Ethereum state trie.
The STF must not modify Ethereum state outside of a transaction. This is important to ensure that replaying the old blocks reaches the same result both for tracing and validation.
It is important for fraud proofs that execution reliably finishes in a short amount of time, and a block gas limit of 32 million gas should safely fit within this limit.
The STF must not fail or panic, it is important that it always produces a new block, even if user input is malformed. Example if STF receives an invalid transaction as input, it still produces an empty block.
It is important to synchronize between the nodes when an upgrade takes effect. Usually for this we an create a certain timestamp by which all nodes must be upgraded.
Usually when also writing a precompiling, as gas cost is associated with it. Even though a precompile can just be written without taking any gas, it is done so because gas fee precompiles are vulnerable to Denial of Service attacks, where an attacker can call a precompile again and again without bearing the computational costs that builds up while executing the precompile logic.
The only time Precompiles makes sense is to access node internals, when extreme optimization is needed and access to system level functions.

Top comments (0)