Skip to content

FuzzingLabs/starknet_in_rust

Β 
Β 

Repository files navigation

πŸ¦€ Starknet in Rust πŸ¦€

Starknet transaction execution library in Rust, featuring ⚑cairo-vm⚑

Report Bug Β· Request Feature

codecov license pr-welcome Telegram Chat

Table of Contents

⚠️ Disclaimer

🚧 This project is a work-in-progress and is not ready for production yet. Use at your own risk. 🚧

πŸ“– About

starknet_in_rust is an implementation of Starknet in Rust. It makes use of cairo-vm, the Rust implementation of the Cairo virtual machine.

πŸŒ… Getting Started

Dependencies

  • Rust 1.74.1
  • A working installation of cairo-lang 0.12 (for compiling the cairo files)
  • [Optional, for testing purposes] Heaptrack

Requirements

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 [email protected]

Optionally, for setting environment, you can install pyenv for MacOS:

brew install pyenv

Installation

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):

Linux (x86-64)

$ make deps
$ make build

OSX (Apple Silicon)

$ make deps-macos
$ make build

Check the Makefile for additional targets.

How to manually install the script dependencies

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

Cairo Native support

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 the MLIR_SYS_170_PREFIX and TABLEGEN_170_PREFIX environment variable needs to point to said installation. In macOS, run
    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
    
    and you're set.

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.

Using ahead of time compilation with Native.

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.

πŸš€ Usage

Running simple contracts

You can find a tutorial on running contracts here.

Customization

Contract class cache behavior

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());

Logging configuration

This project uses the tracing crate as a library. Check out its documentation for more information.

Testing

Logging configuration

This project uses the tracing crate as a library. Check out its documentation for more information.

Testing

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.

RPC State Reader

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.

How to configure it

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}

Profiling

Run the following command:

$ make flamegraph

to generate a flamegraph with info of the execution of the main operations.

Benchmarking

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

πŸ›  Contributing

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'.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

And don't forget to give the project a star! ⭐ Thank you again for your support.

🌞 Related Projects

  • cairo-vm: A fast implementation of the Cairo VM in Rust.
  • cairo-vm-py: Bindings for using cairo-vm from Python code.

πŸ“š Documentation

Starknet

βš–οΈ License

This project is licensed under the Apache 2.0 license.

See LICENSE for more information.

About

A Rust implementation of StarkNet

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 87.7%
  • Cairo 11.6%
  • Other 0.7%