owned this note
owned this note
Published
Linked with GitHub
# Introducing Fossil & The Fossil API
## Fossil
### What is Fossil?
Fossil is a set of smart contarcts and infrastructure running on both Starknet and Ethereum L1. It's purpose is to enable starknet smart contracts to trustlessly access any data that ever appeared on ethereum such as: state, transactions, logs, block parameters.
### What is the problem we are solving?
Among all the debates and different opinions about ethereum in the blockchain community. There is one opinion that unifies all the stakeholder together - Ethereum has a massive scaling issue!
And it is this scaling issue that has led the the birth of many Layer 2 scaling projects in the community.
Fossil is built on one such layer 2 solution - starknet.
Starknet is a new, highly anticipated L2 scaling solution, however L2s have the problem of L1 data being unavailable on L2, Fossil is here to bring that data to starknet.
Fossil is a set of contracts which allows you to prove on starknet, that the ethereum state has a certain value.
### How do we manage to solve it?
Fossil allows anyone to trustlessly prove any past or current headers, state, and storage values of L1 contracts to other L2 contracts.
You do all of the heavy lifting of your L1 data on L2, and trustlessly obtain and prove that data on L2 using Fossil.
## Technical Architecture of the product
Before diving into the smart contract architecture, let us understand some fundamentals first.
### Understanding Ethereum Block Headers
The Ethereum Block Header -
![](https://i.imgur.com/ZS2csSJ.png)
([Source](https://www.mdpi.com/2079-9292/10/4/391/htm#))
To get the blockhash, this block would be first RLP encoded to get a hex string.
We then hash this hex string using keccak to obtain the blockhash of this block.
![](https://i.imgur.com/J1GN1gB.png)
### Syncing with L1
Now every time we sync with L1, a tuple consisting of (block_number, blockhash) is sent to L2.
This very simple tuple, allows us to do a lot of amazing things on starknet.
So let's say you want to prove a block header on starknet, you do the following things -
1. Sync Starknet with L1 by invoking the L1 contract
2. Send RLP of the block to starknet
3. Compute the keccak of this RLP
4. Compare the computed hash with the previously sent hash
But this doesn't stop here. From this block, you can now start working your way backwards and recreating headers in the past all the way up to block 0.
So how do you do this?
### Recreating headers on starknet
Each header has a field called parentHash.
Suppose you have just synced block X, and proven that the RLP of the block header (for block X) provided to starknet is valid.
We know that this block header will contain a parent hash,
If this block header is valid, then the parent hash must also be valid.
And we also know that the parent hash is nothing but the blockhash of the block X-1.
You see where we are going with this?
Now if you want to prove the validity of an RLP we provide for the header of the block X-1, you don't need to sync with L1 again to obtain the blockhash of X-1.
All you have to do is hash the RLP, and check it with the parent hash of the block X ( which we have already proved is valid).
And this way we can recreate blocks all the way upto the genesis block.
As there is no interaction with L1 involved while recreating a block, therefore there is no cost for recreating a block on L1.
Keep in mind, that this doesn't mean that there are no costs associated with recreating a block whatsoever.
Recreating blocks does cost computation on L2, therefore we only want to recreate blocks to a depth where our costs on L2 would be smaller than L1.
If an actor requires a block that is too deep from the last sync point, then it makes more sense to sync from L1 again, instead of recreating blocks from the last sync point.
Timely L1 syncs are also required as we can't recreate them in the future, so fossil syncs regularly with the ethereum chain depending upon the demands of the jobs.
### Smart Contracts
We also need one smart contract on ethereum to sync with starknet:
* L1MessagesSender.sol - This contract is responsible for syncing with starknet contracts on L2. It sends the (block_number, blockhash) to L2.
Fossil is built out of the following contracts on starknet:
#### L1MessagesProxy
This contract is responsible for receiving messages from L1, such a contract is used in order to abstract the communication layer to facilitate any future upgrades.
The message sent to this contract is a tuple consisting of(blockhash, bock_number).
#### L1HeadersStore
This contract is responsible for processing L1 blocks that is:
* Hashing received RLP headers
* Storing the hash of the last processed/received block.
* Comparing the computed hash of the RLP header to the previously known header
* Decoding and saving parameters from the block header
#### Facts Registry
This contract is responsible for veryfing merkle patricia proofs of accounts and storage slots.
In order to verify an account proof this contract first will query the storage of L1HeadersStore to retrieve the state root of a block and verify the proof against this root.
Depending on the request storage_hash|nonce|balance of the account will be cached in the storage of Facts registry.
Once the storage_hash of an account is set any storage slot can be proven.
#### TWAP
This contract is a storage writes optimized version of L1HeadersStore.
In order to compute a TWAP of block: basefee|difficulty|gasUsed we do not need to store the block parentHash etc.
The needed information is just how many blocks were already processed and what is the sum of processed data points.
Similarly to L1HeadersStore the TWAP contract requires submission of RLP headers, those headers are hashed and checked against the hash received from L1.
The TWAP computation is a 3 step process as:
* Computation must be registered by specifying:
* From block
* To block
* Averaged param
* Callback address
* The computation must be processed depending on the size of the range(from, to) the computation might require multiple transactions, such transaction requires it's sender to attach valid rlp block header in the calldata.
* Once the computation is fully processed a contract implementing the [ITwapReceiver.cairo](https://github.com/OilerNetwork/fossil/blob/58ba112fe657d414910982c13ed296f87754c32a/contracts/starknet/TWAP.cairo#L34) interface will be invoked, the result of such computation is later handled by such contract.
### Use Case Walkthrough
In order to access some data using Fossil the following steps are required:
* Sync Starknet with Ethereum L1.
* Process the desired amount of blocks starting from the synced block.
* Decide which parameters from the processed block headers save in starknet's state or not.
* Generate merkle proofs for accounts.
* Generate merkle proofs for storage slots.
And after all these steps you now have your data ready for you.
All those steps make interacting with Fossil quite complex so what do we do about it?
---
## The Fossil API
Fossil is a really powerful tool on its own, but as a product there are 2 places that it is lacking -
1. Proving things on starknet is a complex task, even with Fossil there are a lot of steps that a user needs to make to achieve the final result they want.
2. We could have further optimized our calls to L1, if there was a way to batch the requirements of all the users using Fossil at a particular time.
Introducing the Fossil API!
The purpose of this API is to faciliate interacting with Fossil contracts. This API hides all the complexity from the end user behind a REST API and sends webhooks once the results are ready.
The Fossil API is also able to batch different calls to Fossils together, to optimize our calls to L1.
For example -
Actor A wants to prove a transaction at block X, whereas actor B wants to prove a state at block X + 2.
let's say A uses Fossil individually first, and we sync with ethereum at block X.
B uses Fossil individually next, and we sync with ethereum again at block X+2.
Here we had to make 2 syncs to L1, but we can do this in just 1 sync using the Fossil API.
So now all of the actors A, B, C , D ... Z would not interact with Fossil directly , instead they would interact with the API.
The API would aggregate all of their requests and batch all of these requests to make the most optimal calls to L1.
So to summarize, the Fossil API provides the following benefits -
1. Coordinates actions of multiple actors and makes them cheaper, leverages CoW(Coincidence of wills) situations and optimizes the costs
2. Bunches different requests from the users, and asks for the most optimized sync from L1
3. Queues jobs for some time, and then runs a cron job to mutualize cost for all those jobs
With the fossil API we are able to cover and simplify a major chunk of the usecases that an actor would want from Fossil.
## API Endpoints
Currently we have the following API endpoints enabled:
- **/prove_call**
- **/prove_transaction**
- **/prove_receipt**
- **/compute_twap** - difficulty, basefee, gasUsed
---
# Setup
In order to run Fossil , clone fossil and
* create a venv with python3.7 inside it
* install [tox](https://www.howtoinstall.me/ubuntu/18-04/tox/)
* run tox
* Check out [cairo](https://www.cairo-lang.org/docs/quickstart.html)
* ./.tox/py37/bin/nile compile
# Community & Ecosystem
Several projects have already begun using Fossil in their products! As of publication, Snapshot, Dopewars, and Oiler Networks Pitchlake are using Fossil.
Unfortunately, Fossil is currently not ready for most production applications. This is because the block step limit on Starknet is too low and large requests may fail if they require too many steps.
We have currently solved this problem by using the unsafe keccak, to reduce the steps taken.
We are in contact with Starkware and are awaiting for them to increase the step limit in the near future, at which time we will complete e2e testing and officially launch Fossil.
If you are interested in building with the Fossil API, we can give you API access for testing purposes prior to the official launch. Please send any inquiries to: fossil@oiler.network