Starknet transaction execution library in Rust, featuring ⚡cairo-vm⚡
- Table of Contents
- 📦 Archival Notice
- 📖 About
- 🌅 Getting Started
- 🚀 Usage
- 🛠 Contributing
- 🌞 Related Projects
- 📚 Documentation
- ⚖️ License
Starknet in Rust is now archived.
We have decided to archive this repository. Starknet in Rust supports Starknet up to version 0.13.0. Maintaining and updating this project requires substantial effort, and with Blockifier already in production and consistently updated, we will no longer add support for newer Starknet versions. We prefer to focus our efforts on other Starknet projects that can provide greater value to the community. See: Related Projects
If you were using Starknet in Rust, we recommend transitioning to Blockifier.
starknet_in_rust
is an implementation of Starknet in Rust.
It makes use of cairo-vm, the Rust implementation of the Cairo virtual machine.
- Rust 1.74.1
- A working installation of cairo-lang 0.12 (for compiling the cairo files)
- [Optional, for testing purposes] Heaptrack
You need to have a version of Python 3
installed. If you don't have it, you can install it for Debian-based GNU/Linux distributions with:
sudo apt install python3.9
On MacOS you can use Homebrew:
brew install python@3.9
Optionally, for setting environment, you can install pyenv
for MacOS:
brew install pyenv
If you run make
on it's own it will print out the main targets and their description.
Run the following make targets to have a working environment (if in Mac or if you encounter an error, see the subsection below):
$ make deps
$ make build
$ make deps-macos
$ make build
Check the Makefile for additional targets.
cairo-lang
requires the gmp
library to build.
You can install it on Debian-based GNU/Linux distributions with:
sudo apt install -y libgmp3-dev
In Mac you can use Homebrew:
brew install gmp
In Mac you'll also need to tell the script where to find the gmp lib:
export CFLAGS=-I/opt/homebrew/opt/gmp/include LDFLAGS=-L/opt/homebrew/opt/gmp/lib
Starknet in Rust can be integrated with Cairo Native, which makes the execution of sierra programs possible through native machine code. To use it, the following needs to be setup:
- LLVM
17
needs to be installed and theMLIR_SYS_170_PREFIX
andTABLEGEN_170_PREFIX
environment variable needs to point to said installation. In macOS, runand you're set.brew install llvm@17 export MLIR_SYS_170_PREFIX=/opt/homebrew/opt/llvm@17 export LLVM_SYS_170_PREFIX=/opt/homebrew/opt/llvm@17 export TABLEGEN_170_PREFIX=/opt/homebrew/opt/llvm@17
Afterwards, compiling with the feature flag cairo-native
will enable native execution. You can check out some example test code that uses it under tests/cairo_native.rs
.
Currently cairo-native with AOT needs a runtime library in a known place. For this you need to compile the cairo-native-runtime crate and point the following environment variable to a folder containing the dynamic library. The path must be an absolute path.
CAIRO_NATIVE_RUNTIME_LIBDIR=/absolute/path/to/cairo-native/target/release
If you don't do this you will get a linker error when using AOT.
You can find a tutorial on running contracts here.
starknet_in_rust
supports caching contracts in memory. Caching the contracts is useful for
avoiding excessive RPC API usage and keeping the contract class deserialization overhead to the
minimum. The project provides two builtin cache policies: null and permanent. The null cache behaves
as if there was no cache at all. The permanent cache caches everything in memory forever.
In addition to those two, an example is provided that implements and uses an LRU cache policy. Long-running applications should ideally implement a cache algorithm suited to their needs or alternatively use our example's implementation to avoid spamming the API when using the null cache or blowing the memory usage when running with the permanent cache.
Customized cache policies may be used by implementing the ContractClassCache
trait. Check out our
LRU cache example for more details. Updating the cache requires
manually merging the local state cache into the shared cache manually. This can be done by calling
the drain_private_contract_class_cache
on the CachedState
instance.
// To use the null cache (aka. no cache at all), create the state as follows:
let cache = Arc::new(NullContractClassCache::default());
let state1 = CachedState::new(state_reader.clone(), cache.clone());
let state2 = CachedState::new(state_reader.clone(), cache.clone()); // Cache is reused.
// Insert state usage here.
// The null cache doesn't have any method to extend it since it has no data.
// If the permanent cache is preferred, then use `PermanentContractClassCache` instead:
let cache = Arc::new(PermanentContractClassCache::default());
let state1 = CachedState::new(state_reader.clone(), cache.clone());
let state2 = CachedState::new(state_reader.clone(), cache.clone()); // Cache is reused.
// Insert state usage here.
// Extend the shared cache with the states' contracts after using them.
cache.extend(state1.state.drain_private_contract_class_cache());
cache.extend(state2.state.drain_private_contract_class_cache());
This project uses the tracing
crate as a library. Check out
its documentation for more information.
This project uses the tracing
crate as a library. Check out
its documentation for more information.
Run the following command:
$ make test
Take into account that some tests use the RPC State Reader so you need a full-node instance or an RPC provider that supports Starknet API version 0.6.0.
The RPC State Reader provides a way of reading the real Starknet State when using Starknet in Rust. So you can re-execute an existing transaction in any of the Starknet networks in an easy way, just providing the transaction hash, the block number and the network in which the transaction was executed. Every time it needs to read a storage value, a contract class or contract, it goes to an RPC to fetch them.
Right now we are using it for internal testing but we plan to release it as a library soon.
In order to use the RPC state reader add the endpoints to a full node instance or RPC provider supporting Starknet API version 0.5.0 in a .env
file at root:
RPC_ENDPOINT_TESTNET={some endpoint}
RPC_ENDPOINT_MAINNET={some endpoint}
Run the following command:
$ make flamegraph
to generate a flamegraph with info of the execution of the main operations.
Read the 'bench_integration.py' file to identify which lines need to be commented out for accurate results. Comment out those lines and then run the following command:
$ make benchmark
The open source community is a fantastic place for learning, inspiration, and creation, and this is all thanks to contributions from people like you. Your contributions are greatly appreciated.
If you have any suggestions for how to improve the project, please feel free to fork the repo and create a pull request, or open an issue with the tag 'enhancement'.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
And don't forget to give the project a star! ⭐ Thank you again for your support.
- cairo-vm: A fast implementation of the Cairo VM in Rust.
- cairo-native: A compiler to convert Cairo's intermediate representation "Sierra" code to machine code via MLIR and LLVM.
- cairo-vm-py: Bindings for using cairo-vm from Python code.
This project is licensed under the Apache 2.0 license.
See LICENSE for more information.